ICommand binding causing UI memory leak in WPF application - c#

I'm building a WPF app that connects to a SQL Server database using LINQ to SQL.
The main window of the app contains a ListView containing a series of detail views.
The ItemSource of the ListView is bound to a collection of detail view model objects exposed as a property on the root view model.
Each detail view model object composes several ICommand properties as well as a property exposing a detail model object, which in turn exposes the various data fields shown in the UI.
Analysis with the ANTS memory profiler shows that the objects being leaked are those contained in the detail model object, and some UI classes to which they are bound.
Instances of these objects from previous refreshes are not being garbage collected.
ANTS has a tool that allows the user to trace chains of reference to identify why unwanted memory is being retained. When I use it, I find that all of the chains that show up have an ICommand in them. Accordingly, I've removed the offending ICommand, and found that
the memory leak disappears.
Unfortunately, I need the ICommand to implement some important functionality. What is really confusing me is how it has a reference to the detail model object in the first place- they are two completely separate instance variables in the detail view model object.
Here is the constructor of the detail view model object (The reference to the RootViewModel is used for callbacks in some of the methods connected to the ICommands. I originally suspected that this might be causing a circular chain of references that might be the cause of the problem, but removing it doesn't seem to have any effect.)
public CarDataViewModel(CarData carDataItem, RootViewModel parentViewModel)
{
_parentViewModel = parentViewModel;
CarDataModel = carDataItem;
CompetingCheckboxStatus = CarDataModel.CurrentCar.Competing;
AcknowledgeAlarm = new ParameterlessCommand(AcknowledgeAlarmClicked);
Acknowledge = new ParameterlessCommand(AcknowledgeClicked);
ShowReport = new ParameterlessCommand(ShowReportClicked);
Cancel = new ParameterlessCommand(CancelClicked);
}
Here's the xaml where the bindings are set up - AcknowledgeAlarm is the ICommand, CarDataModel is the detail model object:
<ListView x:Name="itemGridView"Grid.Row="1"ScrollViewer.HorizontalScrollBarVisibility="Disabled" ItemsSource="{Binding CarDataViewModels}" IsSynchronizedWithCurrentItem="True" Margin="0,0,0,0">
<ListView.ItemTemplate>
<DataTemplate>
</DataTemplate.Resources>
<Button Command="{Binding AcknowledgeAlarm}">
<Border DataContext="{Binding CarDataModel}" BorderBrush="{StaticResource GrayFadeBrush}" Background="White" BorderThickness="5">
<Grid> . . .

The CanExecuteChanged event handler is likely implicated in the leak.
WPF expects ICommand implementations to use weak references to the event handlers. You're using a normal .NET event which uses strong references, which can cause this leak.
The way you are creating the ParameterlessCommand instance seems to imply that CanExecute will always be true, and you don't need the event at all.
Are you actually firing the event anywhere, or is OnCanExecuteChanged unused code?
If not, replace the event definition with:
public event EventHandler CanExecuteChanged { add {} remove {} }
This way the event does not store any handlers, and the view model avoids having a strong reference to the UI elements.
If you need to raise the event, the easiest solution is to use CommandManager.RequerySuggested, which matches the weak event semantics expected for ICommand:
public event EventHandler CanExecuteChanged {
add {
CommandManager.RequerySuggested += value;
}
remove {
CommandManager.RequerySuggested -= value;
}
}
Another thing you should do is implement INotifyPropertyChanged in your view model (if you haven't done so already), and use that instead of having individual NameChanged etc. events for each property.
This is because the logic in WPF dealing with the individual properties causes memory leaks when there is a reference from the view model back to the UI elements: http://support.microsoft.com/kb/938416
AFAIK you need to implement INotifyPropertyChanged even if you don't actually have any change events.
My guess is that fixing either of these two problems will make the leak disappear: the incorrectly implemented CanExecuteChanged causes a strong reference from view model to view, which is exactly the circumstance under which the lack of INotifyPropertyChanged causes a leak.
But it's a good idea to fix both issues; not just one of them.

Related

XAML x:bind oneTime on initially null property still works. Why?

x:Bind defaults to OneTime, which updates the target UI with the data when the Page's Loading event triggers the generated code's Initialize function.
I have a Page with a ViewModel property. This ViewModel class implements INPC for its properties. The data for the viewModel is loaded asynchronously, only after the page is loaded. So on Page initialization, and subsequently the generated code initialization, the UI target using x:Bind will have null data.
Since it is OneTime, it shouldn't change unless I manually call Update(which I don't).
So why does my x:Bind UI work?
The following is some simplified code snippets.
<Page x:Name="MyPage" x:Class="MyProject.Pages.MyPage">
<Button Command="{x:Bind ViewModel.GoToAnotherPageCommand}">
public sealed partial class MyPage : Page
{
public MyPageViewModel ViewModel { get; set; }
public MyPage()
{
this.InitializeComponent();
}
// called by an event bound to a Frame's Navigated, which all pages use
public void OnNavigatedTo()
{
this.ViewModel = new MyPageViewModel();
}
}
public class MyPageViewModel : INotifyPropertyChanged, INotifyPropertyChanging
{
// GoToAnotherPageCommand is an INPC property and its set in the constructor
The reason that your command works fine is because OnNavigatedTo will be called before the command instantiation. This means by the time the code tries to set the command, the ViewModel has already been instantiated and is no longer null.
To prove my point, first go open the file under the following path(could be ARM or *x64 depending on which platform you are running on) -
obj/x86/Debug/MyPage.g.cs
This is basically the code-generated file that hooks up all the x:Bind stuff for your page.
Now put a breakpoint at where the command is set. In my case, it's a method called Set_Windows_UI_Xaml_Controls_Primitives_ButtonBase_Command. Then put another breakpoint at OnNavigatedTo.
Now run the app, you will see that the OnNavigatedTo method gets called first.
If your page's NavigationCacheMode is set to Disabled, this behavior makes OnNavigatedTo the ideal place to instantiate x:Bind bindings so the page only uses memory to create these new objects when the user actually navigates to it, instead of doing everything inside the page constructor.
Don't do this inside the Loaded event of the Page though. Because it will get called after the command instantiation. You can try the following code to instantiate the ViewModel, and the result is very different(your command will not work).
public MyPage()
{
InitializeComponent();
Loaded += (s, e) => ViewModel = new MyPageViewModel();
}
The compiled binding system (x:Bind) is smart enough to check for initial null values and not consider them the actual value you wish to bind. It will wait for the first non-null value and bind that value.
This is by design, as binding to an initial null value is (almost) never the intention of the binding.
I didn't find the source of this information, but I believe it was in the Build talk detailing the x:Bind system in 2015.
Updated:
As Justin mentions in the comments below and in his own answer, the binding will not work if the view model is set after the binding operation happens.
I believe this is because the binding terminates when it encounter a null reference in the property chain, but I haven't tested this, so I might be incorrect.

Raise CanExecuteChanged when the model changes

In my ViewModel I have an ObservableCollection of Person objects (that implement INotifyPropertyChanged) and a SelectedPerson property. These are bound to a ListBox in my view.
There is also the following Prism DelegateCommand in my ViewModel:
Private DelegateCommand _myCommand = New DelegateCommand(CanExecute)
Public DelegateCommand MyCommand {get {return _myCommand;}}
Private Bool CanExecute()
{
Return (SelectedPerson.Age > 40);
}
What is the most elegant way of calling MyCommand.RaiseCanExecuteChanged whenever the SelectedPerson changes and whenever the SelectedPerson's age changes?
Adding and removing property changed handlers in the SelectedPerson's setter seems a bit messy to me.
Adding and removing property changed handlers in the SelectedPerson's setter seems a bit messy to me.
That's how I do it, and I'm not sure what a cleaner alternative would be. If the command state depends on a sub-property, you need to observe the changes somehow. Be careful about unsubscribing, though, or you risk a memory leak if your Person outlives your view model. PropertyChangedEventManager and weak event handlers can help if you can't guarantee that you unsubscribe.
To keep things clean, I usually just have one handler that listens for any sub-property changes, which calls a RequeryCommands method (also called directly by view model methods), which in turn invokes RaiseCanExecuteChanged for all the commands in my view.

Direct calls from View to ViewModel. Can DataContext be changed on itself?

Sometimes, i need to do some calls from View to VM. I know, that it is not MVVM style, but still. Should i always re-check DataContext to be my VM (in case it can be changed by re-activation from tombstoning or something like that), or it is enough to store in once?
var vm = DataContext as MyViewModel;
if (vm == null) return;
vm.DoSomething();
DataContext is set via Mvvm Light Locator
DataContext="{Binding MyViewModel, Mode=OneWay, Source={StaticResource ViewModelLocator}}"
It always is safe, when it's limited to a page. Whether in constructor, Loaded event handler or OnNavigatedTo, you can create objects and never check them again - either the page was kept in memory with all the objects, or the constructor, Loaded and OnNavigatedTo were called again upon re-activating from tombstoning. Problems with tombstoning occur mostly in cases of one page relying on the fact, that other page created something.
I use that sometimes, like that:
private MyViewModel viewModel;
When is the page loaded, then add value to that field
viewModel = (MyViewModel)DataContext;
Than I can use it anytime in my view after it.
I know it is not MVVM, but still ;)

If a model implements INotifyPropertyChanged, how should ViewModel register/deregister for PropertyChanged event?

I have a Model which implements INotifyPropertyChanged and it may get updated by a background business thread. Its related ViewModel also implements INotifyPropertyChanged. And their View obviously binds to ViewModel. This View may be shown on multiple places, and my goal is that all of them get updated when the model changes.
I know that ViewModel should register for PropertyChanged event of Model. But I don't know when and where is the best place for this registering and deregistering. Specially about the deregistering, since I'm scared of having hundreds of VM event handlers on the Model for the VM/views that are not shown anymore.
Thanks in advance.
Is it an absolute necessity for you to limit the View not directly bind to the Model?
You can expose the Model as a property on the VM and then have your View directly bind to it thereby not having the VM subscribe to INPC from Model
something like:
public class MyViewModel: INotifyPropertyChanged {
...
private MyModel _model;
public MyModel Model {
get {
return _model;
}
set {
if (value == _model)
return;
value = _model;
RaisePropertyChanged(() => Model);
}
}
...
}
and in xaml (when MyViewModel is the DataContext):
<TextBlock Text="{Binding Model.ModelProperty}" />
Update:
Maybe this is of some help for tapping into the PropertyChanged events of Models in a "weak" fashion
IWeakEventListener
Using the central event dispatching of a WeakEventManager enables the handlers for listeners to be garbage collected (or manually purged) even if the source object lifetime extends beyond the listeners.
which is used in
Josh Smith's PropertyObserver
This should hopefully solve your issue of needing to explicitly un-register?
I've gotten around this issue by hooking in to model events on load and removing them on unloaded, the problem here is that the view model can miss events if it's off the screen. I usually just refresh the data quickly on load.
OnLoad - Refresh the VM data from the model and hook events.
OnUnLoad - remove any hooks that you've put in place.

Strategy, State, Singleton... or a Composition thereof?

I am new on stackoverflow and relatively new to WPF.
I've wrapped my head around a half dozen weighty tomes of Patterns and Best Practices (as well as numerous posts here) but cannot seem to find the solution I am looking for.
My Problem: WPF / .Net 4 / C#
I have a text processor (of type Editor E) that can load one Document (of type Document D) at a time (strored as Editor.CurrentDocument). Several UI controls bind to the Document's properties (all Dependency Properties) such as Document.Title, Document.DateLastModification.
Now I want to be able to switch the actual Document instance without having to unhook and re-hook all event handlers. So I guess the Editor.CurrentDocument property must somehow remain its instance while switching its implementation.
I have tried to create a SingleInstanceDocument class that inherits directly from Document and uses the Singleton pattern. But then I cannot find a way to inject any Document instance into the SingleInstanceDocument without having to internally re-map all properties.
Am I somehow being misguided or missing the point here? If the SingleInstanceDocument approach is a viable solution, is there any way I can use reflection to re-map all available dependency properties from the inner Document to the outer SingleInstanceDocument shell automatically?
Thank you very much!
Addendum:
It turned out that the functionality required here was already provided by WPF/.NET out of the box by implementing INotifyPropertyChanged on the CurrentDocument host object. Thus changing the current document caused the UI to update its bound controls appropriately. I'm sorry for all the confusion.
first, learn some basic MVVM pattern. basically in WPF-MVVM just use ObservableCollection and INotifyPropertyChanged interface.
this type of collection implements the observer pattern that notify update to UI(View) when you add/remove or "select" the current item.
//in main ViewModel
private Ducument _currentDocument;
public Document CurrentDocument
{
get { return _currentDocument; }
set
{
_currentDocument = value;
NotifyPropertyChanged("CurrentDocument");
}
}
//stored all loaded documents as collection.
public ObservableCollection<Document> Documents { get; set; }
binding selected - current item.
<ListBox ItemsSource="{Binding Path=Documents}" SelectedItem="{Binding Path=CurrentDocument}" DisplayMemberPath="Title">
<!-- //all Document.Title as listitem -->
</ListBox>
<!--// Editor's View -->
<ContentControl DataContext="{Binding Path=CurrentDocument}"></ContentControl>

Categories