Search code examples
c#wpfobservablecollection

auto-wrapping for ObservableCollection elements


as title says, i'm trying to implement auto-wrapping of ObservableCollection items, currently i'm doing it using CollectionChanged event, where i check if element is of required type and if not, i create object of this type (which is a container as well), move element to it and try to replace current element in collection by the wrapper, but here i get run-time error that ObservableCollection cannot be modified during CollectionChanged event, so, my question is, how to implement auto-wrapping? here is the example code:

[ContentProperty("Items")]
public partial class BaseUserControl : UserControl
{
    public static readonly DependencyProperty ItemsProperty = DependencyProperty.Register("Items",
        typeof(ObservableCollection<FrameworkElement>), typeof(BaseUserControl));
    public ObservableCollection<FrameworkElement> Items
    {
        get { return (ObservableCollection<FrameworkElement>)GetValue(ItemsProperty); }
        set { SetValue(ItemsProperty, value); }
    }

    public BaseUserControl()
    {
        InitializeComponent();

        Items.CollectionChanged += Items_CollectionChanged;
    }

    private void Items_CollectionChanged(object? sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
    {
        for (int i = 0; i <  Items.Count; i++)
        {
            if (Items[i] is not WrapperUserControl)
            {
                WrapperUserControl wrapper = new WrapperUserControl();
                wrapper.Items.Add(Items[i]);
                Items[i] = wrapper;
                return;
            }
        }

        // do something else
    }
}

Solution

  • i get run-time error that ObservableCollection cannot be modified during CollectionChanged event

    This error only occurs when more than one handler is listening the CollectionChanged event, so if possible, do not expose the ObservableCollection property. You can encapsulate a CollectionChanged event to the desired listener.

    public partial class BaseUserControl : UserControl
    {
        ....
    
        public event NotifyCollectionChangedEventHandler? CollectionChanged;
    
        private void Items_CollectionChanged(object? sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
        {
            ....
            CollectionChanged?.Invoke(sender, e);
        }
    }