Order of setting the DataContext in the default constructor in WPF - c#

I experiment with the order of setting the DataContext property in the default constructor in WPF.
<StackPanel>
<ListBox ItemsSource="{Binding MyItems, PresentationTraceSources.TraceLevel=High}"></ListBox>
<TextBlock Text="{Binding SomeText}"></TextBlock>
<TextBlock Text="{Binding SomeNum}"></TextBlock>
<TextBlock Text="{Binding Path=Person.Name}"></TextBlock>
<ListBox ItemsSource="{Binding Path=PersonList}">
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Name}"></TextBlock>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</StackPanel>
1) With DataContext set before the InitializeComponent method
public partial class MainWindow : Window, INotifyPropertyChanged
{
private string someText = "Default text";
public List<string> MyItems { get; set; }
public List<Person> PersonList { get; set; }
public Person Person { get; set; }
public int SomeNum { get; set; }
public string SomeText
{
get
{
return someText;
}
set
{
someText = value;
OnPropertyChanged("SomeText");
}
}
public MainWindow()
{
this.DataContext = this;
MyItems = new List<string>();
PersonList = new List<Person>();
Person = new Person();
InitializeComponent();
/*These changes are not reflected in the UI*/
SomeNum = 7;
Person.Name = "Andy";
/*Changes reflected with a help of INotifyPropertyChanged*/
SomeText = "Modified Text";
/* Changes to the Lists are reflected in the UI */
MyItems.Add("Red");
MyItems.Add("Blue");
MyItems.Add("Green");
MyItems[0] = "Golden";
PersonList.Add(new Person() { Name = "Xavier" });
PersonList.Add(new Person() { Name = "Scott" });
PersonList[0].Name = "Jean";
}
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string name)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(name));
}
}
}
public class Person
{
public string Name { get; set; } = "Default Name";
}
After the call to the InitializeComponent method changes to the values of properties are not reflected in the UI except for those properties which use INotifyPropertyChanged. Everything is clear so far.
However I noticed that changes to the list items are also reflected in the UI. How come?
I always thought that in order to reflect adding/removing from the collection I need ObservableCollection and to implement INotifyPropertyChanged on list object to detect modifications of these objects. What is the meaning of this?
2) With DataContext set after the InitializeComponent method
Why setting a DataContext property after the InitializeComponent is a bad practice with MVVM? Could you describe it more thoroughly or give a simple code example?

I always thought that in order to reflect adding/removing from the collection I need ObservableCollection<T> and to implement INotifyPropertyChanged on list object to detect modifications of these objects.
You do, if you want reliable updating of the UI during changes in the view model.
What is the meaning of this?
The "meaning" is that in your particular scenario, you are making assumptions that aren't valid. WPF components go through a variety of initialization steps, only some of which occur as part of the InitializeComponent() method.
If, for example, you were to move the code for your value updates into a handler for the Loaded event, you'd find some of the updates reflected in the UI, but not all.
If you move that same code into a method invoked via Dispatcher.InvokeAsync() using a priority of DispatcherPriority.SystemIdle, you'd find that none of the updates would be observed, except for the one backed by INotifyPropertyChanged. In that case, you're explicitly waiting until every aspect of initialization has completed, and there are no longer opportunities for the initialization code to observe your updated values.
It's all about timing. Any code that sets a value before the UI winds up observing it, can do so successfully without INotifyPropertyChanged or equivalent. But you're entirely at the mercy of the current implementation of the framework in that case. Different parts of the initialization happen at different times, and these are not all documented, so you're relying on undocumented behavior. It probably won't change, but you have no way to know for sure.
Why setting a DataContext property after the InitializeComponent is a bad practice with MVVM?
It's not. Don't believe everything you read, even (or especially!) on the Internet.
If you want to forego implementation of INotifyPropertyChanged, then it will be important that you initialize all of your view model data before assigning the DataContext. But, even if you assign the DataContext after calling InitializeComponent, that assignment will be observed (because DataContext is a dependency property and so provides property changed notification to the framework), and the UI will retrieve all of the bound data from your view model data.
What's important is that the view model data be initialized before the assignment of DataContext. Where that happens relative to InitializeComponent() is not important.

When a view model property does not fire the PropertyChanged event, its value must of course be set before assigning the view model instance to the view's DataContext.
It does however not matter if you assign the DataContext before or after calling InitializeComponent:
Given a Binding like
<TextBlock Text="{Binding SomeText}"/>
these two sequence will both result in showing the property value in the view:
DataContext = new { SomeText = "Hello, World." };
InitializeComponent();
and
InitializeComponent();
DataContext = new { SomeText = "Hello, World." };

Related

using INotifyPropertyChanged instead of ObservableCollection

I have a ListView bound to a collection, and I want the ListView to automatically update when an item is added to the collection. I managed to get it working using an ObservableCollection, but I'd rather to use INotifyPropertyChanged instead. Maybe you can give me a hint what I am doing wrong?
First, here is the (relevant part of) XAML:
<StackPanel DataContext="{Binding Family}"> <!-- DataContext is of type Family -->
<TextBlock Text="{Binding LastName}"/>
<ListView ItemsSource="{Binding Members}">
<ListView.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding FirstName}"/>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</StackPanel>
Here are the relevant classes:
public class Family : INotifyPropertyChanged
{
public string LastName { get; private set; }
private readonly IList<Member> _members;
public IEnumerable<Member> Members { get => _members; }
public Family(string lastName, IEnumerable<Member> members)
{
LastName = lastName;
_members = members.ToList();
}
public void AddMember(string name)
{
var member = new Member { FirstName = name };
_members.Add(member);
OnPropertyChanged(nameof(Members));
}
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged([CallerMemberName] string name = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
}
}
public class Member
{
public string FirstName { get; set; }
}
If I use this code and call AddMember somewhere, it will not update the ListView GUI. I don't see why not, because AddMember calls OnPropertyChanged(nameof(Members)), and Members is what the ListView is bound to. So it should get notified about the change.
So what am I doing wrong?
If I change IList<Member> _members into ObservableCollection<Member> _members and _members = members.ToList() into _members = new ObservableCollection<Member>(members) accordingly, it works as expected.
After adding an item to the _members collection, the reference returned by Members is still the same. The Equals method of collections will usually compare references, not items. Consequently, the binding will not detect a change and does not reevaluate the property.
If you want to get this to work, you could do one of the following:
Assign null temporarily, raise property changed, reassign the collection and raise property changed again, so the binding detects a changed reference (thanks to #Ash).
public void AddMember(string name)
{
var member = new Member { FirstName = name };
_members.Add(member);
var members = _members;
_members = null;
OnPropertyChanged(nameof(Members));
_members = members;
OnPropertyChanged(nameof(Members));
}
Naive approach, recreate the collection when you add a member, e.g:
public void AddMember(string name)
{
var member = new Member { FirstName = name };
_members = _members.ToList();
_members.Add(member);
OnPropertyChanged(nameof(Members));
}
This is costly due to lots of unnecessary allocations, don't do it.
As you can see, both approaches have their downsides, either firing additional property changed notifications or unnecessary allocations which will additionally cause the ListView to remove and recreate all of its items each time. This is why there is an ObservableCollection<T> type that implements INotifyCollectionChanged, which allows notifying added and removed items specifically, as well as other operations.
First of all, it is good that you decided that a Family is not an ObservableCollection. After all, you can do a lot of things with ObservableCollections that can't be done in Families. For instance: what would Replace(Member) mean in the context of a family?
The problem is, that you forgot to implement INotifyCollectionChanged.
With this interface you can notify others that you added an element (and moved, and deleted, etc.)
public class Family : INotifyPropertyChanged, INotifyCollectionChanged
{
...
Because you also have to notify if elements are moved / deleted / etc. This will cost some development effort if your family can do more than just Add.
Therefore it might be a good idea to change your
private readonly IList<Member> _members;
into an ObservableCollection<Member>, and implement the interface via this ObservableCollection.
class Family : INotifyPropertyChanged, INotifyCollectionChanged
{
public string LastName { get; private set; }
private readonly ObservableCollection<Member> members;
public IEnumerable<Member> Members => this.members;
public event NotifyCollectionChangedEventHandler CollectionChanged
{
add => this.members.NotifyCollectionChangedEventHandler.CollectinChanged += value;
remove => this.members.NotifyCollectionChangedEventHandler.CollectinChanged -= value;
}
Now your Add / Remove / Replace / Move / etc methods will be one-liners; the appropriate event will be raised.
public void Add(Member member)
{
this.members.Add(member);
}
public void Remove(Member member)
{
this.members.Remove(member);
}
Not sure if you need methods to Move and Replace family Members, but even if you need to, they will also be one-liner calls to the corresponding ObservableCollection method
You have completely hidden that you are using an ObservableCollection<Member>, so if in future you need to completely get rid of the ObservableCollection, your users won't have to change, as long as you promise to implement the interfaces.

Binding to dictionary, memory leak is reported, how to fix?

dotMemory tells me (screenshot below, "WPF binding leak") what there is a memory leak when binding to dictionary like this:
<ComboBox ItemsSource="{Binding Items, Mode=OneTime}"
DisplayMemberPath="Value"
SelectedValue="{Binding SelectedItem}"
SelectedValuePath="Key" />
Question 1, to everyone: why is it a memory leak (namely what scenario should I use to run into problems) and how to fix it?
Queston 2, to dotMemory experts: why so basic mvvm application (see below) has so many problems reported? Should I fix those problems? How?
MCVE (create new WPF solution, use above code in xaml) code behind:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
DataContext = new ViewModel();
}
}
public class ViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChanged([CallerMemberName] string property = "") =>
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(property));
public Dictionary<string, string> Items { get; } = new Dictionary<string, string>
{
{ "1", "One" },
{ "1a", "One and a" },
{ "2a", "Two and a" },
};
string _selectedItem = "1a";
public string SelectedItem
{
get { return _selectedItem; }
set
{
_selectedItem = value;
OnPropertyChanged();
}
}
}
Binding target objects that do not implement the
INotifyPropertyChanged interface or do not use the OneTime binding
mode
Answer 1:
Xaml is bound to Dictionary which is a collection of KeyValuePair and Value property of it is specified as a source for DisplayMemberPath.
KeyValuePair which is exposed doesn't implement INotifyPropertyChanged interface and there is no way to specify OneTime binding mode for DisplayMemberPath. So, all items of Dictionary will stay in memory forever.
Answer 2:
dotMemory reports potential problems, only you can determine if it is a real problem or not.
Unfortunately .NET itself makes string duplicates and creates array which is never will be filled with data, dotMemory reports them too because can't distinguish if these objects created by "user" or by system.
I would recommend you to see why do you have finalized objects, it seems that you forget to call IDisposable.Dispose method for some objects. And check if these not filled arrays created by you or not.
The reason you are getting a Memory leak is that you are binding to an object that doesn't implement the interface INotifyPropertyChanged.
When we bind to an dictionaries' Value property...
the binding target
starts listening for property change notifications. If the property is
not a DependencyProperty or an object that implements
INotifyPropertyChanged, WPF will resort to subscribing to the ValueChanged event of the System.ComponentModel.PropertyDescriptor class to get notifications when the source object’s property value changes.
Why is this a problem? Well, since the runtime creates a reference to
this PropertyDescriptor, which in turn references our source object,
and the runtime will never know when to deallocate that initial
reference (unless explicitly told), both the PropertyDescriptor as
well as our source object will remain in memory.
(source)
This is solved by binding to an ObservableDictionary<Key, Value>

Transferring Textbox data to my ViewModel

I have two textboxes with userinput, of which I need to transfer the data to my ViewModel. I tried looking around how to do this by binding it to a button (as the transfer is supposed to take place upon a buttonclick), but most advice to use bindings. However, to use bindings you have to declare properties in the ViewModel (afaik), but as these strings are used to create a new object, holding properties for them would be all but ideal because the two textboxes might expand to over 10 in the future. I've also tried messing around with CommandParameter but I only seem to be able to declare one.
So for clarification:
How do I transfer the contents of two (or more) textboxes to the corresponding ViewModel so I can create a new Object with them?
Edit:
In addition I'd also like to be able to reset the Text= field to be empty once the method handling the data has succesfully completed.
The View
<TextBox Name="UI1"/>
<TextBox Name="UI2"/>
<Button Source="*ImageSource*" Command="{Binding CallCreateObject}"/>
and the ModelView
private void OnCallCreateObject()
{
Object newObject = new Object(UI1, UI2, false)
}
This is a general example of what I'm trying to achieve
If you want to insert data from UI to ViewModel on Button Click than there is no reason to use binding. Binding is mainly used to sync data between UI and underlying models.
Still if you want only that then on button_click event you can do something like this.
private void button_Click(object sender, RoutedEventArgs e)
{
Model model = new Model();
model.Property1 = textBox1.Text;
model.Property2 = textBox2.Text;
textBox1.Text = string.Empty;
textBox2.Text = string.Empty;
}
That will solve your issue. But this approach is not recommended when you have a better thing that is called 'Binding'
If you want to bind your view with a viewmodel then try this:
Your view model:
public class Person : INotifyPropertyChanged
{
private string name;
// Declare the event
public event PropertyChangedEventHandler PropertyChanged;
public string PersonName
{
get { return name; }
set
{
name = value;
// Call OnPropertyChanged whenever the property is updated
OnPropertyChanged("PersonName");
}
}
// Create the OnPropertyChanged method to raise the event
protected void OnPropertyChanged(string name)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(name));
}
}
}
Great, you have set up your view model. Now the view:
XML PersonView.xml:
<Grid Name="MyContainer">
<TextBox Text="{Binding PersonName}" />
<Button Name="SaveInfoButton" OnClick="SaveInfoButton_Click">Save info</Button>
</Grid>
Now that we have indicated with which property the textbox will be bind, lets indicate to the view the model that will use to update the property named PersonName. The idea is that when you click over the button, the property PersonName of our model Person gets updated with the value of the TextBox.
The xml class:
public partial class PersonView : UserControl
{
private readonly Person Model;
public PersonView()
{
//Components initialization, etc. etc...
this.Model = new Person();
this.DataContext = this.Model; // Here we are binding the model with our view.
}
private void SaveInfoButton_Click(object sender, RoutedEventArgs e)
{
MessageBox.Show(this.Model.PersonName); // this will print the value of your textbox.
}
}
Dont know if you noticed, but we didnt have the need of creating a new object when the user click the button. We just use our model and update the model properpies. If you add more textbox to your view, you'll have to added to our viewmodel as well as the given example.
Here is some post that can help you a little bit more(dont have enough time)
http://blog.scottlogic.com/2012/04/20/everything-you-wanted-to-know-about-databinding-in-wpf-silverlight-and-wp7-part-two.html
http://www.tutorialspoint.com/wpf/wpf_data_binding.htm
You could use bindings like this:
<TextBox Name="UI1" Text="{Binding Path=Ut1Value}"/>
<TextBox Name="UI2" Text="{Binding Path=Ut2Value}"/>
<Button Source="*ImageSource*" Command="{Binding CreateTheThingCommand}"/>
Then in your viewmodel you'll need to have the properties and command for those:
private string _ut1Value;
private string _ut2Value;
public string Ut1Value
{
get
{
return _ut1Value;
}
set
{
if (_ut1Value!= value)
{
_ut1Value= value;
OnPropertyChanged("Ut1Value");
}
}
}
public string Ut2Value
{
get
{
return _ut2Value;
}
set
{
if (_ut2Value!= value)
{
_ut2Value= value;
OnPropertyChanged("Ut2Value");
}
}
}
public ICommand CreateTheThingCommand
{
get { return new RelayCommand(CreateTheThing); }
}
private void CreateTheThing()
{
Object newObject = new Object(_ut1Value, _ut2Value, false);
// Do whatever with your new object
}
It sounds as if you need at least two ViewModel objects:
One to present the data from an existing object. This would be, essentially, what you have already.
A container ViewModel. This encapsulates the behaviours of the IEnumerable collection of objects, including the functionality required to Add a new object.
The container ViewModel would have the properties that you are struggling with, plus the CreateObject command, along with an IEnumerable (ObservableCollection) property to hold the existing ViewModel objects.
In your View, you would have one control to present the data in an existing ViewModel object, and a second control with a ListView (or similar) control to display the existing view controls and the set of TextBox controls, plus the button to create a new object (and add it to the list).
This would also allow you to add 'remove', 'sort', etc. functionality to the container ViewModel, without having to change the existing ViewModel.
A way to accomplish a scalable solution with minimal lines of code, would be to create hold a list of items you bind to in the view model.
This way you can use an ItemsControl in the UI to display a textbox for each item:
public class ViewModel
{
public List<Item> Items {get;} = new List<Item>
{
new Item { Value = "UI1" },
new Item { Value = "UI2" },
};
public class Item
{
public string Value {get;set;}
}
}
View:
<ItemsControl ItemsSource="{Binding Test}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<TextBox Text="{Binding Value}" Margin="5"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
<Button Content="Commit" Margin="5" Click="ButtonBase_OnClick"/>
You can then create the object either from a click event or command:
private void OnCallCreateObject()
{
Object newObject = new Object(Items[0], Items[1], false);
}
The downside is that the order of the items is not explicit, so either you need to assume that the indexed order is correct, or order them manually.

WPF bindings do not pick up any changes

In my WPF application, I have some properties which I have bound to the XAML counterpart, but for some reason do not get set whenever their values change. I have implemented the INotifyPropertyChanged interface as well as set my DataContext for this View too, and it is still not picking up any changes.
I have this same pattern for other properties within this ViewModel which do work, while others don't.
Here is a snippet of my current code:
ViewModel
public class TestViewModel : INotifyPropertyChanged
{
private string testString;
public TestViewModel()
{
.....
this.RunCommand = new RelayCommand(this.RunAction);
}
public string TestString
{
get
{
return this.testString;
}
set
{
this.testString = value;
this.OnPropertyChanged("TestString");
}
}
private void RunAction()
{
.....
this.testString = "Running.";
}
}
View
<StatusBarItem>
<TextBlock Text="{Binding Path=TestString, UpdateSourceTrigger=PropertyChanged, Mode=OneWay}" />
</StatusBarItem>
DataContext (set in code-behind of another MainWindow class)
var testViewModel = SimpleIoc.Default.GetInstance<TestViewModel>();
var testWindow = new TestWindow() { DataContext = testViewModel };
testingWindow.Show();
If it helps, this is part of a multi-windowed application which uses MVVM-Light to pass properties between classes.
You are not changing the value of the TestString, you are assigning a command to change the value but you do not seem to be executing it.
this.RunCommand = new RelayCommand(this.RunAction);
Bind that command to something or execute it manually from somewhere.
Also you need to assign the property not the field
this.TestString = "Running.";
I found the problem. You are only updating the private property testString. But you do not update the property TestString so the notify is never called.
Try this:
this.TestString = "Running";

Binding with custom object in MVVM textbox

I have started learning MVVM with some basic applications and I just encountered below issue with binding.
I have 2 textboxes in my View say- Student_name and Student_year. I have a Student class implemented in my viewmodel with its properties. But, the actual Student class is in my Model layer.
<TextBox x:Name="StuName"
Text="{Binding Path=MyStudent.Name, Mode=TwoWay,
UpdateSourceTrigger=PropertyChanged}" />
<TextBox x:Name="StuYear"
Text="{Binding Path=MyStudent.Year, Mode=TwoWay,
UpdateSourceTrigger=PropertyChanged}" />
ViewModel:
private Student _myStudent = new Student();
public Student MyStudent
{
get { return _myStudent ; }
set
{
if (value != _myStudent )
{
_myStudent = value;
OnPropertyChanged("MyStudent");
}
}
}
Model (Student Class):
public string Name
{
get { return _name; }
set
{
if (_name!= value)
{
_name= value;
OnPropertyChanged("Name");
}
}
}
I can see everything working fine on binding the values from VM to View. But, the other way is behaving little tricky here..
Wheneven I change Name/Year in the textbox, the control has to land on Viewmodel's Set property? Rather, it straight away goes to Model's Set property.
For Instance, When I modify txtbox 'StuName', SET method of Student class is invoked. But not SET method of Viewmodel(MyStudent object).
I am not sure why this behaves in such a way. Is it because I have directly bounded Student.Name to the textbox? What are the alternatives to handle this SET operation in Viewmodel class..
Thanks in advance.
PS: I have implemented INotifyPropertyChanged interface properly and rest other bindings(of primitive data type) are working fine with other controls.
As Philip Stuyck correctly pointed out in his answer, the ViewModel only has a setter for the Student instance, which never changes. So the setter on the ViewModel is never invoked. The binding goes to the name property of that instance.
A different approach would be to wrap the name property in you ViewModel explicitly. This allows for a clearer separation of concerns between Model and ViewModel. I.e. right now your Model implements INotifyPropertyChanged which IMO belongs into the ViewModel, because in general it is only used for triggering View updates. Your ViewModel would look like this:
class StudentViewModel
{
private Student _myStudent = new Student();
public string Name
{
get { return _myStudent.Name ; }
set
{
if (value != _myStudent.Name )
{
_myStudent.Name = value;
OnPropertyChanged("Name");
}
}
}
}
Your Model on the other hand becomes simpler, because it doesn't have to implement INotifyPropertyChanged anymore.
That is normal behavior because your binding is to MyStudent.Name.
So the Mystudent setter is never called because the instance never changes.
The setter of the name is called because in fact that is where your binding is going to.

Categories