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)!
In "pure" MVVM, the ViewModel shouldn't really reference the View. It's often convenient, however, to provide some form of interface in the View whereby the ViewModel can interact with it.
MVVM (Model-View-ViewModel) MVVM is a way of creating client applications that leverages core features of the WPF platform, allows for simple unit testing of application functionality, and helps developers and designers work together with less technical difficulties.
Some comments:
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With