How to communicate with ViewModel from Model - wpf

I am quit new to MVVM. So please correct me if I am doing any mistake in implementing MVVM.
In my Model class there are two properties Price and IsChecked.
public int Price { get; set; }
public static int _total;
bool _isChecked;
public bool IsChecked
{
get
{
return _isChecked;
}
set
{
_isChecked = value;
if (value == true)
{
_total+= this.Price;
}
else
{
_total-= this.Price;
}
}
}
In My ViewModel Class there is a property of Type List <Model > and it is bounded to datagrid in view and another property is Total, which bounded to a textBlock in View.
public int Total
{
get
{
return DocumentStoreModel._total;
}
set
{
}
}
DataGrid has a checkBox column and it is bounded with Ischecked property
<DG:DataGridCheckBoxColumn Header="Select" Binding="{Binding IsChecked, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" ></DG:DataGridCheckBoxColumn>
Now, if user checked the Checkbox from checkbox column in DataGrid then total should be reflected in View.
My ViewModel class is implementing the INotifyPropertyChanged interface.
My qus is,if my model's property is changing how i can tell it to my viewModel ?
please let me know How i can achieve this.

From what I can tell your ViewModel's Total property is pointing to your Model's Total property so any changes you make to the Model's total will essentially be changed for the ViewModel as well.
What you might have to do though is raise the PropertyChanged event for the Total property on the ViewModel when IsChecked is changed. This will tell your View to update the data for your total text block.

As you are exposing your Model within your ViewModel, then you need to implement INotifyPropertyChanged in your Model. You however have a problem in that your Total property is static and (afaik) you cant use INotifyPropertyChanged for static properties.
I would suggest you create a custom event on your model that can be subscribed to on your ViewModel. Here's an example (you might want to tidy it up a bit).
public class Model : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
internal static event EventHandler TotalChanged;
internal static int Total { get; private set;}
private int price;
public int Price
{
get { return price; }
set
{
price = value;
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs("Price"));
}
}
private bool isChecked;
public bool IsChecked
{
get { return isChecked; }
set
{ isChecked = value;
if (value)
Total += Price;
else
Total -= Price;
if (TotalChanged != null)
TotalChanged(this, EventArgs.Empty);
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs("IsChecked"));
}
}
}
public class MainViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public int Total
{
get { return Model.Total; }
}
public MainViewModel()
{
Model.TotalChanged += TotalChanged;
}
private void TotalChanged(object sender, EventArgs e)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs("Total"));
}
}

You need to implement a Command (by implementing ICommand or variants of it) and expose as property on the ViewModel and bind it on the view for the event on the checkbox.

Related

Correct way to update property in ViewModel from Model

I'm fairly novice with WPF. It's my understanding that data changes in the model, and it should notify the viewmodel, and the view will bind to properties and things alike in the viewmodel. Is this correct? If so, I've been reading that the model should implement INotifyPropertyChanged, and look something like this
public class LoginModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public void NotifyPropertyChanged(string propName)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propName));
}
public bool Authenticated { get; set; }
}
and in my ViewModel, I have a property "AuthResult", that should get the update from the Model property "Authenticated"
public partial class view1 : UserControl, INotifyPropertyChanged{
public bool AuthResult
{
get
{
return _authVal;
}
set
{
_authVal = value;
NotifyPropertyChanged("AuthResult");
}
}
public event PropertyChangedEventHandler PropertyChanged;
public void NotifyPropertyChanged(string propName)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propName));
}
}
I know this current implementation is incorrect. I've found that I should be subscribing to the PropertyChanged notification from my model like so:
LoginModel.PropertyChanged += new System.ComponentModel.PropertyChangedEventHandler(LoginModel_PropertyChanged);
void LoginModel_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
{
if(e.PropertyName == "Authenticated")
{
//do something
}
}
I don't see where the "AuthResult" property should be updated. Would I do something in the If statement like AuthResult = _model.Authenticated;?
EDITED:
and in my constructor?
LoginModel _model;
public view1(LoginModel model)
{
_model = model;
InitializeComponent();
}
If the model implements the INotifyPropertyChanged interface you can bind directly to it from the view:
<Button Content="Button" IsEnabled="{Binding Authenticated}" />
Note that the LoginModel class must raise the PropertyChanged event whenever the Authenticated property is set to a new value.
You could also expose the entire model entity through the view model class:
public class ViewModel
{
public ViewModel(LoginModel model)
{
Model = model;
}
public LoginModel Model { get; }
}
...and bind to it like this:
<Button Content="Button" IsEnabled="{Binding Model.Authenticated}" />
It is still the model class that must implement the INotifyPropertyChanged interface and raise change notifications.
Another option is for the view model to wrap any property of the model class that you want to be able to bind to from the view. Then you bind to a property of the view model class that in turn wraps a property of the model class something like this:
public class ViewModel
{
private readonly LoginModel _model;
public ViewModel(LoginModel model)
{
_model = model;
}
public bool AuthResult
{
get
{
return _model.Authenticated;
}
set
{
_model.Authenticated = value;
NotifyPropertyChanged("AuthResult");
}
}
}
<Button Content="Button" IsEnabled="{Binding AuthResult}" />
The benefit of using this latter approach is that view has no dependency upon the model class. It binds to the view model class only and this is how the MVVM design pattern typically is meant to be implemented.
But if you do bind to a (wrapper) property of the view model and want the view to be updated whenever a property of the model class is set, the model has to notify the view model that it has changed one way or another, i.e. it has to raise some kind of event or similar. And this typically means implementing the INotifyPropertyChanged interface. The view model can then subscribe to the PropertyChanged event of the model and raise its own PropertyChanged event for the data bound property whenever the model is updated, e.g.:
public class ViewModel
{
private readonly LoginModel _model;
public ViewModel(LoginModel model)
{
if (model == null)
throw new ArgumentNullException("model");
_model = model;
_model.PropertyChanged += OnModelChanged;
}
private void OnModelChanged(object sender, PropertyChangedEventArgs e)
{
if (e.PropertyName == "Authenticated")
NotifyPropertyChanged("AuthResult");
}
public bool AuthResult
{
get
{
return _model.Authenticated;
}
set
{
_model.Authenticated = value;
NotifyPropertyChanged("AuthResult");
}
}
}
Just use Model as member in the ViewModel
public class ViewModel
{
private Model _myModel;
public Model MyModel
{
get { return _myModel; }
set
{
if (Equals(_myModel, value)) return;
_myModel = value;
NotifyPropertyChanged(nameof(MyModel));
}
}
}
Then in xaml you can bind properties of Model
<CheckBox IsChecked="{Binding MyModel.Authenticated}" />
With this approach your ViewModel will be "build" around your Model.
In case you don't want that models implement INotifyPropertyChanged than create a "Facade" class of model in use it in same way as previous example.
public class ModelFacade : INotifyPropertyChanged
{
private Model _myModel;
public bool Authenticated
{
get { return _myModel.Authenticated; }
set
{
_myModel.Authenticated = value;
NotifyPropertyChanged(nameof(Authenticated));
}
}
}
public class ViewModel
{
private ModelFacade _myModel;
public ModelFacade MyModel
{
get { return _myModel; }
set
{
if (Equals(_myModel, value)) return;
_myModel = value;
NotifyPropertyChanged(nameof(MyModel));
}
}
}

ListBox bind to ObservableCollection is not updated with collection

I have next model:
public class MyModel
{
public ObservableCollection<MyObject> MyList {get; set;}
}
public class MyObject
{
MyObservableDictionary MyDictionary {get; set;}
}
public class MyObservableDictionary : ObservableCollection<EnymValue>
{
}
public class EnymValue : INotifyPropertyChanged
{
private MyEnum key;
private string value;
public MyEnum Key
{
get
{
return this.key;
}
set
{
this.key = value;
NotifyPropertyChanged("Key");
}
}
public string Value
{
get
{
return this.value;
}
set
{
this.value = value;
NotifyPropertyChanged("Value");
}
}
public LanguageValue(MyEnum key, string value)
{
this.Key = key;
this.Value = value;
}
public event PropertyChangedEventHandler PropertyChanged;
public void NotifyPropertyChanged([System.Runtime.CompilerServices.CallerMemberName]string propertyName = "")
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
public enum MyEnum
{
}
And on View I have a ListBox:
<ListBox x:Name="MyList" SelectionMode="Single" ItemsSource="{Binding Path=MyList, Mode=OneWay}">
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Path=MyDictionary, Mode=OneWay, Converter={StaticResource myEnumToTextConverter}}" />
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
(myEnumToTextConverter converter is just selects first element from collection and return it's value, or some specified constant if collection is null or empty)
I want my Model's list box to be updated on view, when any EnymValue values are changed.
Is it possible somehow to implement this?
Currently the view is not updated when Value changed.
I've tried to inherit EnymValue from INotifyPropertyChanged, but this didn't helped. Looks like PropertyChanged == null on EnymValue.NotifyPropertyChanged when property updated.
ObservableCollection is able to notify UI about changes when collection itself is changed(elemends are added or deleted). But ObservableCollection is not aware of changes that are happening when you modify one of it's items. To solve the problem you may subscribe to CollectionChange event of observable collection, and when new item is added, subscribe to new items's PropertyChanged. When PropertyChanged event is raised, you can trigger notification on your list OnPropertyChanged(()=>MyItems); You should be careful implementing this solution and remember to unsubscribe from the event's to avoid memory leaks.
An example of what I mean you can see in this answer.
Your MyDictionary should force a refresh. Easiest way is to re-assign its old value, and implement INPC in MyObject like below :
public class MyObject: INotifyPropertyChanged
{
MyObservableDictionary _myDictionary;
public MyObservableDictionary MyDictionary {
get
{
return _myDictionary;
}
set
{
_myDictionary = value;
OnPropertyChanged("MyDictionary");
}
}
public MyObject()
{
MyDictionary = new MyObservableDictionary();
}
public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChanged(string prop)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(prop));
}
}
Sample code to change Value :
private void Button_Click(object sender, RoutedEventArgs e)
{
// vm is ViewModel instance, vm is DataContext set for Window
var old = vm.MyList[0].MyDictionary;
vm.MyList[0].MyDictionary[0].Value = "aaaa";
vm.MyList[0].MyDictionary = old;
}
I tested this, and it displays changed value as "aaaa".

WPF binding a combobox to an array/list of business objects

I'm building a WPF application and my UI consists of combobox and about a dozen other UI controls. I have a single business object class that contains a dozen or so properties and implements INotifyPropertyChanged.
Here's a snippet of my business object:
public class MyBusinessObject : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(PropertyChangedEventArgs e)
{
if (PropertyChanged != null)
{
PropertyChanged(this, e);
}
}
private int _idNumber;
public int IdNumber
{
get { return _idNumber; }
set
{
if (_idNumber == value)
{
return;
}
_idNumber = value;
OnPropertyChanged(new PropertyChangedEventArgs("IdNumber"));
}
}
private string _name;
public string Name
{
get { return _name; }
set
{
if (_name == value)
{
return;
}
_name = value;
OnPropertyChanged(new PropertyChangedEventArgs("Name"));
}
}
private int _age;
public int Age
{
get { return _age; }
set
{
if (_age == value)
{
return;
}
_age = value;
OnPropertyChanged(new PropertyChangedEventArgs("Age"));
}
}
private string _address;
public string Address
{
get { return _address; }
set
{
if (_address == value)
{
return;
}
_address = value;
OnPropertyChanged(new PropertyChangedEventArgs("Address"));
}
}
private bool _employed;
public bool Employed
{
get { return _employed; }
set
{
if (_employed == value)
{
return;
}
_employed = value;
OnPropertyChanged(new PropertyChangedEventArgs("Employed"));
}
}
public MyBusinessObject(int idNumber)
{
this.IdNumber = idNumber;
// set default values here
}
}
As you might expect, the various UI controls will be bound to the properties of my business object. However, I need to create an array or list of business objects (10 of them to be specific) and bind my combobox to the IdNumber property. So my user will select the object that they want from the combobox and then the other UI controls should update to display the values for each of their bound properties for the selected object.
Right now, I just have one instance of my business object declared in my code behind like this:
public partial class MainWindow : Window
{
// this will be replaced with an array/list of business objects
MyBusinessObject myObject = new MyBusinessObject(1234); // 1234 = IdNumber
public MainWindow()
{
InitializeComponent();
this.DataContext = myObject;
}
}
And currently, my combobox is defined like this:
<ComboBox x:Name="selectedObjectComboBox" IsEditable="False"/>
Once I implement my array/list of business objects, can anyone tell me how I would bind the combobox to the array so that it will display the IdNumber for each object? Also, what, if anything, will I need to do to get the other bound controls to reflect the values of the selected object when the user changes their selection in the combobox?
You need to bind your ComboBox to your list, then use the DisplayMemberPath to specify which member you want displayed:
<ComboBox ItemsSource={Binding yourList} DisplayMemberPath="IdNumber"/>
If you want your other controls to update off of this value, you might want to consider making a SelectedItem property on your view model and bind the selected item to this. Then your other controls can bind to this.
EDIT
That can be achieved by doing something like:
<ComboBox ItemsSource={Binding yourList} SelectedItem={Binding SelectedBusinessObject} DisplayMemberPath="IdNumber"/>
If you don't want to make a backing field, you could do something like..
<ComboBox ItemsSource={Binding yourList} x:Name="BusinessComboBox" DisplayMemberPath="IdNumber"/>
<MyControl Item={Binding SelectedItem, ElementName=BusinessComboBox />

TreeView IsSelected property returns the ItemsSource collection as 'This' pointer

I have a TreeView which binds to a datasource like this
<TreeView ItemsSource="{Binding Data}" Width="190" >
JinkData is defined in my ViewModel as a property of a class. The property is defined like this
public Collection<JinkData> Data { get; set; }
I have a property IsSelected which tells me which node of the TreeView is currently selected and then I can use the 'this' pointer to get the selected node using the following code.
private static object _selectedItem = null;
// This is public get-only here but you could implement a public setter which also selects the item.
// Also this should be moved to an instance property on a VM for the whole tree, otherwise there will be conflicts for more than one tree.
public static object SelectedItem
{
get { return _selectedItem; }
private set
{
if (_selectedItem != value)
{
_selectedItem = value;
OnSelectedItemChanged();
}
}
}
static virtual void onselecteditemchanged()
{
// raise event / do other things
}
private bool _isSelected;
public bool IsSelected
{
get { return _isSelected; }
set
{
if (_isSelected != value)
{
_isSelected = value;
OnPropertyChanged("IsSelected");
if (_isSelected)
{
SelectedItem = this;
}
}
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propertyName)
{
var handler = this.PropertyChanged;
if (handler != null)
handler(this, new PropertyChangedEventArgs(propertyName));
}
The problem that I am facing is 'this' pointer in Set of IsSelected is the collection JinkData but I wanted this pointer to be the selectedJinkData insead of the whole collection. How can I get the currently selected JinkData from the TreeView?
How will I do that?

Two Layer Binding in Textbox is not writing

I think I may be using the DependencyObject incorrectly.
I have a generic class that implements the DependencyObject called Person with the properties FirstName and LastName.
public class Person : DependencyObject
{
public static readonly DependencyProperty FirstNameProperty =
DependencyProperty.Register("FirstName", typeof(string), typeof(Person));
public string FirstName
{
get { return (string)GetValue(FirstNameProperty); }
set { SetValue(FirstNameProperty, value); }
}
public static readonly DependencyProperty LastNameProperty =
DependencyProperty.Register("LastName", typeof(string), typeof(Person));
public string LastName
{
get { return (string)GetValue(LastNameProperty); }
set { SetValue(LastNameProperty, value); }
}
}
Then I have a xaml control with its datacontext set to my ViewModel class. Inside the ViewModel class I have a property called UserName that gets/sets a Person. The text box is bound to the UserName.FirstName property. It can populate the textbox correctly but can't seem to call the set when I enter characters and tab out. I think the issue is the two level property binding. For design reasons I need to access it through two levels of properties. Any suggestions?
Here is my xaml:
<TextBox Width="100" Margin="10,0,0,0" Text="{Binding Path=UserName.FirstName, Mode=TwoWay}" />
Here is my property in the view model class:
public Person UserName
{
get
{
return person;
}
set
{
person = value;
}
}
I've also tried it this way too:
public Person UserName
{
get
{
return person;
}
set
{
person.FirstName = value.FirstName;
}
}
Your property will not be called from the binding, the property is only there because of the pattern so it is easily visible from code.
The binding sets the dependency property directly.
Why do you want dependency properties in this situation? Dependency properties are relevant on controls - for your scenario use regular properties and INotifyPropertyChanged - the code will be simpler that way :)
If you do want notification when a dependencyproperty is changed you have to add a static eventhandler to the DependencyProperty.Register(...) call.
Agree with rune Andersen.
You should use INotifyPropertyChanged.
public class Person :INotifyPropertyChanged
{
private string _firstName;
public string FirstName
{
get { return _firstName; }
set { _firstName = value; OnPropertyChanged("FirstName"); }
}
private string _lastName;
public string LastName
{
get { return _lastName; }
set { _lastName = value; OnPropertyChanged("LastName"); }
}
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string name)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(name));
}
}
}

Resources