Removing DataContextChanged/Loaded Event Handlers on XAML User Control - c#

In efforts to reduce memory leaks, I am attempting to figure out whether, after adding a handler to the "DataContextChanged" event or the "Loaded" event on a XAML User Control, i.e. (UserControl.xaml.cs):
public MyUserControl()
{
InitializeComponent();
DataContextChanged += new DependencyPropertyChangedEventHandler(MyUserControl_DataContextChanged);
Loaded += new RoutedEventHandler(MyUserControl_Loaded);
}
If I need to remove it. Does WPF handle this, or do I need to remove them manually?

Short answer -- no.
You only need to remove handlers when they would keep an object rooted, meaning prevent its garbage collection. This will not happen if you create a child object and have one of its event handlers point into a parent object, because there are no dangling references to the child object.
It will happen if you create a child object and the parent object points one of its event handlers into the child object, because now the parent object has a reference to the child object that will keep it alive (rooted).
In the case you specify above, it's totally internal -- you are adding a reference to your own class, inside the class. When the user control is destroyed, it won't have references sitting around in another class' event handler. So you do not need to remove the event handler.

Related

Does not removing event Handlers cause problems in .Net?

I have the following code:
public void LoadPage()
{
LayoutPanel.Controls.Clear();
ContentObjects = LoadContentObjects();
foreach (string img in ContentObjects)
{
thumbnail = new ctrlThumbnailControl(img);
LayoutPanel.Controls.Add(thumbnail);
thumbnail.ThumbnailSelected += Thumbnail_ThumbnailSelected;
}
}
private void Thumbnail_ThumbnailSelected(string PathToMedia)
{
lblFileName.Text = PathToMedia;
}
I create a thumbnail control for each image loaded (limited to 150 per page). The thumbnail controller has an event which is raised on hovering over the control.
So this page has 150 controls with events. When I load the next page I do not remove the events. I do remove the controls. And create another 150 controls adding events to them.
So the question is:
Are the event handlers removed when the controls are Clear()ed?
if not how would I programmatically clear the event handlers without going through each control individually and removing it's handler?
What you're doing is safe because the thing you're throwing away is the thing that holds the reference. That is, each Control is holding a reference to a delegate which holds a reference to the object with the event handler, which is typically a Form or some other long-lived object, but the object with the event handler holds no reference to the Control after it is removed from the UI. The Control becomes completely unreachable (assuming you're not holding a reference to it for some other purpose), so it's irrelevant that it still "knows" about the long-lived object. When the garbage collector runs, the Control will vanish and the delegate with it.
On the other hand, what you don't want to do is register a short-lived object as an event handler for a long-lived object (without remembering to unregister). When you do this, even after you think you've "removed" the short-lived object, the long-lived object still knows about the short-lived object, which keeps it alive such that it is never garbage collected and you have what is essentially a managed-memory version of a memory leak (not to mention that the now-supposedly-disconnected objects will still be responding to events).
If the controls are not visible there is no way to trigger the event from the GUI. There is no need to detach the events. They do not do any "harm".

When do I detach event handlers in a control?

so I have a control whose panel attaches these events inside the panel's initialized event:
gvvm = DataContext as GraphViewerViewModel;
gvvm.ZoomToFitEvent += new EventHandler(_GraphViewerViewModel_ZoomToFitEvent);
gvvm.ZoomInEvent += new EventHandler(_GraphViewerViewModel_ZoomInEvent);
gvvm.ZoomOutEvent += new EventHandler(_GraphViewerViewModel_ZoomOutEvent);
gvvm.CloseVCDEvent += new EventHandler(gvvm_CloseVCDEvent);
gvvm.LoadVCDEvent += new EventHandler(gvvm_LoadVCDEvent);
gvvm.ScrollToTimeEvent += new EventHandler<GraphViewerViewModel.ScrollToTimeEventArgs>(gvvm_ScrollToTimeEvent);
Question 1. When should I detach the events? Is is appropriate to do so in panel.unloaded?
question 2. Is it appropriate to use events to communicate from your view model to your view? it seemed more reasonable than creating a property bool and doing actions in the panel based on the propertychanged event, though that has the advantage of not requiring me to subscribe/unsubscribe events. But the downside is I have to think of reasonable names for a property event toggle.
Answer to question #1 is yeaaahhh, kinda, Unloaded event should serve for releasing resources.
However if the event handler is living only inside the control and you know that the control is not gonna be added or removed from VisualTree constantly during runtime then you could let the garbage collector do the job for you. Means once nobody holds the instance to your control the garbage collector will collect all of it anyways.
Answer to question #2: Read what Bernard said. The communication between View and ViewModel should not exist. However the ViewModel may communicate with the View which is the case everytime you set a Binding or you use INotifyPropertyChanged interface.

When to remove event handlers from an object?

So I am registering some event handlers in an object's loaded event.
tv.PreviewMouseDown += new MouseButtonEventHandler(SignalScrollViewer_PreviewMouseDown);
but I had two questions.
If loaded occurs twice and it tries to add the event handler again will there be problems?
How should I handle unregistering the event? Will it automatically handle unregistering on destruction or do I need to handle it in some event like unloaded or something?
Yes that will cause another subscription which causes the handler to execute twice. You can remove the loaded handler inside the loaded handler.
MSDN:
Loaded and Unloaded might both be raised on controls as a result of user-initiated system theme changes. A theme change causes an invalidation of the control template and the contained visual tree, which in turn causes the entire control to unload and reload. Therefore Loaded cannot be assumed to occur only when a page is first loaded through navigation to the page.
If the object is gone it cannot raise any events, so no need to do anything about that. And the handler will not keep the object alive (it's the other way around).
If loaded occurs twice and it tries to add the event handler again will there be problems?
The event will get subscribed multiple times. You may want to handle this scenario.
How should I handle unregistering the event? Will it automatically handle unregistering on destruction or do I need to handle it in some event like unloaded or something?
You can unregister it in Unloaded or similar. In general, you only need to do this if object on which the event you're subscribing will live beyond your lifetime. If it's an object within yourself (ie: subscribing to an event on a button within a user control, from the user control), unsubscription isn't required.
One normally subscribes to events from inside the constructor, i.e. immediately after the tv object gets created. If you keep those together, it's not possible to subscribe multiple times to the same tv object.
One would want to register event handlers in Loaded and remove them in Unloaded, so any objects which handle the event would have a defined lifetime. So, one would wish that every Loaded event occurs only once when the control is shown, and is followed by an Unloaded event when the control is removed from sight.
However, Loaded might be raised several times, depending on your layout: Expander causes Loaded to be raised twice, but Unloaded only once on controls inside the Expander, and TabControl might reuse the same controls for different TabItems (different data) without raising Loaded or Unloaded in between.
I found two ways to get around this: Either use DataTemplates (this helps at least in the TabControl case), or use the DataContextChanged event to register / unregister the events, which is a good way to ensure that the object which receives the events is always the current DataContext (i.e. ViewModel).
You might also want to check out the following link for more information on attaching and removing event handlers and other behaviours: http://wpfglue.wordpress.com/2009/12/11/the-sticky-component-framework/

Unregistering all events to release memory

I have a program which allows the editing of product information. I noticed that it was not releasing memory after closing the editing forms. After some research I stumbled upon this question which mentions that the problem may be that it is hanging on to event subscriptions.
That made sense to me because this form has about 100+ controls on it, many of which are custom with custom events which are subscribed to by their parent controls. This creates a pretty large hierarchy of event subscriptions. So I looked for a way to release these and found this which allows you to unsubscribe from that event.
The problem is, I have a ton of subscriptions. Do I really have to manually unsubscribe from each event one by one on form close, or is there at least a way to release them in one fell swoop, or loop through them?
Remember this: The object on the LEFT of the += keeps alive the object containing the method on the RIGHT of the +=. That is, the object that raises the event keeps alive the object that handles the event, even if the object (such as a form) that handles the event is disposed.
So the thing you need to ensure is that all the event-raisers have gone away.
However, if all the event-raisers happen to be controls in the same Form class that subscribes to those events, you will not need to manually unhook all the event handlers when the form is closed.
This is because the controls that raise the events to which to form has subscribed have the same lifetime as the form itself.
You only need to worry if you subscribe to events raised by an object that has a longer lifetime than the object that is subscribing. Then the subscribing object (the form) would need to unsubscribe when it (the form) is disposed.
It depends on how long your form and its events will be living.
However, you can loop through your controls within the form, releasing the events.
If you remove a nonexisting event accidentally - don't worry, it won't throw an exception.
For example, this is how to get rid of all your TextBox.KeyDown-Events:
private void frm_FormClosed(object sender, FormClosedEventArgs e)
{
foreach (Control tb in this.Controls)
{
if (tb is TextBox)
{
TextBox tb1 = (TextBox)tb;
tb1.KeyDown -= TextBox_KeyDown;
}
}

C# Should I manually remove the event handler I declared?

Okay, make an example here:
I have UserControl A, UserControl B, UserControl C and one Windows Form.
This Windows Form is only started with UserControl A.
UserControl C has [Next] and [Back] buttons.
Say, UserControl A is declared with an event handler. One of function in UserControl A will actually raise the event call to execute one function at UserControl C.
So, at UserControl C, I have to add with
"UserControlA.OneFunction += this.UserControlC_Function;"
If I click Next button at UserControl C, it will dispose the UserControl A and add new UserControl B to the Windows Form. But I never remove this event handler manually.
One of the function in UserControl A is the caller (where event is declared).
One of the function in UserControl C is the listener.
So, these are my questions:
Should I manually remove the handler before UserControl A disposed?
Will this User Control A dispose automatically remove the handler that declared previously?
Should I add this somewhere?
"UserControlA.OneFunction -= this.UserControlC_Function;"
By convention, we don't. And since no event should be invoked after disposal, there is no need to do so unless the control in question is behaving weirdly.
No. At least there isn't such code as seen from reflector.
You don't need to remove the handlers in this case because neither the form nor its buttons are referenced by code external to the form, and the entire object graph will therefore be garbage collected.
The answer to this post does a really good job explaining when you need to manually remove an event handler and when it is not necessary.
Do I need to remove event subscriptions from objects before they are orphaned?
If the form is released (assuming no other objects has a reference to the objects in question) there's little risk of not removing the event handler, however it's a good idea always to remove the event handler before the object listening can no longer be reach (ie all variables referencing the object i sout of scope) not doing so can create a memory leak.
This is not the case in your situation (if I get what you are describing, code would make it more clear)
The problem would be if you attach a delegate referencing object C to an event on object A and then looses access to C (e.g. assigning a new value to the variable). C would then hang around until A is garbage collected
If the memory lifetime of an event publisher is not limited relative to the useful lifetime of an event subscriber, failure to unsubscribe an event will likely cause a memory leak. Were it not for the unfortunate hassle of doing so, there wouldn't be any reason for an event subscriber that was being disposed not to unsubscribe from all events, and for an event publisher that was being disposed not to nullify all event subscriptions. Since neither C# nor VB provides any convenient means of doing those things, however, one has to balance the hassle of proper subscription handling with the fact that in many situations one can get away skimping on it.

Categories