User Control with custom ItemsSource dependency property

微笑、不失礼 提交于 2020-03-13 07:31:30

问题


I've got a UserControl with an ItemsSource property. As the base UserControl class does not implement ItemsSource, I had to create my own dependency property like this:

#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

Now in my ViewModel I have an Events property which is an ICollectionView of EventItem items like so:

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

The issue I'm facing is that in my View, when I bind to the Events property, and I add an Item to eventItems, the UserControl won't fire the ItemsSourceChanged event and hence not update the UI. For the sake of testing I added a simple listbox to the view which also binds to the Events property. That works like a charm. Updates to eventItems observableCollection are reflected in the ListBox.

I'm figuring it has something to do with my ItemsSource dependency property. Maybe I would need to use a Custom Control which inherits form ItemsControl instead of a UserControl?

To help you understand my problem: I'm trying to create a calendar like control which shows events/agenda entries (similar to Google Calendar). It works like a charm. The UI is updated when the control is resized. The only thing that's left is the automagical update once the ItemsSource changes.

Hope someone can help.

EDIT: The moment I posted I realized that the event can't be fired as the ItemsSource property does not change. It is the underlying collection that changes. However, I'm not how to handle that. What do I need to implement to make this work. Just a hint would be enough. I don't need every implementation details.


回答1:


Opening the PresentationFramework.dll within Reflector and looking at System.Windows.Controls.ItemsControl showed the following:

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

Not knowing what RefreshLayout does my hunch is that it has something to do with the way the ObservableCollection<T> is being wrapped as the above code is oblivious to what the concrete collection type is and it would therefore be handled by the type being wrapped; in this case an ObservableCollection<T> Try modifying your property as seen below to return the default view and adjust your ItemsSource property to be more akin to the above code from the framework and work backwards from there.

private ObservableCollection<Controls.EventCalendar.EventItem> eventItems;
private ICollectionview eventsView;
public System.ComponentModel.ICollectionView Events
{
    get
    {
        if (eventsView == null)
            eventsView = CollectionViewSource.GetDefaultView(eventItems);
        return eventsView;
    }
}


来源:https://stackoverflow.com/questions/4539416/user-control-with-custom-itemssource-dependency-property

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!