0

I am modifying the standard WPF Grid.

On it I have several ObservableItemCollections that are dependency properties.

These dependency properties have Property Changed Callbacks that set instance event handlers to the CollectionChanged and ItemChanged events.

The Observable Collections work internally to the control and are not linked to the ViewModel.

The Grid doesn't appear to have a dispose method and the deconstructor isn't being called.

How do I release the events as I'm getting a memory leak?

Many thanks in advance.

Example Code:

    public class ChDynamicGrid : Grid 
{
    #region Dependency Properties

    public ObservableItemCollection<CHColumnDefinition> CHColumnDefinitions
    {
        get { return (ObservableItemCollection<CHColumnDefinition>)GetValue(CHColumnDefinitionsProperty); }
        set { SetValue(CHColumnDefinitionsProperty, value); }
    }

    public static readonly DependencyProperty CHColumnDefinitionsProperty =
        DependencyProperty.Register(
            "CHColumnDefinitions",
            typeof(ObservableItemCollection<CHColumnDefinition>),
            typeof(ChDynamicGrid),
            new PropertyMetadata(null, new PropertyChangedCallback(OnColumnDefinitionsChanged))
         );


    #endregion //Dependency Properties

    public ChDynamicGrid() : base()
    {
        ShowGridLines = true;
        CHColumnDefinitions = new ObservableItemCollection<CHColumnDefinition>();
    }

    ~ChDynamicGrid()
    {
        this.CHColumnDefinitions.ItemChanged -= CHColumnDefinitions_ItemChanged;
        this.CHColumnDefinitions.CollectionChanged -= CHColumnDefinitions_CollectionChanged;

    }

    private static void OnColumnDefinitionsChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        var g = (ChDynamicGrid)d;
        if (g == null) return;
        if (e.OldValue != null)
        { 
            ((ObservableItemCollection<CHColumnDefinition>)e.OldValue).ItemChanged -= g.CHColumnDefinitions_ItemChanged;
            ((ObservableItemCollection<CHColumnDefinition>)e.OldValue).CollectionChanged -= g.CHColumnDefinitions_CollectionChanged;
        }
        if (e.NewValue != null)
        {
            ((ObservableItemCollection<CHColumnDefinition>)e.NewValue).ItemChanged += g.CHColumnDefinitions_ItemChanged;
            ((ObservableItemCollection<CHColumnDefinition>)e.NewValue).CollectionChanged += g.CHColumnDefinitions_CollectionChanged;
        }
    }


    private void CHColumnDefinitions_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e){}

    private void CHColumnDefinitions_ItemChanged(object sender, ItemChangedEventArgs<CHColumnDefinition> e){}

}
Lance
  • 251
  • 5
  • 13
  • At the same time as subscribing to certain events you have to also subscribe to some other events which can be used to "clean up", not sure how good is [Unloaded](https://learn.microsoft.com/en-us/dotnet/api/system.windows.frameworkelement.unloaded) in your case. If it's not possible you can try to use [weak events](https://learn.microsoft.com/en-us/dotnet/framework/wpf/advanced/weak-event-patterns). – Sinatr Aug 22 '19 at 14:43
  • 1
    I think you should use the weak event pattern rather than a strong reference. http://blog.functionalfun.net/2012/03/weak-events-in-net-easy-way.html – Andy Aug 22 '19 at 14:46
  • @Sinatr Unloaded is not good. It'll get raised when the view is temporarily not visible. OP: Personally, I would make my collection properties non-generic IEnumerable leave all of the updating to bindings in the control template. I handle events like that in viewmodels, but they're all IDisposable. – 15ee8f99-57ff-4f92-890c-b56153 Aug 22 '19 at 14:50
  • I implemented IDisposable. Now I get "The calling thread cannot access this object because a different thread owns it" error on the instance property. – Lance Aug 22 '19 at 15:04
  • @Lance You can't touch the contents of an ObservableCollection from a thread other than the one where it was created. Sounds like you may have more than one can of worms here. – 15ee8f99-57ff-4f92-890c-b56153 Aug 22 '19 at 16:18
  • I put the following code in my Dispose method: this.Dispatcher.Invoke(() => { this.CHColumnDefinitions.ItemChanged -= CHColumnDefinitions_ItemChanged;}); appears to fix the thread issue. I am reading up on weak events as this sounds like a better way to handle it – Lance Aug 22 '19 at 16:28

0 Answers0