When I use databinding in WPF, my target controls are listening for events on the binding source. For example, I may have a ListView listening for CollectionChanged events on a ObservableCollection.
If the lifetime of an event source is expected to exceed the lifetime of an event listener, there is a potential memory leak, and the weak event pattern should be used.
Does WPF databinding follow the weak event pattern? If my ObservableCollection lives longer than my ListView, will my ListView be garbage collected?
Here is why I suspect that WPF controls do not implement the weak event pattern. If they did, I would expect both DerivedListView Collected! and DerivedTextBlock Collected! to be output to the console. Instead, only DerivedTextBlock Collected! is.
After fixing a bug in the code, both objects are collected. I'm not sure what to think.
Window1.xaml.cs
using System;
using System.Collections.ObjectModel;
using System.Windows;
using System.Windows.Controls;
namespace LeakDetector
{
public class DerivedListView : ListView
{
~DerivedListView()
{
Console.WriteLine("DerivedListView Collected!");
}
}
public class DerivedTextBlock : TextBlock
{
~DerivedTextBlock()
{
Console.WriteLine("DerivedTextBlock Collected!");
}
}
public partial class Window1 : Window
{
// The ListView will bind to this collection and listen for its
// events. ObColl will hold a reference to the ListView.
public ObservableCollection<int> ObColl { get; private set; }
public Window1()
{
this.ObColl = new ObservableCollection<int>();
InitializeComponent();
// Trigger an event that DerivedListView should be listening for
this.ObColl.Add(1);
// Get rid of the DerivedListView
this.ParentBorder.Child = new DerivedTextBlock();
GC.Collect(GC.MaxGeneration, GCCollectionMode.Forced);
GC.WaitForPendingFinalizers();
GC.Collect(GC.MaxGeneration, GCCollectionMode.Forced);
this.ParentBorder.Child = null;
GC.Collect(GC.MaxGeneration, GCCollectionMode.Forced);
GC.WaitForPendingFinalizers();
GC.Collect(GC.MaxGeneration, GCCollectionMode.Forced);
Console.WriteLine("Done");
}
}
}
Window1.xaml
<Window
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:LeakDetector"
x:Class="LeakDetector.Window1"
DataContext="{Binding RelativeSource={RelativeSource Self}}"
Height="300" Width="300"
Title="Leak Detector">
<Border x:Name="ParentBorder">
<local:DerivedListView ItemsSource="{Binding Path=ObColl}" />
</Border>
</Window>
In essence, the WPF controls themselves do not have anything to do with weak events. Instead, there are certain classes related to WPF's Binding engine that implement the weak event pattern. The class PropertyChangedEventManager implements WeakEventManager. And if you use Reflector, you'll see that several classes implement IWeakEventListener in the MS.Internal.Data namespace (one in particular is the MS.Internal.Data.PropertyPathWorker class which directly uses the PropertyChangedEventManager). These objects are used by WPF internally to do Data Binding.
ItemsControls and CollectionChanged events are a different story and has nothing to do with Bindings. See, you could do something like "listView.ItemsSource = myObservableCollection" in the code behind and the collection-changed notification will still work. No Binding objects are involved here at all. Here, a different set of "weak-event-related classes" are in play. ItemCollection and ItemContainerGenerator implement IWeakEventListener, and they work in conjunction with the CollectionChangedEventManager(which implements WeakEventManager).
The second sentence of the MSDN article to which you linked pretty clearly states that WPF does use the Weak Event Pattern. In fact, it goes so far as to say that WPF introduced the pattern.
Edit:
I was hoping to find some documentation which explicitly states "WPF controls implement the weak event pattern." – emddudley
After doing some research, I think the answer to that question is "no", and I think the reason the answer is "no" is that WPF doesn't expect UI controls to be transitory. While there is a CollectionChangedEventManager class built specifically for weak events against the CollectionChanged event, none of the controls that support databinding appear to implement IWeakEventListener, which would be necessary to use weak events against the collection.
I think the pattern and usage are built for a ViewModel rather than a View, which is more likely to be transitory than a View.
Edit2:
After fixing a bug in the code, both objects are collected. Therefore I believe that WPF controls use the weak event pattern.
Interesting result. If they do implement Weak Events, they must do it internally.
Related
I created a ViewModel and bound its property to two textboxes on UI. The value of the other textbox changes when I change the value of first and focus out of the textbox but I'm not implementing INotifyPropertyChanged. How is this working?
Following is XAML
<Window x:Class="WpfApplication1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WpfApplication1"
Title="MainWindow" Height="350" Width="525">
<Window.DataContext>
<local:ViewModel />
</Window.DataContext>
<StackPanel>
<TextBox Text="{Binding Name}" />
<TextBox Text="{Binding Name}" />
</StackPanel>
</Window>
And following is my ViewModel
class ViewModel
{
public string Name { get; set; }
}
I tested it, you are right. Now i searched for it on the web, and found this.
Sorry to take so long to reply, actually you are encountering a another hidden aspect of WPF, that's it WPF's data binding engine will data bind to PropertyDescriptor instance which wraps the source property if the source object is a plain CLR object and doesn't implement INotifyPropertyChanged interface. And the data binding engine will try to subscribe to the property changed event through PropertyDescriptor.AddValueChanged() method. And when the target data bound element change the property values, data binding engine will call PropertyDescriptor.SetValue() method to transfer the changed value back to the source property, and it will simultaneously raise ValueChanged event to notify other subscribers (in this instance, the other subscribers will be the TextBlocks within the ListBox.
And if you are implementing INotifyPropertyChanged, you are fully responsible to implement the change notification in every setter of the properties which needs to be data bound to the UI. Otherwise, the change will be not synchronized as you'd expect.
Hope this clears things up a little bit.
So basically you can do this, as long as its a plain CLR object. Pretty neat but totally unexpected - and i have done a bit of WPF work the past years. You never stop learning new things, right?
As suggested by Hasan Khan, here is another link to a pretty interesting article on this subject.
Note this only works when using binding. If you update the values from code, the change won't be notified. [...]
WPF uses the much lighter weight PropertyInfo class when binding. If you explicitly implement INotifyPropertyChanged, all WPF needs to do is call the PropertyInfo.GetValue method to get the latest value. That's quite a bit less work than getting all the descriptors. Descriptors end up costing in the order of 4x the memory of the property info classes. [...]
Implementing INotifyPropertyChanged can be a fair bit of tedious development work. However, you'll need to weigh that work against the runtime footprint (memory and CPU) of your WPF application. Implementing INPC yourself will save runtime CPU and memory.
Edit:
Updating this, since i still get comments and upvotes now and then from here, so it clearly is still relevant, even thouh i myself have not worked with WPF for quite some time now. However, as mentioned in the comments, be aware that this may cause memory leaks. Its also supposedly heavy on the Reflection usage, which has been mentioned as well.
I just found out that this also works in WinForms, kinda :/
public class Test
{
public bool IsEnabled { get; set; }
}
var test = new Test();
var btn = new Button { Enabled = false, Text = "Button" };
var binding = new Binding("Enabled", test, "IsEnabled");
btn.DataBindings.Add(binding);
var frm = new Form();
frm.Controls.Add(btn);
test.IsEnabled = true;
Application.Run(frm);
Strangely though, this doesn't disable the button:
btn.Enabled = false;
This does:
test.IsEnabled = false;
I can explain why the property is updated when focus changes: all Bindings have an UpdateSourceTrigger property which indicates when the source property will be updated. The default value for this is defined on each DependencyProperty and for the TextBox.Text property is set to LostFocus, meaning that the property will be updated when the control loses focus.
I believe UrbanEsc's answer explains why the value is updated at all
I created a ViewModel and bound its property to two textboxes on UI. The value of the other textbox changes when I change the value of first and focus out of the textbox but I'm not implementing INotifyPropertyChanged. How is this working?
Following is XAML
<Window x:Class="WpfApplication1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WpfApplication1"
Title="MainWindow" Height="350" Width="525">
<Window.DataContext>
<local:ViewModel />
</Window.DataContext>
<StackPanel>
<TextBox Text="{Binding Name}" />
<TextBox Text="{Binding Name}" />
</StackPanel>
</Window>
And following is my ViewModel
class ViewModel
{
public string Name { get; set; }
}
I tested it, you are right. Now i searched for it on the web, and found this.
Sorry to take so long to reply, actually you are encountering a another hidden aspect of WPF, that's it WPF's data binding engine will data bind to PropertyDescriptor instance which wraps the source property if the source object is a plain CLR object and doesn't implement INotifyPropertyChanged interface. And the data binding engine will try to subscribe to the property changed event through PropertyDescriptor.AddValueChanged() method. And when the target data bound element change the property values, data binding engine will call PropertyDescriptor.SetValue() method to transfer the changed value back to the source property, and it will simultaneously raise ValueChanged event to notify other subscribers (in this instance, the other subscribers will be the TextBlocks within the ListBox.
And if you are implementing INotifyPropertyChanged, you are fully responsible to implement the change notification in every setter of the properties which needs to be data bound to the UI. Otherwise, the change will be not synchronized as you'd expect.
Hope this clears things up a little bit.
So basically you can do this, as long as its a plain CLR object. Pretty neat but totally unexpected - and i have done a bit of WPF work the past years. You never stop learning new things, right?
As suggested by Hasan Khan, here is another link to a pretty interesting article on this subject.
Note this only works when using binding. If you update the values from code, the change won't be notified. [...]
WPF uses the much lighter weight PropertyInfo class when binding. If you explicitly implement INotifyPropertyChanged, all WPF needs to do is call the PropertyInfo.GetValue method to get the latest value. That's quite a bit less work than getting all the descriptors. Descriptors end up costing in the order of 4x the memory of the property info classes. [...]
Implementing INotifyPropertyChanged can be a fair bit of tedious development work. However, you'll need to weigh that work against the runtime footprint (memory and CPU) of your WPF application. Implementing INPC yourself will save runtime CPU and memory.
Edit:
Updating this, since i still get comments and upvotes now and then from here, so it clearly is still relevant, even thouh i myself have not worked with WPF for quite some time now. However, as mentioned in the comments, be aware that this may cause memory leaks. Its also supposedly heavy on the Reflection usage, which has been mentioned as well.
I just found out that this also works in WinForms, kinda :/
public class Test
{
public bool IsEnabled { get; set; }
}
var test = new Test();
var btn = new Button { Enabled = false, Text = "Button" };
var binding = new Binding("Enabled", test, "IsEnabled");
btn.DataBindings.Add(binding);
var frm = new Form();
frm.Controls.Add(btn);
test.IsEnabled = true;
Application.Run(frm);
Strangely though, this doesn't disable the button:
btn.Enabled = false;
This does:
test.IsEnabled = false;
I can explain why the property is updated when focus changes: all Bindings have an UpdateSourceTrigger property which indicates when the source property will be updated. The default value for this is defined on each DependencyProperty and for the TextBox.Text property is set to LostFocus, meaning that the property will be updated when the control loses focus.
I believe UrbanEsc's answer explains why the value is updated at all
I created a ViewModel and bound its property to two textboxes on UI. The value of the other textbox changes when I change the value of first and focus out of the textbox but I'm not implementing INotifyPropertyChanged. How is this working?
Following is XAML
<Window x:Class="WpfApplication1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WpfApplication1"
Title="MainWindow" Height="350" Width="525">
<Window.DataContext>
<local:ViewModel />
</Window.DataContext>
<StackPanel>
<TextBox Text="{Binding Name}" />
<TextBox Text="{Binding Name}" />
</StackPanel>
</Window>
And following is my ViewModel
class ViewModel
{
public string Name { get; set; }
}
I tested it, you are right. Now i searched for it on the web, and found this.
Sorry to take so long to reply, actually you are encountering a another hidden aspect of WPF, that's it WPF's data binding engine will data bind to PropertyDescriptor instance which wraps the source property if the source object is a plain CLR object and doesn't implement INotifyPropertyChanged interface. And the data binding engine will try to subscribe to the property changed event through PropertyDescriptor.AddValueChanged() method. And when the target data bound element change the property values, data binding engine will call PropertyDescriptor.SetValue() method to transfer the changed value back to the source property, and it will simultaneously raise ValueChanged event to notify other subscribers (in this instance, the other subscribers will be the TextBlocks within the ListBox.
And if you are implementing INotifyPropertyChanged, you are fully responsible to implement the change notification in every setter of the properties which needs to be data bound to the UI. Otherwise, the change will be not synchronized as you'd expect.
Hope this clears things up a little bit.
So basically you can do this, as long as its a plain CLR object. Pretty neat but totally unexpected - and i have done a bit of WPF work the past years. You never stop learning new things, right?
As suggested by Hasan Khan, here is another link to a pretty interesting article on this subject.
Note this only works when using binding. If you update the values from code, the change won't be notified. [...]
WPF uses the much lighter weight PropertyInfo class when binding. If you explicitly implement INotifyPropertyChanged, all WPF needs to do is call the PropertyInfo.GetValue method to get the latest value. That's quite a bit less work than getting all the descriptors. Descriptors end up costing in the order of 4x the memory of the property info classes. [...]
Implementing INotifyPropertyChanged can be a fair bit of tedious development work. However, you'll need to weigh that work against the runtime footprint (memory and CPU) of your WPF application. Implementing INPC yourself will save runtime CPU and memory.
Edit:
Updating this, since i still get comments and upvotes now and then from here, so it clearly is still relevant, even thouh i myself have not worked with WPF for quite some time now. However, as mentioned in the comments, be aware that this may cause memory leaks. Its also supposedly heavy on the Reflection usage, which has been mentioned as well.
I just found out that this also works in WinForms, kinda :/
public class Test
{
public bool IsEnabled { get; set; }
}
var test = new Test();
var btn = new Button { Enabled = false, Text = "Button" };
var binding = new Binding("Enabled", test, "IsEnabled");
btn.DataBindings.Add(binding);
var frm = new Form();
frm.Controls.Add(btn);
test.IsEnabled = true;
Application.Run(frm);
Strangely though, this doesn't disable the button:
btn.Enabled = false;
This does:
test.IsEnabled = false;
I can explain why the property is updated when focus changes: all Bindings have an UpdateSourceTrigger property which indicates when the source property will be updated. The default value for this is defined on each DependencyProperty and for the TextBox.Text property is set to LostFocus, meaning that the property will be updated when the control loses focus.
I believe UrbanEsc's answer explains why the value is updated at all
This is what I'm trying to do:
I'm writing a UserControl that I want to be consumed by other developers.
I want end users to be able to use my control using Dependency Properties.
<lib:ControlView ControlsText={Binding Path=UsersOwnViewModelText} />
I'm using the MVVM pattern.
I'm binding my ViewModels to their View's using <DataTemplates>
<DataTemplate DataType="{x:Type local:ControlViewModel}">
<local:ControlView />
</DataTemplate>
So I have two questions:
Am I right in thinking that if a UserControl is being consumed in XAML then the UserControl must set the ViewModel as its DataContext when the control's Loaded event fires instead of using the <DataTemplate> method?
How do I allow users to data bind to my control's dependency properties while still being data bound to my ViewModel?
You should separate the two use cases:
The (user) control that will be consumed by other developers.
The user control that will be consumed by your application.
Importantly, the latter depends on the former - not vice versa.
Use case 1 would use dependency properties, template bindings, all the things that go into making a regular WPF control:
MyControl.cs:
public class MyControl : Control
{
// dependency properties and other logic
}
Generic.xaml:
<ControlTemplate Type="local:MyControl">
<!-- define the default look in here, using template bindings to bind to your d-props -->
</ControlTemplate>
You would then define use case 2 as:
MyViewModel.cs:
public class MyViewModel : ViewModel
{
// properties and business logic
}
MyView.xaml:
<UserControl ...>
<local:MyControl SomeProperty="{Binding SomePropertyOnViewModel}" .../>
</UserControl>
Best of both worlds with a clean separation. Other developers depend only on the control, which could (and probably should) be in a completely different assembly than your view model and view.
First off, I don't think MVVM is a good choice if you are developing a UserControl that will be consumed by others. A lookless control is what you really should be developing. Jeremiah Morrill has a blog post about this subject.
With that said, you can set the datacontext with XAML if you have a default public constructor.
Inside ControlView.xaml put:
<UserControl.DataContext>
<local:ControlViewModel />
</UserControl.DataContext>
Basically, instead of binding your UserControl's datacontext to the userControlViewModel, it's better to do it on the first child element of the user control. That way, all the references that you make within the control will be bound to the userControlViewModel, but the dependencies properties can be set from the data context set where you want to use your UserControl.
This is from a project I'm working at:
<UserControl x:Class="Six_Barca_Main_Interface.MyUserControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:Six_Barca_Main_Interface"
xmlns:System="clr-namespace:System;assembly=mscorlib"
mc:Ignorable="d"
d:DesignHeight="900" d:DesignWidth="900">
<DockPanel x:Name="rootDock" >
<TextBlock>{Binding SomethingInMyUserControlViewModel}</TabControl>
</DockPanel>
</UserControl>
Then on the code behind:
public partial class MyUserControl : UserControl
{
UserControlViewModel _vm;
public MyUserControl()
{
InitializeComponent();
//internal viewModel set to the first child of MyUserControl
rootDock.DataContext = new UserControlViewModel();
_vm = (UserControlViewModel)rootDock.DataContext;
//sets control to be able to use the viewmodel elements
}
#region Dependency properties
public string textSetFromApplication
{
get{return (string)GetValue(textSetFromApplicationProperty);}
set{SetValue(textSetFromApplicationProperty, value);}
}
public static readonly DependencyProperty textSetFromApplicationProperty = DependencyProperty.Register("textSetFromApplication", typeof(string), typeof(MyUserControl), new PropertyMetadata(null, OnDependencyPropertyChanged));
private static void OnDependencyPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
((MyUserControl)d)._vm.SomethingInMyUserControlViewModel =
e.NewValue as string;
}
#endregion
Then when you use this on your main view, you can set the dependency property with the value you want to pass to MyUSerControl
A UserControl is part of the "View" in "MVVM" just like the TextBox or ListView controls are part of the View.
Whether you decide to use MVVM to develop your UserControl itself or write it in QBASIC (not recommended) it does not break the MVVM pattern for the consumers of your UserControl so long as they can do every thing they need with your UserControl by binding to DependencyProperty's exposed on your UserControl. i.e. Your UserControl should expose the properties it is dependent upon (hence the name). Once you grasp this DependencyProperty's suddenly make a whole lot of sense and you want their helpful on changed event handlers and default values you specify in their constructor.
If your UserControl is in a different assembly or not I cannot see how that makes a difference.
That said many would advocate you build your UserControl using the MVVM pattern itself for all the good reasons MVVM brings e.g. helping another developer looking at your code. However some things simply are not possible and/or much harder more complex and less performant hacking the XAML to do this - I am not talking about your garden variety Add User Form but for example a UserControl handling the layout of thousands of visuals. Furthermore since you are working in your View you do NOT want your UserControl's ViewModels mixed in with you applications!
Basically I am saying it is well within MVVM not to use MVVM on your View!
I'm building a simple UserControl example with DependencyProperties so that the properties of the control can be changed in XAML (code below).
But of course in my application I don't want this control to have tightly-coupled code-behind, but instead the user control will be a view called "DataTypeWholeNumberView" and it will have its own ViewModel called "DataTypeWholeNumberViewModel".
So I am going to implement the DependencyProperty logic below into the ViewModel, but in ViewModels I usually inherit INotifyPropertyChanged which seems to give me the same functionality.
So what is the relationship between:
binding the DataContext of UserControl XAML to its code behind which has a DependencyProperties
binding the DataContext of UserControl XAML (View) to its ViewModel (which inherits from INotifyPropertyChanged) and has properties which implements INotifyPropertyChanged functionality?
XAML:
<UserControl x:Class="TestDependencyProperty827.SmartForm.DataTypeWholeNumber"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<StackPanel>
<StackPanel HorizontalAlignment="Left" VerticalAlignment="Top" Orientation="Horizontal">
<TextBlock Text="{Binding Label}"/>
</StackPanel>
</StackPanel>
</UserControl>
Code Behind:
using System.Windows;
using System.Windows.Controls;
namespace TestDependencyProperty827.SmartForm
{
public partial class DataTypeWholeNumber : UserControl
{
public DataTypeWholeNumber()
{
InitializeComponent();
DataContext = this;
}
public string Label
{
get
{
return (string)GetValue(LabelProperty);
}
set
{
SetValue(LabelProperty, value);
}
}
public static readonly DependencyProperty LabelProperty =
DependencyProperty.Register("Label", typeof(string), typeof(DataTypeWholeNumber),
new FrameworkPropertyMetadata());
}
}
INotifyPropertyChanged is an interface that exists in .Net since 2.0. It basically allows objects to notify when a property has changed. An interested party can perform certain actions when this event is raised. The problem with it is that it only publishes the name of the property. So you end up using reflection or some iffy if statements to figure out what to do in the handler.
DependencyProperties are a more elaborate construct that supports default values, change notifications in a more memory-efficient and performant way.
The only relationship is that the WPF binding model supports binding to either DependencyProperties or to standard Clr properties, with an INotifyPropertyChanged implementation. Your ViewModel could be a DependecyObject as well and the third option would be to bind to the ViewModel's DependencyProperties!
Kent Boogaart wrote a very interesting article on having a ViewModel be a POCO vs a DependencyObject.
I don't really think there is a relationship between DependencyProperties and INotifyPropertyChanged. The only magic here is that the Binding classes/utils are smart enough to recognize a DependencyProperty and bind directly to it, or subscribe to the binding-target's notify-property-changed event and wait for that to fire.
With WPF, you can either bind to DependencyProperties or to Properties which implement INotifyPropertyChanged. It's a matter of choice.
So your question breaks into either to have them in code behind or in view model. Since you have mentioned that you do not want a tightly-coupled code behind, you are better off having a view model following the MVVM pattern.
You can use the DependencyProperties even in your view model just like you have done in your code behind.