Thus far, my testing has shown that all standard approaches, examples, and frameworks leveraging the MVVM pattern in silverlight suffer from a huge problem: massive memory leaks which prevent VMs from being garbage collected.
Obviously this is a huge and ridiculous claim - so my expectation is that someone will have an obvious answer of why and where I'm going wrong :)
The steps to reproduce are simple:
Bind your viewmodel to a view by setting the views datacontext to the VM (assume the viewmodel leverages INotifyPropertyChanged to support data binding)
Bind a UI element to a property on the viewmodel, for example:
<TextBox Text="{Binding SomeText}" />
Leverage the binding in some way (for example - just type in the textbox).
This creates a reference chain that extends from the root, to a BindingExpression, to your viewmodel. You can then remove the View from your UI tree, as well as all refernences to the VM - however the VM will never be garbage collected thanks to the root<>BindingExpression<>VM reference chain.
I have created two examples illustrating the problem. They have a button to create a new view/viewmodel (which should dump all references to the old one(s)) and a button which forces garbage collection and reports on the current memory usage.
Example 1 is a super stripped down caliburn micro example. Example 2 uses no frameworks and just illustrates the problem in the simplest way I could think of.
Example 1
Example 2
For those who might like to help but don't wish to download the example projects, here is the code for example 2. We start with a viewmodel called FooViewModel:
public class FooViewModel : INotifyPropertyChanged
{
string _fooText;
public string FooText
{
get { return _fooText; }
set
{
_fooText = value;
NotifyPropertyChanged("FooText");
}
}
private byte[] _data;
public FooViewModel()
{
_data = new byte[10485760]; //use up 10mb of memory
}
private void NotifyPropertyChanged(String info)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(info));
}
}
public event PropertyChangedEventHandler PropertyChanged;
}
It simply exposes a string property called FooText which we will bind too. INotifyPropertyChanged is neccessary to facilitate the binding.
Then we have a view called FooView which is a usercontrol containing:
<UserControl x:Class="MVVMLeak.FooView">
<StackPanel x:Name="LayoutRoot" Orientation="Horizontal">
<TextBlock Text="Bound textbox: " />
<TextBox Text="{Binding FooText}" Width="100"/>
</StackPanel>
</UserControl>
(namespaces omitted for brevity)
The important bit here is the textbox which is bound to the FooText property. Of course we need to set the datacontext, which I've chosen to do in the codebehind rather than introduce a ViewModelLocator:
public partial class FooView : UserControl
{
public FooView()
{
InitializeComponent();
this.DataContext = new FooViewModel();
}
}
MainPage looks like this:
<StackPanel x:Name="LayoutRoot" Background="White">
<Button Click="Button_Click" Content="Click for new FooView"/>
<Button Click="Button2_Click" Content="Click to garbage collect"/>
<ContentControl x:Name="myContent"></ContentControl>
</StackPanel>
with the following in the code behind:
private void Button_Click(object sender, RoutedEventArgs e)
{
myContent.Content = new FooView();
}
private void Button2_Click(object sender, RoutedEventArgs e)
{
MessageBox.Show("Memory in use after collection: " + (GC.GetTotalMemory(true) / 1024 / 1024).ToString() + "MB");
}
Note: To replicate the problem, be sure to type something in the textbox, as I believe the Binding Expression isn't created until it's needed.
It's worth noting that this KB article may be related, however I'm not convinced since 'method 2' workaround doesn't seem to have an effect, and the reference chain doesn't seem to match.
Also, I'm not sure it matters, but I used CLR Profiler to diagnose the cause.
Update:
If anyone would like to test, and report their findings in a comment, I'm hosting the silverlight application via dropbox here: Hosted Example . To reproduce: Hit the top botton, type something, hit the top button, type something, hit the top button. Then hit the button. If it reports 10MB usage (or perhaps some other amount that is not increasing), you are not experiencing the memory leak.
Thus far, the problem seems to be happening on ALL of our development machines, which are ThinkPad w510 (43192RU) with 12GB Ram, 64 bit Win 7 Enterprise. This includes some which have not had development tools installed. It might be worth noting that they are running VMWare workstation.
The problem does NOT seem to happen on other machines I have tried - including several home PCs and other PCs in the office. We have somewhat ruled out SL versions, amount of memory, and probably vmware. Still haven't nailed down a cause.
A solution is yet to be found, however the problem is now identified. This behavior will occur if Silverlights' automation faculties are invoked due to:
Tablet PC Input Service (in other words, all 'tablet like' PCs)
Automated Testing tools
Screen Readers (and other accessability software)
More information here: http://www.wintellect.com/cs/blogs/sloscialo/archive/2011/04/13/silverlight-memory-leaks-and-automationpeers.aspx
So a new problem surfaces: How do we disable automationpeers or otherwise get them to clean-up correctly?
This post illustrates one approach: WPF UserControl Memory leak
However, it isn't really a viable solution as we'd have to override every silverlight control which we plan to use binding for, not to mention the control templates of complex controls.
I will change my answer if anyone can identify a good solution, but for now there doesn't seem to be one...
Edit:
Here is a nice little workaround which seems to do the job. Simply add the following parameter in your HTML where you define the silverlight object:
<param name="windowless" value="true" />
A side-effect of running in 'windowless' mode is that automation doesn't work :)
There is no memory leak in your second example.
After you affect a new FooView instance to your ContentControl using myContent.Content = new FooView();, there is no more used reference to the whole View + ViewModel object graph.
It'll be garbage-collected when needed, eventually.
Maybe you should clarify about what make you think there is a memory leak (i.e. statistics, repro steps...etc.)
Related
I do have a ListDetailsView showing some data (lets say Company as a simple example here). Normally the details of a Company are shown as readonly. However, via the ListDetailsView.DetailsCommandBar it is possible to edit a Company (and also add a new Company). A clear separation between view and edit mode seems to be a good choice for the UI. I'm using a UserControl to show details of a Company.
So here are my questions:
Where should the differentiation between view- and edit-mode happen? I thought it is a good idea to have a CompanyDetailsControl and a CompanyDetailsEditControl and select between the two (both using the same CompanyDetailsViewModel). There are other solutions as well, for example, the CompanyDetailsControl could handle the edit- and view-mode internally.
Assuming that it is a good idea to switch between two UserControl, how can that be realized with the ListDetailsView.DetailsTemplate? I though it would be easy to use a DataTemplateSelector here, but that is only available for the ItemTemplate.
Not sure what code to provide to clarify my questions. So in case you need any code to better understand my question please leave a comment.
Note: I have never worked with UWP app, only applying MVVM pattern from WPF.
Straight line where the split should happen is not drawn. It often depends on the developer himself, which framework is used and more.
Me personally would go in way where UI handles UIs' things and ViewModel handles data only. That means the view is responsible for showing you the controls you are expecting to see/control the application. And when the view learns that property was changed, it should update how it looks.
Since the point we know the app will have edit & readonly modes, we should prepare all necessary UI components (UserControls, Pages, ...) to handle both those states. They would be binded to ViewModel that have base in BaseViewModel that already have this edit variable inside. So that each UI component know it can work with that.
Base view model:
abstract class BaseViewModel : INotifyPropertyChanged
{
private string mIsInEditMode;
public string IsInEditMode
{
get { return mIsInEditMode; }
set
{
if(mIsInEditMode == value) return;
mIsInEditMode = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(IsInEditMode));
}
}
public event PropertyChangedEventHandler PropertyChanged;
}
All "normal" ViewModels inherit from it:
class CompanyViewModel : BaseViewModel
{ /* props and logic of company */ }
UI component (UserControl) would have either trigger (<Style.Triggers/>) or binded properties Visibility and IsEnabled to the BaseViewModel. Those bindings would handle this logic of showing/hiding and you have potential to control whole layouts, hide controls etc.
<UserControl d:DataContext="{x:Bind local:CompanyViewModel}">
<UserControl.Resources>
<local:BoolInverterConverter x:Key="BoolInvert"/>
</UserControl.Resources>
<Grid>
<Grid IsVisible="{Binding IsInEditMode}" IsEnabled="{Binding IsInEditMode}">
<!-- Controls for edit mode -->
</Grid>
<Grid IsVisible="{Binding IsInEditMode, Converter={StaticResource BoolInvert}}"
IsEnabled="{Binding IsInEditMode, Converter={StaticResource BoolInvert}}">
<!-- Controls for readonly mode -->
</Grid>
</Grid>
</UserControl>
Note: I've used property IsVisible, You would actually use Visibility with some custom converter.
I am currently working on a just for fun solution for myself, to create workout plans for the gym.
Solution on Github
It is a simple WPF solution using Caliburn.Micro to easier get the connections for the MVVM pattern.
On my MainView I have a TabControl:
MainView.xaml:
<TabControl x:Name="Items" />
With the following code for the ViewModel:
public MainViewModel()
{
DisplayName = "Plan2Fit";
Items.Add(new CreatePlanViewModel(_exerciseProviderViewModel));
Items.Add(new ExerciseManagementViewModel(_exerciseProviderViewModel));
}
I only have two ViewModels displayed in the TabControl, one to manage exercises and store them in xml to have kind of a database of your exercises and one where you should later be able to pick exercises for your plan.
Problem:
At startup everything looks ok, but as soon as I switch between the Tabs, one of them might loose its child control for whatever reason.
I have already tried the following:
MainView.xaml:
<TabControl x:Name="Items" cal:Message.Attach="[Event SelectionChanged] = [Action Reload]" />
MainViewModel:
public void Reload()
{
_exerciseProviderViewModel = new ExerciseProviderViewModel();
Items.Refresh();
DisplayName = "Plan2Fit";
}
This makes the error happen less often but it still is existing.
I have already found this question ... the solutions I was able to find are all working with MVVM, but not with Caliburn.Micro, so I am really not havinmg any Idea, how to solve this.
I have tried Avalon dock, but I was not able to get it to work with the Caliburn way of binding x:Name="Items"
Note:
If you want to recreate the Bug using my solution, you have to add some "Exercises" by picking an image in the "Manage Exercise" tab and click Add (You can add the same "Exercise" multiple times).
There is no errorhandling or testing done so far, as this is at the state where I want to validate, if it works at all.
I found the problem, still I dont understand, why it is a problem at all.
Given:
public MainViewModel()
{
DisplayName = "Plan2Fit";
Items.Add(new CreatePlanViewModel(_exerciseProviderViewModel));
Items.Add(new ExerciseManagementViewModel(_exerciseProviderViewModel));
}
The tabcontrol will occasionally loose its childs.
If I pass a new ExerciseProviderViewModel into eacht Items.Add() call, the bug does not occur.
Thus I stored a ExerciseProvider as member and passed this one into my ViewModels I want to add.
public MainViewModel()
{
_exerciseProvider = new ExerciseProvider(new DCSerializer<List<Exercise>>());
DisplayName = "Plan2Fit";
ActivateItem(new CreatePlanViewModel(new ExerciseProviderViewModel(_exerciseProvider)));
ActivateItem(new ExerciseManagementViewModel(new ExerciseProviderViewModel(_exerciseProvider)));
}
This works without any problems.
OK so I am (very) new to WPF but have 13 years with Win-forms, so no newbie.
I am writing a new system and decided to break the ice with WPF using the MVP-VM pattern as I am familiar with MVP.
Now I am also reusing my own (composite) architecture which has the UI or presentation layer in a project separate from the Presenters and View models. One of the main benefits to this approach is that the Presenter layer or Base layer has all the presentation/command/controller logic and no reference to UI matters. The Main IDEA is that this layer has no reference to any UI assemblies like Winforms or WPF.
The ISSUE:
IN Xaml, I have a menu item 'Exit' which I want to bind to the View model. Every example of this that I have seen uses ICommand.. which is housed in Presentation.core... the view model is in the presentation layer and therefore does not have a reference to the Presentation.Core..
The Xaml thus far is
<Window x:Class="Homestead.Wpf.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="244" Width="490" xmlns:xcad="http://schemas.xceed.com/wpf/xaml/avalondock">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="23"/>
<RowDefinition Height="*"/>
<RowDefinition Height="23" />
</Grid.RowDefinitions>
<Menu Grid.Row="0">
<MenuItem Header="File">
<MenuItem Header="Exit" />
</MenuItem>
</Menu>
<StatusBar Grid.Row="2" >
<StatusBarItem >
<TextBlock Text="{Binding Path=StatusMessage}" />
</StatusBarItem>
<StatusBarItem HorizontalAlignment="Right" MinWidth="120">
<TextBlock Text="{Binding Path=UserName}" />
</StatusBarItem>
</StatusBar>
</Grid>
the view model thus far is
public class ShellViewModel : ViewModelBase
{
#region Private Variables
private string _status = string.Empty;
private string _userName;
#endregion
#region Public Properties
public string StatusMessage
{
get { return _status; }
set
{
_status = value;
OnPropertyChanged("StatusMessage");
}
}
public string UserName
{
get { return _userName; }
set
{
_userName = value;
OnPropertyChanged("UserName");
}
}
#endregion
#region Commands
// No reference to ICommand... WTF
#endregion
}
is there any alternative to this pattern..(without using code behind)
How does this work with Menu items added at runtime, amusing there is a way.
Must I redesign or break the design pattern to use WPF? please guide me.
There is one possibility that I can see and that is to Interface out the ViewModel as well as the View.. I have a Controller that creates the Presenters that usually passes in to the presenter the implementation of the view. I.e a typical Presenter constructor would be
public ErrorMessagePresenter(IErrorMessageView view)
{
View = view;
View.Presenter = this;
}
The controller code is thus
public ErrorMessagePresenter CreateErrorPresenter()
{
return new ErrorMessagePresenter(_viewFactory.CreateErrorMessageView());
}
now I know that this is reversed but literally the UI layer is only concerned with UI matters, everything else including navigation is handled in the Base layer or below..
The most common pattern in WPF is Model-View-ViewModel, a pattern in which the presenter has been removed in favor of a more knowledgeable view-model, which is indeed aware of things like ICommand (which is a contract, shared by the view layer as a way to communicate actions, and this justifies the view-model knowing about it).
You can try to force the application not use ICommand by implementing behaviors and data triggers which execute actions in the view-model based on the actions in the UI, but this requires much more coding, not just binding and it is not scalable or sustainable if you intend to continue developing WPF applications in the future (or Windows Store applications for that matter, which commonly use MVVM as well).
I recommend looking into incorporating a framework like MVVM-light in your project using NuGet, so that you can start on top of the foundation of a well-established set of classes, patterns and utilities which have adapted to WPF as a technology for many years instead of trying to hammer the "spherical" framework you are familiar with into the "square" cavity which is WPF (figuratively speaking).
I learned the hard way when I started with it, that trying to bring my experience and habits from WinForms to WPF brings nothing but trouble, so the sooner you start making your peace with the idea that most of what you have done won't be applicable in WPF, the better for your mental sanity.
Of course, patterns are patterns and they can be applied in different ways in a multitude of environments, but the reality is that WPF already has the plugs for certain things to work a certain way and trying to go against that is only going to give you more work as a developer and architect, so it is better to just go with the flow and adapt to the most common way of doing things in the XAML world.
To give you some quick solution options:
1 - System.Windows.Input.ICommand is available if you're targeting a Portable Class Library. This might be possible or not depending on other requirements for your project, but if it is an option, it's really recommendable that you put all your ViewModels and "UI targeting" code in PCLs, since these are by definition the most reusable and platform agnostic type of assemblies you can create in .Net, supporting not only Windows .Net scenarios, but also mobile thru Xamarin.
2 - Abstract the ICommand interface away via dependency injection or Service Locator patterns.
3: Add a reference to PresentationCore.dll but make sure you aren't using anything other than ICommand from there.
To answer your question #2, Menus: ListBoxes, ComboBoxes, TreeViews, and any other items-based UI elements in WPF are derived from the ItemsControl class. It provides an abstract way to dynamically generate UI elements from a collection of data items.
See this blog series for a very comprehensive explanation. The ItemsControl is one of the most powerful WPF features and mastering it can be very rewarding. I have created all sorts of things using it from breadcrumb bars to "Tagging controls" (similar to StackOverflow's tags selection), to chess boards to diagram designers
Basically you will create a class with simple properties representing the Menu Items (with text, an image, and an ICommand to be executed when the menu item is clicked) and put instances of this class into an ObservableCollection<T>, which supports WPF databinding to collections.
On a side note, your Controller creating the presenter and the view and associating the view with the ViewModel and the Presenter approach and all that is not needed. It creates an overly complex, totally unmaintainable scenario where you need to manually do this every time you need to show a View.
WPF resolves that with the use of DataTemplates. See this answer for a very simple usage example.
In fact, the whole concept of a Presenter is not even needed at all in WPF. Since your Views are "glued" to their underlying ViewModels using DataBinding, and there's no need for manual "piping" code or passing data back and forth.
I'm curious how this works, because I have a MainViewModel, which has Property say called SubViewModel which has a Property of ObservableCollection (we'll call it Property1.)
I've implemented INotifyChangedProperty on everything.
My Main Window
<Window ..
DataContext="{Binding MainViewModel}" />
...
<StackPanel DataContext="{Binding SubViewModel}">
<local:SomeControl DataContext="{Binding}" />
</StackPanel>
</Window>
And my UserControl
<UserControl Name="SomeControl">
<DataGrid Name="MyDataGrid" ItemSource="{Binding Property1, Mode=TwoWay}" CurrentCellChanged="TestMethod" />
...
</UserControl>
In my test method, just as a test to figure out why the changes are not propegating up to the main view model I do something like this
private void TestMethod()
{
var vm = this.DataContext as SubViewModel;
var itemSourceObservableCollection = MyDataGrid.ItemsSource as ObservableCollection<MyType>;
//I thought vm.Property1 would be equal to itemSourceObservableCollection
//but they are not, itemSourceObservableCollection shows the changes I've made
//vm.Property1 has not reflected any changes made, even though I though they were the same item
}
So I figured out that ItemSource must create a copy of the item you bind it to? I'm stuck here, how do manually notify the viewModel that this property has changed and it needs to update? I thought that was INotifyPropertyChanged's job?
I think part of my problem is I lack the understanding of how this kinda works internally. If anyone can point to a good blog post, or documentation to help me understand why my code isn't working the way I expected, that would be great.
1) No copy is made.
2) ObservableCollection will propogate changes made to the collection, not the items within the collection. So you'll see additions, deletions etc. but NOT property changes to items within the collection.
3) If you want to see changes made to individual items in the ObservableCollection, you need to implement INotifyPropertyChanged on those items.
There's actually TWO different issues here. What happens internally when you bind to a collection? AND why changes on the user surface are not propagated back to your View Model. Based upon what you wrote, the two issues are not connected, but let's take them one at a time...
For the first issue... When you bind a collection, the WPF binding engine creates a "CollectionView" class that mediates between your object store and the logical tree. You can, if needed, get a copy of the the "CollectionView" using a static method on CollectionViewSource...
var cvs = CollectionViewSource.GetDefaultView(MyCollectionOfThings);
There are several interesting properties in the result, and some of them contain write accessors which allow you to directory modify the CollectionView.
For the second issue... The business classes in your SubViewModel need to inherit from INotifyPropertyChanged such that changes are 'announced' via the WPF binding engine. Your VM should be a publisher, but can also be a subscriber. A property that participates in the INotifyPropertyChanged plumbing gets declared like this...
private string _name;
[Description("Name of the driver")]
public string Name
{
[DebuggerStepThrough]
get { return _name; }
[DebuggerStepThrough]
set
{
if (value != _name)
{
_name = value;
OnPropertyChanged("Name");
}
}
}
This code publishes changes, but can also subscribe to changes made on the user surface by setting the appropriate attributes in your Xaml.
Background reading: What is a CollectionView?
Also, Similar question
I have defined my binding thus:
<TreeView
ItemsSource="{Binding UsersView.View}"
ItemTemplate="{StaticResource MyDataTemplate}"
/>
The CollectionViewSource is defined thus:
private ObservableCollection<UserData> users;
public CollectionViewSource UsersView{get;set;}
UsersView=new CollectionViewSource{Source=users};
UsersView.SortDescriptions.Add(
new SortDescription("IsLoggedOn",ListSortDirection.Descending);
UsersView.SortDescriptions.Add(
new SortDescription("Username",ListSortDirection.Ascending);
So far, so good, this works as expected: The view shows first the users that are logged on in alphabetical order, then the ones that are not.
However, the IsLoggedIn property of the UserData is updated every few seconds by a backgroundworker thread and then the code calls:
UsersView.View.Refresh();
on the UI thread.
Again this works as expected: users that log on are moved from the bottom of the view to the top and vice versa. However: Every time I call the Refresh method on the view the application hoards 3,5MB of extra memory, which is only released after application shutdown (or after an OutOfMemoryException...)
I did some research and below is a list of fixes that did NOT work:
The UserData class implements INotifyPropertyChanged
Changing the underlying users collection does not make any difference at all: any IENumerable<UserData> as a source for the CollectionViewSource causes the problem.
-Changing the ColletionViewSource to a List<UserData> (and refreshing the binding) or inheriting from ObservableCollection to get access to the underlying Items collection to sort that in place does not work.
I am out of ideas! Help?
EDIT:
I found it:
The Resource MyDataTemplate contains a Label that is bound to a UserData object to show one of its properties, the UserData objects being handed down by the TreeView's ItemsSource. The Label has a ContextMenu defined thus:
<ContextMenu Background="Transparent" Width="325" Opacity=".8" HasDropShadow="True">
<PrivateMessengerUI:MyUserData IsReadOnly="True" >
<PrivateMessengerUI:MyUserData.DataContext>
<Binding Path="."/>
</PrivateMessengerUI:MyUserData.DataContext>
</PrivateMessengerUI:MyUserData>
</ContextMenu>
The MyUserData object is a UserControl that shows All properties of the UserData object. In this way the user first only sees one piece of data of a user and on a right click sees all of it.
When I remove the MyUserData UserControl from the DataTemplate the memory leak disappears! How can I still implement the behaviour as specified above?
Step one in troubleshooting memory leaks is to find the definitive source. This is not always obvious and can occasionally defy your intuition. Based on your explanation, if you remove your user control, the issue disappears, but when you put it back, you start leaking again. This very likely points to a memory leak within that control (though not necessarily). Perhaps your control fits into one of the many types of WPF memory leaks, or you have a more classic problem of subscribing to an event but not properly unwiring it when no longer needed (the weak event pattern is useful here).
Your best bet is to grab a tool like .NET Memory Profiler or ANTS Memory Profiler (both are excellent and have free trials). Use one of these tools find the objects that are hanging around after they should be gone. These tools provide help in tracing the chain of objects that are hanging onto your object. There are many good articles on memory profiling on their sites, here on SO and on the wide open web.
You could try 2 things:
First of all the DropShadow has some problems, as it hold some references that should be garbadge collected.. see this article for more information:
http://blog.ramondeklein.nl/index.php/2009/02/20/memory-leak-with-wpf-resources-in-rare-cases/
You can try to set th HasDropShadow to false, and see what happens with your memory.
Secondly, if we have multiple ContextMenu objects of the same menu, you might want to create a ContextMenu resource in your Resources, and reference it with the StaticResource extension like so:
<ContextMenu Background="Transparent" Width="325" Opacity=".8" x:Key="MyAwesomeContextMenu">
<PrivateMessengerUI:MyUserData IsReadOnly="True" >
<PrivateMessengerUI:MyUserData.DataContext>
<Binding Path="."/>
</PrivateMessengerUI:MyUserData.DataContext>
</PrivateMessengerUI:MyUserData>
</ContextMenu>
And use it where you defined your context menu:
ContextMenu="{StaticResource MyAwesomeContextMenu}"
Hope this helps a bit!