WPF Memory Leak in Command Binding
In a medium sized project I'm working on at the moment, we've had a rather large memory leak issue. We've implemented our tiers in a pattern called MVVM. I wasn't sure on how well this would work but to be honest, its been an amazing pattern to follow and pretty easy to get right.
Our UI exclusively does binding, I.e. there is no real code behind. Our buttons are bound to commands, ItemsControls bound to ObservableCollection<T> and inputs bound to properties on our ViewModels. Pretty simple stuff and extremely powerful. During testing of the application, switching in between items in a ItemsControl, the RAM usage would go up. And up. And up. Until we hit 800MB of RAM and the system would start thrashing and be unresponsive. After further investigation, it was down to buttons that were binding to ICommands. As the button was in a template in the ItemsControl, each time we changed the selected Item, the datatemplate was being instanciated and therefore a new button instance was being created inside the framework.
1: <ItemsControl ItemsSource="{Binding Property=DataToBindTo}" >
2: <ItemsControl.ItemTemplate>
3: <DataTemplate>
4: <Button Command="{Binding Property=RunCommand}" />
5: </DataTemplate>
6: </ItemsControl.ItemTemplate>
7: </ItemsControl>
Above is a very simple example of the scenario I have. In the Button Control which extends ButtonBase, whenever the Command's DependencyProperty is changed, a callback is fired which is supposed to remove any handlers from the command's CanExecute EventHandler. However, the code to Unhook these event handlers NEVER seems to run, so the Button is therefore NEVER released and stays in memory.
1: private void OnCommandChanged(ICommand oldCommand, ICommand newCommand)
2: {
3: if (oldCommand != null)
4: {
5: this.UnhookCommand(oldCommand);
6: }
7: if (newCommand != null)
8: {
9: this.HookCommand(newCommand);
10: }
11: }
The code above is from the WPF code base inside PresentationFramework.dll. In researching this bug on the Internet, a few posts mentioned clearing the command bindings. This would set the Command in the Button to null, which in turn, the OnCommandChanged method would be called. Line 3 and 5 are important here, since the oldCommand is not null, it will be unhooked. However, because the UI has no code behind, we can't clear the bindings in code without some serious hacking.
To get round this bug, we ended up implementing our own Button object, extending ContentControl and writing our own keyboard and mouse events to handle executing the command. Please note, this is on 3.5 without the SP1 beta installed so things may change when this goes RTM.
