views:

1049

answers:

3

One-line summary: What is the best practice for unhooking event handlers created in the constructor of a UserControl in Silverlight2?

Background: I am currently building a line-of-business application in Silverlight2. As Silverlight is a browser plugin, there is no concept of a Window - everything is done within UserControls. The way I'm handling different "forms" in the application is to have a top-level usercontrol that contains a Viewbox. To show different forms, I set the Child property of the Viewbox to different UserControls. My app has a singleton PageManager class that is called to open and close forms. The forms (UserControls) are stored in a stack. Opening a form puts it on the top of the stack, closing it removes it from the stack and shows the one below it.

I'm trying to follow the Model-View-ViewModel pattern. In each form (derived from UserControl), I have a ViewModel that manages all the data for the View. The ViewModel exposes events so the UI can be notified when operations such as load and save have completed.

In my form, I subscribe to the event in the constructor, after I've got the ViewModel

public partial class MyPage : UserControl
{

    public MyViewModel ViewModel{get; set;}

    // other constructors, which create the viewmodel and call the constructor below.

    public MyPage(MyViewModel viewModel)
    {
        InitializeComponent();
        ViewModel = viewModel;
        this.LayoutRoot.DataContext = this.ViewModel;

        // subscribe to event so we can do stuff
        this.ViewModel.LoadCompleted += new MyViewModel.LoadCompletedEventHandler(ViewModel_LoadCompleted);
    }

My question is: Now that I've subscribed to this event, when do I remove the handler? Do I create a destructor and do it there, or does that create a chicken-and-egg situation where the garbage collector wont destroy the object until all references (ie: the event handlers) are gone? Do I create an interface that the forms must implement that specifies an UnhookEvents function that's called when the form is closed by the PageManager?

Edit: Thanks for the responses. What about the situation where the ViewModel lasts longer than the form (UserControl)? Part of my app allows users to create what is quite a complex structure, but in 95% of cases it's much simpler. What I've did was create 2 forms that use the same ViewModel. Users can start filling out the simple form, then switch to advanced mode, which creates a new form, passing the ViewModel to it.

In the simple setup form:

    private void AdvancedSessionSetupButton_Click(object sender, RoutedEventArgs e)
    {
        PageManager.GetPageManager().Close(this);
        PageManager.GetPageManager().Open(new CreateSessionPage(this.ViewModel), "Create Session");
    }

In the advanced setup form:

    private void BasicSessionSetupButton_Click(object sender, RoutedEventArgs e)
    {
        PageManager.GetPageManager().Close(this);
        PageManager.GetPageManager().Open(new CreateBasicSessionPage(this.ViewModel), "Create Session");
    }

After PageManager.Close, the only things referencing the form are the events within the ViewModel. I guess that's where I should be unhooking them.

+1  A: 

Events are automatically unbinded when the garbage collector goes through your object.

But you can explicitly unbind them with the "-=" syntax at anytime:

this.ViewModel.LoadCompleted -= ViewMode_LoadCompleted;

You can implement a destructor:

~MyPage
{
    this.ViewModel.LoadCompleted -= ViewMode_LoadCompleted;
}
tucod
+1  A: 

A destructor, more commonly known to C# programmers as Finalizers, is not necessary in this case. Assuming that ViewModel_LoadCompleted is a member function, it contains a pointer to "this" which you are giving to the ViewModel object which is fully contained by "this". The garbage collector should intelligently ignore this.

In this case, the correct thing to do is to not waste time unbinding them.

In general, you need to unbind an event handler when you pass "this" (explicitly, or implicitly) to some object which will hold that reference longer than the intended lifetime of "this". For example, if you set a handler on a parent control's event. Now the parent has a reference to you via the handler as well as in its Children controls collection. In this case, you should unbind when you are removed from the parent.

When in doubt, implement IDisposable and unbind in the call to Dispose().

Brandon Bloom
Typo, "descructor"
Richard Szalay
What if my ViewModel lasts longer than the UserControl? (I'll edit the question to show how)
geofftnz
If your model lasts longer than the user control, you don't need to worry about unbinding then either. The model shouldn't have a reference to the control. When the control goes out of scope, it will be garbage collected and won't point to the model any more.
Brandon Bloom
How so? The MulticastDelegate that backs the event holds a strong reference to the object handling the event.
Jeffrey Hantin
A: 

I have a similar issue, where the event is being hooked up in the constructor of a Usercontrol which is in a dataTemplate for listboxitems. Load up new data and the old listboxitems are still hanging around because I've not unhooked the event handlers.

Stephen Price