使用自定义ItemsSource依赖属性进行用户控件控制

9

我有一个带有ItemsSource属性的UserControl。由于基础UserControl类没有实现ItemsSource,因此我不得不创建自己的依赖属性,如下所示:

#region ItemsSource Dependency Property
    public static readonly DependencyProperty ItemsSourceProperty =
        DependencyProperty.Register("ItemsSource", typeof(IEnumerable), typeof(MonthViewControl),
        new PropertyMetadata(OnItemsSourceChanged));

    static void OnItemsSourceChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e)
    {
        (obj as MonthViewControl).OnItemsSourceChanged(e);
    }

    private void OnItemsSourceChanged(DependencyPropertyChangedEventArgs e)
    {
        RefreshLayout();
    }

    public IEnumerable ItemsSource
    {
        get
        {
            return (base.GetValue(ItemsSourceProperty) as IEnumerable);
        }
        set
        {
            base.SetValue(ItemsSourceProperty, value);
        }
    }

    #endregion

现在在我的ViewModel中,我有一个Events属性,它是一个EventItem项目的ICollectionView集合,如下所示:

private ObservableCollection<Controls.EventCalendar.EventItem> eventItems;
    private CollectionViewSource events;
    public System.ComponentModel.ICollectionView Events
    {
        get
        {
            if (events == null)
            {
                events = new CollectionViewSource();
                events.Source = eventItems;
            }

            return events.View;
        }
    }

我面临的问题是,在我的视图中,当我绑定到Events属性并向eventItems添加一个项目时,用户控件不会触发ItemsSourceChanged事件,因此不会更新UI。为了测试,我在视图中添加了一个简单的ListBox,它也绑定到Events属性。那很好用。对eventItems observableCollection的更新反映在ListBox中。
我想这与我的ItemsSource依赖属性有关。也许我需要使用从ItemsControl继承而来的自定义控件,而不是用户控件?
为了帮助您理解我的问题:我正在尝试创建一个类似于Google日历的控件,显示事件/议程条目。它很好用。当控件调整大小时,UI会更新。唯一剩下的就是在ItemsSource更改后的自动更新。
希望有人能帮忙。
编辑:我发布时意识到,由于ItemsSource属性未更改,因此无法触发事件。它是基础集合发生变化。但是,我不知道如何处理。我需要实现什么才能使其工作?只需要一个提示即可。我不需要每个实现细节。
1个回答

6

打开Reflector并查看System.Windows.Controls.ItemsControl中的PresentationFramework.dll,显示如下:

public static readonly DependencyProperty ItemsSourceProperty = DependencyProperty.Register("ItemsSource", typeof(IEnumerable), 
   typeof(ItemsControl), new FrameworkPropertyMetadata(null, 
   new PropertyChangedCallback(ItemsControl.OnItemsSourceChanged)));

private static void OnItemsSourceChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
    ItemsControl control = (ItemsControl) d;
    IEnumerable oldValue = (IEnumerable) e.OldValue;
    IEnumerable newValue = (IEnumerable) e.NewValue;
    ItemValueStorageField.ClearValue(d);
    if ((e.NewValue == null) && !BindingOperations.IsDataBound(d, ItemsSourceProperty))
    {
        control.Items.ClearItemsSource();
    }
    else
    {
        control.Items.SetItemsSource(newValue);
    }
    control.OnItemsSourceChanged(oldValue, newValue);
}

不知道RefreshLayout是做什么的,但我猜它与ObservableCollection<T>的包装方式有关,因为上面的代码不知道具体的集合类型,所以应该由被包装的类型处理;在这种情况下,是一个ObservableCollection<T>。请尝试修改您的属性如下,返回默认视图并调整ItemsSource属性,使其更类似于框架中的上述代码,然后从那里开始进行调整。
private ObservableCollection<Controls.EventCalendar.EventItem> eventItems;
private ICollectionview eventsView;
public System.ComponentModel.ICollectionView Events
{
    get
    {
        if (eventsView == null)
            eventsView = CollectionViewSource.GetDefaultView(eventItems);
        return eventsView;
    }
}

抱歉回复晚了。我会将您的答案标记为“答案”,因为它看起来是正确的方法。但是,我不能确定,因为我还没有尝试过。实际上,我决定将控件从UserControl重新设计为继承自ItemsControl的CustomControl,这似乎(目前)解决了问题。 - Gilles Radrizzi

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接