Make View Models activation aware in Prism/Composite MVVM WPF application - wpf

In my MVVMC application I have a process which contains multiple steps, basically a wizard.
My controller resolves my outer View (call it WizardView) and adds it to the main region.
WizardView contains a breadcrumb trail to show the progress through the wizard and a sub Region to load other views into (call this WizardRegion). Step1View is the first View loaded into WizardRegion.
Each view has it's ViewModel injected into the constructor using the Unity container.
WizardViewModel subscribes to several event aggregation events which are published by the step view models.
As each step is completed the View Model publishes an event which WizardViewModel uses to store the state, this means WizardViewModel is collecting the data from each step as we progress. The step ViewModel also calls the controller to load the next step into WizardRegion.
At the final step WizardViewModel saves the result of the wizard and the MainRegion is navigated back to some other screen.
The next time we enter the wizard, we create a new instance of all the Views and ViewModels but the event subscriptions from the previous wizard still exist.
How can I make my view models aware that they're de-activated so I can unsubscribe my events?
Another option would be to unsubscribe from the event in the event handler. This will probably work but will add complexity when I step back in the wizard and need to re-subscribe to the events again.

The solution is to implement Microsoft.Practices.Prism.IActiveAware in my View Model.
public bool IsActive
{
get { return _isActive; }
set
{
if (_isActive != value)
{
_isActive = value;
DoActiveChangedWork(value);
}
}
}
public event EventHandler IsActiveChanged;
It's also possible to implement this in the View but it's not a requirement.

If you are talking about Prism EventAggregator - you can set keepSubscriberReferenceAlive parameter to false so it will use a weak reference under the hood so everything will be "unsubscribed" automatically by GC when an object will "die".
fundAddedEvent.Subscribe(FundAddedEventHandler, ThreadOption.UIThread, true);
otherwise you have to do it yourself by explicitly unsubscribing:
fundAddedEvent.Unsubscribe(FundAddedEventHandler);

Related

MVVM Light: how to send a parameter to ViewModel (when opening new window)?

I am trying to learn MVVM with MVVM Light Toolkit in WPF. But I am stuck on one simple problem.
I have an AddEditProfileWindow which basically has a textbox for profile name and a confirm button. It adds new profile to database table or updates name of existing profile.
In MainWindow/MainViewModel I have a list of profiles and two buttons: "Add Profile" and "Edit Selected Profile". They both open this window via commands + messages. For example here is command for the "Add Profile" button
public RelayCommand OpenAddProfileWindowCommand
{
get
{
return _openAddProfileWindowCommand ?? (_openAddProfileWindowCommand = new RelayCommand(
() => { Messenger.Default.Send(new NotificationMessage("OpenAddProfile")); }));
}
}
and it's receiver in MainWindow code behind
private void MessageReceived(NotificationMessage msg)
{
if (msg.Notification == "OpenAddProfile")
{
var window = new AddEditProfileWindow();
window.Owner = this;
window.ShowDialog();
}
}
So the problem is that I need to somehow pass a parameter to the AddEdit... Window/ViewModel (set IsEditing bool property in ViewModel for example) to change window behavior and customize it a bit (change title and the confirm button text to "Add" or "Update"). Also for updating I need Profile object (or at least Id) of selected record.
For creating ViewModels I use ViewModelLocator and Unity
public ViewModelLocator()
{
var container = new UnityContainer();
ServiceLocator.SetLocatorProvider(() => new UnityServiceLocator(container));
container.RegisterType<MainViewModel>(new ContainerControlledLifetimeManager()); // singleton
container.RegisterType<AddEditProfileViewModel>();
}
public AddEditProfileViewModel AddEditProfile
{
get
{ return ServiceLocator.Current.GetInstance<AddEditProfileViewModel>(); }
}
I have read a lot of similar threads and examples but still don't understand how should I pass parameters to view models. Some answers suggest creating view models on app startup (and make them singletons) in the ViewModelLocator and then I can send message before opening. But looks like not very clean and also I will need to reset view models before opening (via Cleanup() probably).
Is there any better/easier/cleaner approach?
In my opinion, Messenger and getting AddEditProfileViewModel from IoC are not suitable in this scenario. First you send message from a UI's DataContext to UI. Messenger works between loosely coupled components and usually on the same level, view model and view model for example. If you want view model to notify view, you can use InteractionRequest from Prism. Second, AddEditProfileViewModel can be considered as a temporary, based on its view is a modal dialog, so its creation might depend on the environment that creates it.
One approach, using shared service, maybe called IDialogService, which has a method might called ShowAddEditDialog. Your main view model gets this service from IoC and calls it when executing command, add/edit. When calling the method, main view model also creates AddEditProfileViewModel and passing states, such as add/edit, existing profile, etc.
Another approach, using application controller, if you still want to keep Messenger and IoC. You still can use Messenger here but it is not the view who listens to messages, instead it is an application controller. Now, application controller, main view model, AddEditProfileViewModel and AddEdit window are all resolved from IoC container. The application controller holds both view models and listen to the message. When it got message from main view model, it updates states on AddEditProfileViewModel, resolve dialog, set DataContext and show the dialog. You can put the application controller instance in MainWindow code behind or anywhere since once it gets resolved from IoC, it is autonomous.

Silverlight/Windows Phone ViewModel updating question

I have a XAML page whose DataContext is set to my ViewModel. A switch control on the page is bound to the following code in the ViewModel:
public bool TeamLiveTileEnabled
{
get
{
return Data.Subscriptions.Any(s => s.TeamName == this.Team.Name);
}
}
When this page is initialized, Data.Subscriptions is an empty list. I retrieve the list of subscriptions through an async web service call, so it comes back after the getter above is called.
When the web service call comes back, Data.Subscriptions has items added to it, and I'd like the UI to update based on the new result of the LINQ expression. Right now nothing happens, and I confirmed that Data.Subscriptions contains items that satisfy the condition above.
Data.Subscriptions is an ObservableCollection of Subscription items.
Can someone point me to what to do? Thanks!
The problem is that your ViewModel is not aware of any changes to the ObservableCollection. Within the ViewModel, subscribe to the CollectionChanged event of Data.Subscriptions.
Data.Subscriptions.CollectionChanged += SubscriptionsChangedHandler;
Within the event handler notify listeners of TeamLiveTileEnabled by sending a PropertyChanged notification
NotifyPropertyChanged( "TeamLiveTileEnabled" );

Pass Single Instance of Model to Multiple View Models in same Module for different views

I am working on a project using PRISM where I have left navigation implemented as Tree View and any click event happens raise event using event aggergation to Enrolment Module which has multiple view model for multiple views (like Wizard Applicaiton where you can go through many views to collect data). I want to have a common or shared or singleton model which can be passed across this view models and save at the end.... users can click on any link any navigation at any time and it should save data in to this singleton model expsosed through different view model. Do you have any samples which are doing something like this... or can you type up a quick one on how to do it? OR it is not possible to do it at all. I am following all patterns from Brian Lagunas's Pluralsight Video for PRISM so try to use that way....
I would have a MasterViewModel which controls the "wizard" pages and current state
It would contain the following properties:
List<ViewModelBase> Pages
int CurrentPageIndex
ViewModelBase CurrentPage, which returns Pages[CurrentPageIndex]
MyClass DataObject
The MasterView that goes with the MasterViewModel would be nothing more than a ContentControl with it's Content bound to CurrentPage. I would probably also define DataTemplates in the MasterView which tells WPF which View to draw with which Page
Your MasterViewModel would be in charge of handling the pages, and passing each page a reference to the data it needs. For example in the constructor it might say,
public MasterViewModel(MyClass dataObject)
{
DataObject = dataObject;
Pages.Add(new InfoPage(DataObject));
Pages.Add(new AddressPage(DataObject.Addresses));
Pages.Add(new PhonePage(DataObject.Phones));
Pages.Add(new SaveMyClassPage(DataObject));
CurrentPageIndex = 0;
}
I have an example here if you're interested
I don't know, is it prism way, or something another, when I build something like wizard, first of all I create instance of all data which wizard collect.
public WizardData wd = new WizardData();
Then, every page of wizard are initialized by this wd instance, i.e.
public FirstWizardPage(WizardData wd)
{
this.wizardData = wd;
}
So, this way allow you to have button Finish on every page, for example. You can initialize your ViewModel with wd, or its properties.
This way is not the best. Its hust one of the possible way.
Another - is to create singleton and use it without reference passing from page-to-page.
When you use Prism you also have a Dependency Injection Container, usually Unity or MEF. To solve your problem you can register your model as singleton to those DI containers. Every view model that asks the DI container to resolve their dependecy, in our special case the model, will get the singleton instance back from the DI container.
Unity example: You register your model as singleton instance:
public void Initialize( )
{
container.RegisterInstance<Model>(new Model(), new ContainerControlledLifetimeManager( ));
}
Now you can resolve your dependencies in your view model:
public ViewModel(IUnityContainer container)
{
Model model = container.Resolve<Model>();
}

MVVMLight Message firing multiple times in WP7 app

I am new to MVVMLight and have started to use it in my WP7 app. I have a View/page which registers for MessageDialogs and then my VM sends the message to show it. This works great. However, when you go back to the previous screen (with WP7 back button) and then enter the page again (using AppBar menu item) then the message fires twice (and increments every time you view the page). I assume it the View is registering every time and old versions are subscribing to the message, but I am not sure of how it should work.
I tried to call VM.Cleanup in my NavigatedFrom event to ensure the old messages are unregistered when they leave the page, but this did not help. Here is my code:
View:
public AboutPage()
{
InitializeComponent();
Messenger.Default.Register<DialogMessage>(this, msg => { var result = MessageBox.Show(msg.Content, msg.Caption, msg.Button); });
}
protected override void OnNavigatedFrom(NavigationEventArgs args)
{
ViewModelLocator.AboutViewModelStatic.Cleanup();
base.OnNavigatedFrom(args);
}
AboutViewModel: (Code gets fired by a command)
var message = new DialogMessage("Why does this fire multiple times?", DialogMessageCallback) { Button = MessageBoxButton.OK, Caption = "" };
Messenger.Default.Send(message);
That's all there is too it, but each time you come to this page it fires once more... I assume it is something to do with Cleanup but I am not sure how it is supposed to work in WP7... any tips appreciated...
A view is created & destroyed as you navigate through the application. Therefore, in your AboutPage view's constructor, the view is registering for the message every time it is created.
A better approach is to setup the registration in the ViewModel's constructor, use a ViewModelLocator and databind the View to the ViewModel. The ViewModel is created once and used throughout the lifetime of the application. Jonas Follesoe's FlightsNorway is good WP7 application to learn about MVVMLight, you can find the MVVMLight Messenger class being used very nicely.
HTH, indyfromoz

Update observable collection by requerying or adding to collection?

I have a observable collection exposed as a property within a view model. The observable collection is loaded with objects from a data access layer (linq2sql).
When a new item is added to the database through another view model what is the best way to update the observable collection? Should I repopulate the observable collection with a query to the database or directly insert the new object into the collection?
Also I am still trying to work out how to get one view model to communicate with another one, but I've only been using mvvm for 3 days.
You can use a whole new class to manage notifications from one class to another. For the question regarding whether to load all entities or just add newly added entity, it really depends on the number of possible entities to load each time. If they are going to be very few, you can reload them each time, otherwise just add the newly added object to the collection.
Example:
class ViewModel1
{
ObservableCollection<Object> entities;
public ViewModel1()
{
EventsManager.ObjectAddedEvent += new EventHandler<ObjectAddedEventArgs>(EventsManager_ObjectAddedEvent);
entities = new ObservableCollection<Object>();
}
void EventsManager_ObjectAddedEvent(object sender, ObjectAddedEventArgs e)
{
entities.Add(e.ObjectAdded);
}
}
class EventsManager
{
public static event EventHandler<ObjectAddedEventArgs> ObjectAddedEvent;
public static void RaiseObjectAddedEvent(Object objectAdded)
{
EventHandler<ObjectAddedEventArgs> temp = ObjectAddedEvent;
if (temp != null)
{
temp(null, new ObjectAddedEventArgs(objectAdded));
}
}
}
class ObjectAddedEventArgs : EventArgs
{
public Object ObjectAdded { get; protected set; }
public ObjectAddedEventArgs(Object objectAdded)
{
ObjectAdded = objectAdded;
}
}
class ViewModel2
{
public void AddObject(Object o)
{
EventsManager.RaiseObjectAddedEvent(o);
}
}
Whenever a model object is saved into the database, send a message to the viewmodels including the saved model object. This could be achieved using the Messenger helper class in MVVM Light Toolkit
As for the second part (how to teach view models talk to each other) I prefer to keep view models as decoupled as possible. Thus Event Aggregation or some kind of Message Broker seems to be natural choice.
The first part of the question is bit trickier, and I don't know correct answer. If Observable collection contains thousands of items I would try to choose an approach that doesn't involve complete reconstruction. Otherwise try the simplest and easiest solution you can come up with.
I use libraries I created that effectively allow one DataContext to save changes back to a parent DataContext and for a parent DataContext to notify its children that it just received changes.
With this functionality, I create a single master DataContext for my whole application, then for any modal windows with Ok and Cancel buttons or other parts of the UI that temporarily need their own "view of reality" I create child DataContexts. When the child DataContext writes back to the parent this causes all controls bound to objects in the parent to update and for the parent to broadcast the changes to all children so they can update too (or not, if they are in snapshot mode).
This solution took a while to code but works beautifully. I also use exactly the same mechanism to send changes to a parent DataContext on the server which is shared by other clients, so everyone has up-to-date data and giving me great caching performance. I even use it for communicating with my back-end data store.

Resources