问题
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
回答1:
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
}
回答2:
Try this
private ObservableCollection<long> _windowCollection
public ObservableCollection<long> WindowCollection
{
get { return _windowCollection; }
set
{
_windowCollection = value;
RaiseOnPropertyChange(() => WindowCollection);
}
}
来源:https://stackoverflow.com/questions/22650599/how-to-bind-collection-dependency-property-in-usercontrol