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.
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().
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;
}
Related
I have a child window and there is a ComboBox. I want to send value of child window to parent window using WPF and MVVM. Can anyone help me how to do it ??
Don't focus on MVVM for this scenario, MVVM is not designed for passing values from child windows back to parent windows.
Instead, if you have some code in a viewmodel or the code behind of a view that spawns or opens the child window, then that code should be responsible for retrieving the value determined by the child window and propagating it back to the appropriate property (at which point any binding will reflect the value back in the UI - this is where MVVM should be used).
The best thing you can do is make sure the code that opens the child window doesn't go directly accessing a ComboBox on the child window, instead the child window should bind it to a property which is then accessed by the parent's code (preferably via an interface).
For further reading check out Creating an MVVM friendly dialog strategy. This should be your preferred solution, then the parent code simply uses the Dialog Service to show the child window, and the Dialog Service is responsible for aggregating the child window result and making it available back to the calling code in the parent window.
Since you havent given adequate info so, lets assume the child window is a dialog. Now, lets assume the child window is a class Child() with its ViewModel having the object in Child class, say
public ChildViewModel chVM { get; set; }
and this viewmodel having the property:
public string ComboBoxSelectedValue { get; set; }
Lets have the xaml of the dialog having the combobox as this :-
<ComboBox Name="cbTest" SelectedItem="{Binding ComboBoxSelectedValue}">
<ComboBoxItem>A</ComboBoxItem>
<ComboBoxItem>B</ComboBoxItem>
<ComboBoxItem>C</ComboBoxItem>
</ComboBox>
Now, everytime a value is selected in the combobnox, the property in its viewmodel that is, ComboBoxSelectedValue will be filled with the selected value.
You have to handle the Close event of the dialog on your parent page. Lets move on to the class Parent() that is the parent page:
public partial class Parent : Page
{
private Child ch;
public Parent()
{
InitializeComponent();
ch= new Child();
ch.Closed += ChildClosed;
}
public void ChildClosed(object sender, System.EventArgs e)
{
//even after closing of child window
var selectedValue = ch.chVM.ComboBoxSelectedValue;
}
public void OpenChild(object sender, System.EventArgs e)
{
//Button event to open the child window
ch.Show();
}
}
Please respond if this is the one you needed. Or else please feel free to ask for another solution. This can be done without mvvm also, but since you asked for MVVM, so this is the solution.
The best solution I have found for allowing view models to communicate with each other is via a messaging framework, my preference is MVVM light available on nuget.
Your child view model sends a message via the framework, which the parent subscribes to. Not dissimilar to event handlers.
Child...
Messenger.Default.Send<MyMessageClass>(message);
That can go in a combo box selected item binding setter, or part of a command action method.
Parent...
Messenger.Default.Register<MyMessageClass>(this, OnMessage);
The MyMessageClass must extend BaseMessage and should include properties for the data you want to share. The OnMessage method in the parent should accept this class as a parameter and do whatever you need it to in the parent with those values.
Is is better MVVM since it keeps the logic out of code behind or view, but also doesn't create strong couplings. If a view model sends a message that no other view model receives, nothing happens - you also use message objects rather than inspection of view models to share data.
Tutorial on msdn here.
I will appreciate if some body can explain with a simple example.
Imagine a Window containing a dense hierarchy of child controls. Now let's say you want to do something, there's a right click anywhere in your window.
With normal events, you'd have to handle a Click event for all controls, because you're not sure where the user might click.
With WPF's routed events, the events either "bubble" or "tunnel" (i.e travel up the UI tree or down) if they dont find an event handler, which "handles" it at the current level. So you could write one handler for the window's event i.e. TopLevel. (WPF has a convention of event pairs, PreviewXXX and XXX - the PreviewXXX event fires first and tunnels down from root to control which received the stimulus and the counterpart XXX event then bubbles up from child control back upto Root). So if you right click a button, WPF travels up the UI hierarchy, invoking all handlers that it finds (unless someone marks the event has "handled" in the event args.)
Routed events are events with more 'traveling abilities', as mentioned in a Gishu's answer. Routed events are represented by an instance of a RoutedEvent class + ordinary .NET event, which wraps it:
public class MyClassWithARoutedEvent : UIElement
{
public static readonly RoutedEvent DoSomethingEvent;
public event RoutedEventHandler DoSomething
{
add { base.AddHandler ( MyClassWithARoutedEvent.DoSomethingEvent, value );
remove { base.AddHandler ( MyClassWithARoutedEvent.DoSomethingEvent, value );
}
}
You would typically use touted events in such situations:
Implementing your own control which seamlessly integrates with WPF's infrastructure
Processing events, fired by different controls at a common root
Sort of communication between elements in an element tree
In most situations you will probably use the routed events infrastructure without even noticing it.
In addition it's worth to mention, that you can use RoutedEvent in your control even if it does not define it or even inherits from an element, which does. That's because you can really think about a RoutedEvent instance as a strong typed name of an event. So, if you have an access to this 'name' (this is why an instance of a routed event is usually made public), you can owe it:
public class MyClassWithARoutedEvent : UIElement
{
public static readonly RoutedEvent ClickEvent;
static MyClassWithARoutedEvent ( )
{
ClickEvent = ButtonBase.ClickEvent.AddOwner( typeof ( MyClassWithARoutedEvent ) );
}
// A wrapper should be placed here as described above
}
I have a ListBox that is bound to a ViewModel that exposes a parameter of type ObservableCollection. I have setup an ICommand that gets fired when one of the rows in the ListBox is selected. (using method 3 in this post - it works great by the way).
Now my question (which has nothing to do with method 3 described above or the ListBox) is when my ICommand is fired and what I want to do is navigate to a different page (eg: details page), where is the logic stored (or how do I do it?).
The reason I ask is that I am not sure how to setup the command method in the ViewModel class such that it remains testable.
ps: I am using Prism and was also wondering if it provides any classes/patterns for Navigation.
Just to elaborate on the use of IEventAggregator - it gives you a simple Pub/Sub model for sending arbitrary messages between decoupled (ie neither needs to know anything about the other) parts of the application. So we can get a reference to the IEventAggregator in our ViewModel constructor (this is automatically resolved for you by the framework) ie:
private IEventAggregator eventAggregator;
public PublisherViewModel(IEventAggregator eventAggregator)
{
this.eventAggregator = eventAggregator;
Then in our selection changed handler, we can publish the event:
var changedEvt = eventAggregator.GetEvent<MyListboxChangedEvent>();
changedEvt.Publish(selectedItemId);
This relies on our custom event class MyListboxChangedEvent:
public class MyListboxChangedEvent : CompositePresentationEvent<int> {}
So finally, in the ViewModel which responds to this action, we set up a subscription to the event, and corresponding handler method:
public SubscriberViewModel(IEventAggregator eventAggregator)
{
var changedEvt = eventAggregator.GetEvent<MyListboxChangedEvent>();
changedEvt.Subscribe(OnListBoxChanged, ThreadOption.UIThread);
}
public void OnListBoxChanged(int selectionId)
{
// do whatever we need
}
Seems like a lot of glue, but it becomes a logical method for wiring the different parts of the UI together, and it becomes second nature pretty quickly.
Have you considered using the EventAggregator to send the message that you want to show a different view. The StockTrader application included in the PRISM distribution will provide a good example of the use.
In Microsoft's view injection sample/article they have the code like the following:
public void Initialize()
{
this.RegisterViewsAndServices();
EmployeesPresenter presenter = this.container.Resolve<EmployeesPresenter>();
IRegion mainRegion = this.regionManager.Regions[RegionNames.MainRegion];
mainRegion.Add(presenter.View);
}
http://msdn.microsoft.com/en-us/library/dd458920.aspx
here Presenter is resolved which contains the public property of type IEmployeesView and thats used for injecting the view to the region. The benefit of resolving the presenter is that it gets automatically tied to the view (by taking it in constructor (via unity)). However don't you think the Presenter is prone to garbage collection because nothing has reference to presenter after the scope of initialize method ends?
View/ViewModel obviously won't have reference to presenter unless VM/View has an event which is subscribed by presenter. We can go into an inconsistent state in which the view is active but the presenter is garbage collected.
To prevent garbage collection of presenter probably we'll need a KeepAlive property in ViewModel that just holds the reference to presenter for preventing its GC but that sounds hacky to me. What do you do or would do in this situation?
Please note that in a situation where there will be multiple instances of the view, registering the presenter with ContainerControlledLifetimeManager is not feasible. Also if the mode of communication for presenter (with view) is via commands and the commands happen to be DelegateCommands of prism then they will only keep weak reference to the presenter so that won't serve the purpose either.
This is a complicated question about lifetime. In this example in the Prism documentation, the implementation of the EmployeesPresenter hooks up to an event on the EmployeesListPresenter:
public EmployeesPresenter(
IEmployeesView view,
IEmployeesListPresenter listPresenter,
IEmployeesController employeeController)
{
this.View = view;
this.listPresenter = listPresenter;
this.listPresenter.EmployeeSelected += new EventHandler<DataEventArgs<BusinessEntities.Employee>>(this.OnEmployeeSelected);
this.employeeController = employeeController;
View.SetHeader(listPresenter.View);
}
This ties the lifetime of the EmployeesPresenter to the lifetime of the IEmployeesListPresenter. It is registered with the container like this:
this.container.RegisterType<IEmployeesListPresenter, EmployeesListPresenter>();
Not staticly or ContainerControlledLifetime, either. Now we have to look at the implementation of EmployeesListPresenter. Here is its constructor:
public EmployeesListPresenter(IEmployeesListView view,
IEmployeeService employeeService)
{
this.View = view;
this.View.EmployeeSelected += delegate(object sender, DataEventArgs<BusinessEntities.Employee> e)
{
EmployeeSelected(sender, e);
};
view.Model = employeeService.RetrieveEmployees();
}
Now we see that the EmployeesListPresenter is tied up in the lifetime of the IEmployeesListView.
So, the lifetime of the EmployeesPresenter is the same as the EmployeesListView, which will be essentially as long as it is in the control tree.
This is a pretty confusing sample. You will find that the Prism 4 samples are much more straightforward... I would recommend looking at them and possibly upgrading to Prism 4 if you have a choice.
This question already has answers here:
How can I Have a WPF EventTrigger on a View trigger when the underlying Viewmodel dictates it should?
(4 answers)
Closed 8 years ago.
I have quite simple (I hope :)) problem:
In MVVM, View usually listens on changes of ViewModel's properties. However, I would sometimes like to listen on event, so that, for example, View could start animation, or close window, when VM signals.
Doing it via bool property with NotifyPropertyChanged (and starting animation only when it changes from false to true) is possible, but it feels like a hack, I'd much prefer to expose event, as it is semantically correct.
Also, I'd like to do it without code in codebehind, as doing viewModel.myEvent += handler there would mean that I'd have manually unregister the event in order to allow View to be GC'd - WPF Views are already able to listen on properties 'weakly', and I'd much prefer to program only declaratively in View.
The standard strong event subscription is also bad, because I need to switch multiple ViewModels for one View (because creating View every time takes too much CPU time).
Thank you for ideas (if there is a standard solution, a link to msdn will suffice)!
Some comments:
You can use the weak event pattern to ensure that the view can be GC'd even if it is still attached to the view model's event
If you're already switching multiple VMs in for the one view, wouldn't that be the ideal place to attach/detach the handler?
Depending on your exact scenario, you could just have the VM expose a state property which the view uses as a trigger for animations, transitions, and other visual changes. Visual state manager is great for this kind of thing.
This is something that I wrestled with as well...
Similar to what others are saying, but here is an example with some code snippets... This example shows how to use pub/sub to have a View subscribe to an event fired by the VM - in this case I do a GridView. Rebind to ensure the gv is in sync with the VM...
View (Sub):
using Microsoft.Practices.Composite.Events;
using Microsoft.Practices.Composite.Presentation.Events;
private SubscriptionToken getRequiresRebindToken = null;
private void SubscribeToRequiresRebindEvents()
{
this.getRequiresRebindToken =
EventBus.Current.GetEvent<RequiresRebindEvent>()
.Subscribe(this.OnRequiresRebindEventReceived,
ThreadOption.PublisherThread, false,
MemoryLeakHelper.DummyPredicate);
}
public void OnRequiresRebindEventReceived(RequiresRebindEventPayload payload)
{
if (payload != null)
{
if (payload.RequiresRebind)
{
using (this.gridView.DeferRefresh())
{
this.gridView.Rebind();
}
}
}
}
private void UnsubscribeFromRequiresRebindEvents()
{
if (this.getRequiresRebindToken != null)
{
EventBus.Current.GetEvent<RequiresRebindEvent>()
.Unsubscribe(this.getRequiresRebindToken);
this.getRequiresRebindToken = null;
}
}
Call unsub from the close method to prevent memory leaks.
ViewModel (Pub):
private void PublishRequiresRebindEvent()
{
var payload = new RequiresRebindEventPayload();
payload.SetRequiresRebind();
EventBus.Current.GetEvent<RequiresRebindEvent>().Publish(payload);
}
Payload class
using System;
using Microsoft.Practices.Composite.Presentation.Events;
public class RequiresRebindEvent
: CompositePresentationEvent<RequiresRebindEventPayload>
{
}
public class RequiresRebindEventPayload
{
public RequiresRebindEventPayload()
{
this.RequiresRebind = false;
}
public bool RequiresRebind { get; private set; }
public void SetRequiresRebind()
{
this.RequiresRebind = true;
}
}
Note that you can also set the constructor up to pass in a Guid, or some identified in, which can be set on Pub and checked on sub to be sure pub/sub is in sync.
imho yYand separated
state - to be able to move data back/forth between view <-> vm
actions - to be able to call onto view model functions/commands
notifications - to be able to signal to the view that something has happened and you want it to take a viewy action like make an element glow, switch styles, change layout, focus another element etc.
while is true that you can do this with a property binding, its more of a hack as tomas mentioned; always has felt like this to me.
my solution to be able to listen for 'events' from a view model aka notifications is to simple listen for data-context changes and when it does change i verify the type is the vm i'm looking for and connect the events. crude but simple.
what i would really like is a simple way to define some 'view model event' triggers and then provide some kind of handler for it that would react on the view side of things all in the xaml and only drop to code behind for stuff thats not do-able in xaml
Like adrianm said, when you trigger your animation off a bool property you are actually responding to an event. Specifically the event PropertyChanged which the WPF subsystem. Which is designed to attach/detach correctly to/from so that you don't leak memory (you may forget to do this when wiring an event yourself and cause a memory leak by having a reference active to an object which otherwise should be GCed).
This allows you to expose your ViewModel as the DataContext for the control and respond correctly to the changing of properties on the datacontext through databinding.
MVVM is a pattern that works particularly well with WPF because of all these things that WPF gives you, and triggering off a property change is actually an elegant way to use the entire WPF subsystem to accomplish your goals :)
A more general question to ask is: "Why am I trying to deal with this event in my ViewModel?"
If the answer has anything to do with view-only things like animations, I'd argue the ViewModel needs not know about it: code behind (when appropriate), Data/Event/PropertyTriggers, and the newer VisualStateManager constructs will serve you much better, and maintain the clean separation between View and ViewModel.
If something needs to "happen" as a result of the event, then what you really want to use is a Command pattern - either by using the CommandManger, handling the event in code behind and invoking the command on the view model, or by using attached behaviors in the System.Interactivity libs.
Either way, you want to keep your ViewModel as "pure" as you can - if you see anything View-specific in there, you're probably doing it wrong. :)