WPF MVVM: View is not reloaded when ViewModel is changed - wpf

I have a WPF application based on the MVVM pattern. It has a tree on the left side and a details area on the right side. The tree contains objects of various types and I have a view model for each type. In the view, I have a different data template (containing a user control) for each view model type. The view is then selected via databinding based on the current details view.
Now, when I switch between tree nodes, I also instantiate a new details view model for each node and the view gets changed accordingly, firing both the DataContextChanged event and the Loaded event. That is, until I start to switch between objects of the same type. Here, too, the details view model gets updated, but the view instance stays the same. This means, only the DataContextChanged event is fired, but not the Loaded event.
Is there a way to force the Loaded event to fire, for instance, by re-initializing the view?
The reason why I need the Loaded event is that WPF fires the selection change events on input controls during data binding and since I have logic that acts upon user-triggered selection change events, I need to be able to ignore those triggered by data binding. For now, the Loaded event seems to be the best option to do this. Escept for the described issue.
Thanks, Michael

Move whatever logic you have in the Loaded handler to the DataContextChanged handler if it needs to run every time the data context changes.

Related

DataGrid fails to commit edits upon view unload

My main viewmodel encapsulates a set of workspace viewmodels only one of which is exposed at any given time via a CurrentWorkspace property on the main view model. The user gets to switch between workspaces through a series of radio buttons so that when a radio button is clicked, the value of the CurrentWorkspace is replaced with a new workspace viewmodel.
Each workspace has a corresponding view (datatemplate) so that as workspaces are switched, old view unloads and a new view loads in keeping with the value of CurrentWorkspace property. Now each view has a datagrid that could still be in edit mode when the user may choose to click a radio button forcing view unload that dismantles its visual tree including the datagrid. The problem is that the last pending row edit is not committed to the underlying view model when this happens.
I tried to remedy the situation by trying to handle DataGrid LostFocus, LostKeyboardFocus, Unload events as well as the UnloadingRow event but none of them seem to offer a handle to the issue. It seems that once the view unload is triggered, these events either do not fire or fire too late for me to invoke a commit.
I would appreciate any help or pointer to where I should look to resolve this issue.
The problem that I noted above was only observed when the user clicked RadioButton triggering view unload. However, there are other buttons in the main view that also trigger workspace unload (such as Quit button) but those surprisingly did not produce the same bug.
On close scrutiny I found, that radio buttons had by design set their attribute Focusable=False. Undoing it solves my problem and the datagrid now commits edits properly when the view unloads. As I noted above, DataGrid/DataGridRow LostFocus events were not firing as expected and now I believe I found the reason.
So I guess DataGrid/DataGridRow rely on their LostFocus events to trigger commits and its important that this event be not suppressed in any manner. I still wish though if wpf controls had an "Unloading" event as a fall back option to handle problems created by design decisions like the one in my case.

How to make a contained control in a view to do operation by ViewModel triger?

I have a view that containing a user control.
I want to make the view model to notify the user control to do some action (refresh its' data).
I can pace some bool property in my VM and bind it to the user control so it will trigger it, but I think it's a little abuse of the property.
I feel I missing something, and can't find a solution. I will appreciate any comment.
My solution:
I'm going to solve the problem by registering an event of collection changed in my UserControl, since I'm binding to that control a view of a collection thru CollectionViewSource.
My original problem was how to make a chart control inside the UserControl to get updated when I filtering the data source. After the filtering operation from the VM, an event will raise and I will make the chart to get refresh either in the UserContol's code behind
Since you've indicated MVVM tag solution would be pretty straightforward - just bind control to a data by exposing data items/whatever by ViewModel so any data updates would be automatically dispatched to View via bndings if you would provide INotifyPropertyChanged / INotifyCollectionChanged by a ViewModel.
If you've bound your UserControl correctly, you shouldn't need to manually refresh the data since WPF will automatically update the UI when INotifiyPropertyChanged triggers
That said, if you wish to send a message from the ViewModel to the View, I usually use an messaging system of some kind to keep the Views and ViewModels separate. The two I've used and would recommend are MVVM Light's Messenger, or Microsoft Prism's EventAggregator
Either system will allow your ViewModel to broadcast a message, and your View can subscribe to receive these messages and handle them as needed.

Triggering a command in the view model based on an action in the view

I am working with the Infragistics XamDatagrid. When an event takes place in the view (new record updated), I would like to call a method in the view model. I can't just listen to the CollectionChanged event of the source in the VM because that event gets triggered once the user begins editing the new row in the grid. The RecordUpdating event gets called when the user finished the edits.
What I am doing now is accessing the DataContext directly and calling the method. The problem is that there is nowhere I can find to attach a Command to the gird that would tigger on the event I am interested, nothing like the Command of a Button.
Generally, what is the best way to call into a VM from a view when there is no Command parameter I can set in the view?
Code-behind glue is fine in most cases, but if you find yourself gluing the same type of thing repeatedly, you can look into Attached Behaviors. Here's one old-school approach the introduces the concepts, but doesn't use the new Behavior class: http://www.codeproject.com/KB/WPF/AttachedBehaviors.aspx
Here is an example using the new Behavior class, including a demonstration of how it integrates with Blend: http://geekswithblogs.net/lbugnion/archive/2009/04/05/using-a-behavior-to-magnify-your-wpf-applications.aspx

WinForms databinding with a view model - slow to display view model changes

I have the following configuration...
Single form with approximately 50-60 controls (it's a lot, I know) and a single view model which is responsible for storing the state of the form. The view model properties are bound to the editable controls on the form via a BindingSource.
My problem is this... I want to be able to clear the form with a button. To clear the form, I have tried the following:
Assigning the DataSource of the BindingSource to null and reassigning a new view model. This ends up lagging terribly.
Setting all of the properties to null/empty in the view model and then firing the OnPropertyChanged event (my view model base class is implementing INotifyPropertyChanged). This too lags terribly, and it would, given that I'm clearing 60 fields and then firing an OnPropertyChanged event for each one.
So, is there a way I can modify the application so that it is able to clear the view model and reflect the changes on the form in timely fashion? My form requires all 50-60 controls to be on the same form, but the controls could be divided up into four logical groups. Would it be beneficial for me to break up the view model into four presenters/view models that could handle the logical groups independently? Would this impact performance and the time it takes to reflect the changes in the form?
Thanks!

At what stage in the life-cycle of Silverlight UIElements do existing Bindings get removed?

I'm trying to work out why some of my Silverlight controls are staying in memory. I've noticed that when I navigate away from a page with the controls on, there remain referential links to the view model. These links are a result of left-over subscriptions to the ErrorsChanged event (my view model implements INotifyDataError) created when Bindings are created between the Page and the view model. At some point some but not all of the Bindings are removed and I can't work out why this isn't happening.
At what point in the life-cycle of Silverlight UIElements do existing Bindings get removed?
I think I now know the answer to this. Please correct me if I'm wrong. Bindings are not removed. Instead referential links are made (e.g. INotifyDataError events are wired up) as a result of a particular instantiation of a Binding. In the simplest case that's when the DataContext changes.
Let's say you set the DataContext of a Page to a new, different INotifyDataError object as you load it. If there are Bindings on your Page (to the DataContext) the Page is not a candidate for garbage collection until the DataContext object is destroyed. That's because the DataContext holds a reference to the Page through its ErrorsChanged event. If you want the DataContext object to be collected you'll have to set the DataContext to null in the Page's Unloaded event.
As far as I understand it, it seems the proper pattern for implementing a DataContext (that is different from the Page) is setting the DataContext in the Loaded event on the page, and then setting the DataContext to null as the page fires its Unloaded event. This concept applies any Framework element.
As an aside, I haven't delved into element Bindings. For instance, when one element on a Page is bound to a property on another element. I'm not sure when the referential links between these objects are removed. I'm presuming it's when the page is unloaded. Anyone know the answer to this?

Resources