This is not a duplicate! When I failed I tried to look to similar posts but without success. I cannot understand why OnUCItemsSourceChanged is not called? I'm pretty sure that I miss something simple but I cannot find it.

I have Window that contains UserControl1 which has attached collection property that is bound to Window's WindowCollection collection. I expect UserControl1.OnUCItemsSourceChanged to be called when I add items to WindowCollection. But it doesn't happen.

What I miss?

Window1.xaml.cs

public partial class Window1 : Window
{
    public ObservableCollection<long> WindowCollection { get; set; }
    public Window1()
    {
        InitializeComponent();

        DataContext = this;

        WindowCollection = new ObservableCollection<long>();
    }

    private void Button_Click(object sender, RoutedEventArgs e)
    {
        WindowCollection.Add(1);
        WindowCollection.Add(2);
    }
}

Window1.xaml

<StackPanel>
    <uc:UserControl1 UCItemsSource="{Binding Path=WindowCollection}" />
    <Button Content="Refresh" Click="Button_Click" />
</StackPanel>

UserControl1.xaml.cs

public static readonly DependencyProperty UCItemsSourceProperty = DependencyProperty.Register("UCItemsSource", typeof(IEnumerable), typeof(UserControl1), new PropertyMetadata(null, new PropertyChangedCallback(OnUCItemsSourceChanged)));

public IEnumerable UCItemsSource
{
    get { return (IEnumerable)GetValue(UCItemsSourceProperty ); }
    set { SetValue(UCItemsSourceProperty , value); }
}

public ObservableCollection<MyItem> UCItems { get; set; }

private static void OnUCItemsSourceChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
    var control = d as UserControl1;
    var items = e.NewValue as ObservableCollection<long>;

    foreach (var item in items)
   {
         control.UCItems.Add(new MyItem(item));
   }
}

UserControl1.xaml

<ItemsControl ItemsSource="{Binding UCItems}" ... />

UPDATE This is link to my test project

有帮助吗?

解决方案

In this line:

<ItemsControl ItemsSource="{Binding UCItems}" ... />

Must be RelativeSource with FindAncestor, because UCItems located in UserControl:

UserControl

<ItemsControl ItemsSource="{Binding Path=UCItems,
                                    RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type UserControl}}}" />

I cannot understand why OnUCItemsSourceChanged is not called?

If you add RelativeSource construction, then OnUCItemsSourceChanged causing at least once because PropertyChangedCallback triggered every time then you set new value for the dependency property:

Represents the callback that is invoked when the effective property value of a dependency property changes.

Since you once sets the value for dependency property here:

<uc:UserControl1 UCItemsSource="{Binding Path=WindowCollection}" />

I expect UserControl1.OnUCItemsSourceChanged to be called when I add items to WindowCollection.

For this is an ObservableCollection<T>.CollectionChanged event, in that contains the enumeration of acts performed on the collection:

Occurs when an item is added, removed, changed, moved, or the entire list is refreshed.

For your case it will be something like this:

Version with CollectionChanged

MainWindow

public partial class MainWindow : Window
{
    public ObservableCollection<long> WindowCollection { get; set; }

    public MainWindow()
    {
        InitializeComponent();
        DataContext = this;

        WindowCollection = new ObservableCollection<long>();

        WindowCollection.Add(1);
        WindowCollection.Add(2);            
    }

    private void Button_Click(object sender, RoutedEventArgs e)
    {
        WindowCollection.Add(3);
        WindowCollection.Add(4);
    }
}

UserControl

public partial class UserControl1 : UserControl
{
    #region Public Section

    public ObservableCollection<long> UCItems { get; set; }
    public static UserControl1 control;

    #endregion

    public UserControl1()
    {
        InitializeComponent();
        UCItems = new ObservableCollection<long>();            
    }

    #region UCItemsSource Property

    public static readonly DependencyProperty UCItemsSourceProperty = DependencyProperty.Register("UCItemsSource", 
                                                                                                  typeof(IEnumerable), 
                                                                                                  typeof(UserControl1),
                                                                                                  new PropertyMetadata(null, new PropertyChangedCallback(OnUCItemsSourceChanged)));

    public IEnumerable UCItemsSource
    {
        get { return (IEnumerable)GetValue(UCItemsSourceProperty); }
        set { SetValue(UCItemsSourceProperty, value); }
    }

    #endregion

    private static void OnUCItemsSourceChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        control = d as UserControl1;
        var items = e.NewValue as ObservableCollection<long>;

        items.CollectionChanged += new NotifyCollectionChangedEventHandler(CollectionChanged);
        AddItem(control, items);
    }

    private static void CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
    {
        var items = sender as ObservableCollection<long>;
        control.UCItems.Clear();

        if (e.Action == NotifyCollectionChangedAction.Add)
        {
            AddItem(control, items);
        }
    }

    private static void AddItem(UserControl1 userControl, ObservableCollection<long> collection) 
    {
        if (collection.Count > 0)
        {
            foreach (var item in collection)
            {
                userControl.UCItems.Add(item);
            }
        }
    }
}

This project available in this link

Alternative version

This version is simpler and more correct. Here we just reference to UCItemsSource property that contain collection, also here RelativeSource justified:

UserControl

XAML

<Grid>
    <ItemsControl ItemsSource="{Binding Path=UCItemsSource, 
                                        RelativeSource={RelativeSource Mode=FindAncestor,
                                                                       AncestorType={x:Type UserControl}}}">
        <ItemsControl.ItemTemplate>
            <DataTemplate>
                <TextBlock Text="{Binding}" />
            </DataTemplate>
        </ItemsControl.ItemTemplate>
    </ItemsControl>
</Grid>

Code-behind

public partial class UserControl1 : UserControl
{
    #region Public Section

    public ObservableCollection<long> UCItems { get; set; }

    #endregion

    public UserControl1()
    {
        InitializeComponent();

        UCItems = new ObservableCollection<long>();
    }

    #region UCItemsSource Property

    public static readonly DependencyProperty UCItemsSourceProperty = DependencyProperty.Register("UCItemsSource", 
                                                                                                  typeof(IEnumerable), 
                                                                                                  typeof(UserControl1));                                                                                                      

    public IEnumerable UCItemsSource
    {
        get { return (IEnumerable)GetValue(UCItemsSourceProperty); }
        set { SetValue(UCItemsSourceProperty, value); }
    }

    #endregion
}

其他提示

Try this

private ObservableCollection<long> _windowCollection 
public ObservableCollection<long> WindowCollection 
{ 
   get { return _windowCollection; }
   set
   {
      _windowCollection = value;
      RaiseOnPropertyChange(() => WindowCollection);
   }
}
许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top