【问题标题】:How to bind collection dependency property in UserControl如何在 UserControl 中绑定集合依赖属性
【发布时间】:2014-03-27 02:34:01
【问题描述】:

这不是重复的!当我失败时,我试图寻找类似的帖子,但没有成功。我不明白为什么不调用OnUCItemsSourceChanged?我很确定我错过了一些简单的东西,但我找不到它。

我有Window,其中包含UserControl1,它附加了绑定到WindowWindowCollection 集合的集合属性。我希望在向WindowCollection 添加项目时调用UserControl1.OnUCItemsSourceChanged。但它不会发生。

我想念什么?

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}" ... />

更新 这是我的测试项目的link

【问题讨论】:

    标签: c# wpf user-controls observablecollection


    【解决方案1】:

    在这一行:

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

    必须是 RelativeSource 和 FindAncestor,因为 UCItems 位于 UserControl:

    UserControl

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

    I cannot understand why OnUCItemsSourceChanged is not called?

    如果你添加RelativeSource 构造,那么OnUCItemsSourceChanged 至少会导致一次,因为PropertyChangedCallback 每次都会触发然后你为依赖属性设置新值:

    表示依赖属性的有效属性值发生变化时调用的回调。

    因为你曾经在这里设置了依赖属性的值:

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

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

    这是一个ObservableCollection&lt;T&gt;.CollectionChanged 事件,其中包含对集合执行的操作的枚举:

    在添加、删除、更改、移动项目或刷新整个列表时发生。

    对于您的情况,它将是这样的:

    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);
                }
            }
        }
    }
    

    此项目可在此link

    Alternative version

    这个版本更简单,更正确。这里我们只引用了包含集合的UCItemsSource 属性,这里也是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
    }
    

    【讨论】:

    • 它不起作用。我更新了我的帖子 - UCItems 的类型是 ObservableCollection&lt;MyItem&gt; 而不是 ObservableCollection&lt;long&gt;。无论如何,如果DataContextUserControlUserControl1,为什么我需要RelativeSource
    • @theateist: UCItems is of type ObservableCollection&lt;MyItem&gt; and not ObservableCollection&lt;long&gt; - 为什么你的问题没有立即包含这个?究竟是什么不起作用?
    • 当我将项目添加到WindowCollection 时,它们应该被添加到绑定到ItemsControl.ItemsSourceUserControl1UCItems 集合中。与ItemsControl.ItemsSource 的工作方式非常相似。我添加了指向我的测试项目的链接。如果您能看一下,我将不胜感激
    • @theateist:我看了你的例子。找到了一些笔记。首先,输入ObservableCollection&lt;T&gt; 应该是一个。也就是说,或者所有LongMyItem 类型。在我的情况下,在主窗口绑定将不起作用然后声明不同的类型。其次,OnUCItemsSourceChanged 的处理程序中的代码仅在程序启动时运行一次。在您分配新集合之前,此处理程序不会调用,因此不会更新集合。
    • 那么,有什么办法可以做我想做的事——将long 的集合传递给UserControl1,它应该从中创建另一个应该绑定到ItemsControl.ItemsSource 的集合?
    【解决方案2】:

    试试这个

    private ObservableCollection<long> _windowCollection 
    public ObservableCollection<long> WindowCollection 
    { 
       get { return _windowCollection; }
       set
       {
          _windowCollection = value;
          RaiseOnPropertyChange(() => WindowCollection);
       }
    }
    

    【讨论】:

      猜你喜欢
      • 2014-08-31
      • 2017-12-24
      • 1970-01-01
      • 2023-04-07
      • 2014-04-10
      • 1970-01-01
      • 1970-01-01
      • 2017-12-18
      • 2011-07-29
      相关资源
      最近更新 更多