Pass Data to Model without triggering PropertyChanged-Event - c#

I am new to MVVM and am writting a little test-application using it.
I've got a Model, which represents the data structure -
a ViewModel and a View (parent class is Page), too.
Now i would like to pass some initial data to the Model,
so the window could show me these.
In a sample application of David Anderson, he passes the data
as method-parameter, which is actually the right way and causes
not the trigger of the PropertyChanged-Event, but my Model-class
is quite "fat" - it gots a lot of properties (> 30).
So, how shall i realize it in this case?
I don't think a method with more then 30 parameters is the right
way to handle that. Or am i wrong?
Does someone has an idea, how professionals realize this?
Here is my used code:
View (PersonPropertiesView is a subclass of the Page-class)
XAML
<TextBlock Text="{Binding Person.ID, UpdateSourceTrigger=PropertyChanged}" />
Code-Behind
public PersonPropertiesView()
{
InitializeComponent();
this.DataContext = new PersonPropertiesViewModel();
}
ViewModel (PersonPropertiesViewModel)
Code
private Person _Person;
public Person Person
{
get
{
return this._Person;
}
}
public Person()
{
this._Person = new Individual();
this._Person.ID = 12;
}
Model (Person, inherits INotifyPropertyChanged)
private long _ID;
public long ID
{
get
{
return this._ID;
}
set
{
if (this._ID != value)
{
this._ID = value;
OnPropertyChanged("ID");
}
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string propertyName)
{
if (propertyName != null)
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
If i try to compile this code, i get the System.Reflection.TargetInvocationException exception. Does someone knows why?

My first reaction would be: why? I'm not sure this is too important - when setting these properties immediately after construction, I would imagine it's fairly likely that this occcurs before the ViewModel is bound to any View (though without the context, I couldn't be sure).
The cost of attempting to fire the PropertyChanged event with no observers is likely too small to measure.
In the code you've posted, this code will throw an exception when there are no listeners:
protected void OnPropertyChanged(string propertyName)
{
if (propertyName != null)
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
You need to check if the event is null, not the property name:
protected void OnPropertyChanged(string propertyName)
{
var handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}

Related

WPF: UI not updated on value change in bound object

I am relatively new to WPF and having a problem with data binding. I am binding a dependency property of a user control to a class property in my code behind. During intantiation of the class entity in my code behind the UI is sucessfully updated through INotifyPropertyChanged. However when subsequently changing the value in my code behind the OnPropertyChangedEventHandler fires, but the OnPropertyChanged method does no longer answer to this. Below the details. It would be great if someone could give me some hints what I am doing wrong.
I implemented a user control that I am binding to a property CurrentAccProp.DiscountRate of my partial class in code behind:
<local:doubleUEdit x:Name="InterestRate" LabelField="Interest rate" MinimumValue="0" MaximumValue="1" FormatStringForNumbers="P2" IncrementSize="0.01" UncertainValue="{Binding ElementName=RibbonWindow, Path=CurrentAccProp.DiscountRate, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
The class of which CurrentAccProp is an instance implements INotifyPropertyChanged to inform the UI about value changes
//Event to inform data grid about changes
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
OnPropertyChanged is called in the setter for the DiscountRate property:
doubleU discountingrate;
public doubleU DiscountRate
{
get {return discountingrate;}
set
{
discountingrate = value;
OnPropertyChanged("DiscountingRate");
}
}
The property of my user control that I am binding to is implemented as a dependency property:
//Property for data binding to doubleU
[Description("The formatstring for the double boxes"), Category("Default")]
public doubleU UncertainValue
{
get { return new doubleU(0, 0, (double)doubleUSupremum.Value, (double)doubleUSupremum.Value); }
set { doubleURangeSlider.LowerValue = value.Interval.Infimum; doubleURangeSlider.HigherValue = value.Interval.Supremum; doubleUInfimum.Value = value.Interval.Infimum; doubleUSupremum.Value = value.Interval.Supremum; }
}
public static readonly DependencyProperty UncertainValueProperty =
DependencyProperty.Register(
"UncertainValue",
typeof(doubleU),
typeof(doubleUEdit),
new PropertyMetadata(default(doubleU), OnItemsPropertyChanged));
private static void OnItemsPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
doubleUEdit MydblUEdt = d as doubleUEdit;
MydblUEdt.UncertainValue = e.NewValue as doubleU;
}
When I am instantiating CurrentAccProp in my code behind the OnPropertyChanged informs the UI and the value is updated.
AccountingProperties currentaccprop = new AccountingProperties(new doubleU(0.0));
public AccountingProperties CurrentAccProp { get { return currentaccprop; } set { currentaccprop = value; } }
However, when I later update the value of DiscountRate
CurrentAccProp.DiscountRate = new doubleU(1.0);
OnPropertyChanged gets executed, but the UI is no longer updated. Does anyone have a clue what I am doing wrong here?
The typo pointed out by HighCore and zaknotzach was indeed the problem. Thanks for your help! I implemented the approach in the thread referenced by HighCore to avoid this and it works like a charm. Below the changed AccountingProperties class from which CurrentAccProp is instantiated for reference:
public class AccountingProperties : INotifyPropertyChanged
{
doubleU discountrate;
public doubleU DiscountRate
{
get {return discountrate;}
set { SetField(ref discountrate, value, () => DiscountRate); }
}
//------------------------------------------------
//constructors
public AccountingProperties(doubleU discountrate)
{
DiscountRate = discountrate;
}
//Event to inform data grid about changes
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
}
protected virtual void OnPropertyChanged<T>(Expression<Func<T>> selectorExpression)
{
if (selectorExpression == null)
throw new ArgumentNullException("selectorExpression");
MemberExpression body = selectorExpression.Body as MemberExpression;
if (body == null)
throw new ArgumentException("The body must be a member expression");
OnPropertyChanged(body.Member.Name);
}
protected bool SetField<T>(ref T field, T value, Expression<Func<T>> selectorExpression)
{
if (EqualityComparer<T>.Default.Equals(field, value)) return false;
field = value;
OnPropertyChanged(selectorExpression);
return true;
}
}
You need to first change the string in
OnPropertyChanged("DiscountingRate");
to "DiscountRate". The string you are giving your OnPropertyChanged function must match the property name. That is most likely the issue you are having.
As already answered, the problem is OnPropertyChanged("DiscountingRate"); providing the event with an incorrect property name.
In order to prevent errors like this, you can avoid using string literals all together. In your OnPropertyChanged parameter, use CallerMemberName. You can modify your OnPropertyChanged signature to
public void OnPropertyChanged([CallerMemberName] string propertyName = "")
{
// Do your stuff
}
Then in your setters, you just call this.OnPropertyChanged();. The method will be given the property name that was changed.
public doubleU DiscountRate
{
get {return discountingrate;}
set
{
discountingrate = value;
OnPropertyChanged();
}
}
The benefit to this is that you can refactor your code and not worry about breaking your property changed events.

Class implementing INotifyPropertyChanged. WPF

)
Please have a look at the code below:
public class MyClass : INotifyPropertyChanged
{
private string _myName;
public string MyName
{
get { return _myName; }
set
{
if (_myName != value)
{
_myName = value;
OnPropertyChanged("MyName");
}
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
Object of this class is used as a DataContext for textBox control on WPF form.
Idea is simple: changes done to MyName property of this object should be automatically displayed in textBox. It works but I do not understand why.
Please follow my way of thinking and point out where I get it wrong...
We set a new value for MyName. Program goes to 'setter' and it calls 'OnPropertyChanged'. It goes to 'OnPropertyChanged' and checks if 'PropertyChanged' is null.
And it is not. And I do not know why since I do not add any event handlers to this event.
So... how does it work?
Thank you!
The Event Handler is added automatically when you bind the object to the DataContext.

MVVM pattern implementation

I want use MVVM pattern in the WP app. I have a some idea about this pattern. But I do not understand some things. I do not know whether it's good practice or not to do so.
So, I have Model.
Model is a data structure. The set of fields and properties.
Model
public class Person : INotifyPropertyChanged
{
private string name;
private GeoCoordinate coordinate;
public string Name
{
get
{
return name;
}
set
{
if (this.name != value)
{
this.name = value;
this.RaisePropertyChanged("Name");
}
}
}
public GeoCoordinate Coordinate
{
get
{
return this.coordinate;
}
set
{
if (this.coordinate != value)
{
this.coordinate = value;
this.RaisePropertyChanged("Coordinate");
}
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void RaisePropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = this.PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
}
ViewModel initializes the fields of the model.
ViewModel
public class PersonViewModel : INotifyPropertyChanged
{
public Person User
{
get;
private set;
}
public PersonViewModel()
{
this.User = new Person();
}
public LoadData()
{
Service.GetUser((result) =>
{
this.User.Name = result.Name;
this.User.Coordinate = result.Coordinate;
});
}
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(String propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (null != handler)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
}
View
PersonViewModel _viewModel;
this.DataContext = _viewModel;
_viewModel.LoadData();
Here are the moments that I would like to clarify:
How can ViewModel notify View about error in loading data, end loading?
Can I pass part of date to View (without databinding, technically it is possible, I mean, this is permissible under the pattern)?
For exaple, in ViewModel:
public LoadData(Action<Person, Exception> act)
{
Service.GetUser((result, error) =>
{
if (error != null)
{
act.Invoke(null, error);
}
else
{
this.User.Name = result.Name;
this.User.Coordinate = result.Coordinate;
act.Invoke(result, null);
}
});
}
In View:
_viewModel.LoadData((result, error) =>
{
if (error != null)
{
//error data loading
}
else
{
//successfully loading
}
});
This is terrible, probably this approach destroys the whole concept. But, for example, I work with Jeff Wilcox static map.
<jwMaps:StaticMap
Provider="Bing"
Visibility="Visible">
<jwMaps:StaticMap.MapCenter>
<geo:GeoCoordinate
Latitude ="50"
Longitude="50" />
</jwMaps:StaticMap.MapCenter>
</jwMaps:StaticMap>
I can't binding coordinate to this control. I tried, dont't work. If use
StaticMap.MapCenter =
new GeoCoordinate() { Latitude = user.Latitude, Longitude = user.Longitude };
then works.
In the case of a delegate I could do it in a successfully branch...
Please help advice.
you can send messages from your viewmodel to your view for that you can use Messenger class of mvvm light.
And i will not broke the MVVM pattern Because What MVVM pattern is that you have to do your logic part in ViewModel but it does not mean that you can not use Your Page.xaml.cs Code behind.you can make decisions on visibility or any UI related Code in your Code behind.Also it is not a Hard Coded Pattern actually it is made to organize your project in much better and simpler way but if you have to communicate between view and your viewModel for some Reason that you can not solve from viewmodel than you can use you codebehind.
I am not an expert but this is what i think..hope some updates on this topic..

creating a ViewModel class

I would like to create a ViewModel Class to retrieve the values from the Database.
My goal is to retrieve the Values of Usage of RAM (Ram total & Ram available) from my DB Table and then display it on my View.
This is what I have done so far on my ViewModel Class
public class RamViewModel : INotifyPropertyChanged
{
float _ramTotal;
float _ramUsed;
public float RamTotal
{
get { return _ramTotal; }
set { _ramTotal = value; RaisePropertyChanged("RamTotal"); }
}
public float RamUsed
{
get { return _ramUsed; }
set { _ramUsed = value; RaisePropertyChanged("RamUsed"); }
}
private void RaisePropertyChanged(string p)
{
throw new NotImplementedException();
}
}
when I build the class, I got this error stating, " ViewModel.RamViewModel Does not implement interface member 'System.ComponentModel.INotifyPropertyChanged.PropertyChanged'"
How to overcome this error
INotifyPropertyChanged is an interface with one member that needs to be included in your class definition:
public event PropertyChangedEventHandler PropertyChanged;
You should also change the code in RaisePropertyChanged to not throw an exception, by implementing the actual functionality:
private void RaisePropertyChanged(string p)
{
if (null != PropertyChanged) PropertyChanged(this, new PropertyChangedEventArgs(p));
}
Your class does not expose the PropertyChanged event, which is necessary for classes that implement INotifyPropertyChanged (it's the only member of that interface).
So you should add:
public event PropertyChangedEventHandler PropertyChanged;
private void RaisePropertyChanged(string propertyName)
{
var handler = this.PropertyChanged;
if (handler != null) {
handler(this, new PropertyChangedEventArgs(propertyName);
}
}
ObservableCollection is unrelated to this.

Calling OnPropertyChange("property_name") Sends Updates to All Bound Properties

So I have a class with 40 or so properties that are updated from communication with a micro controller. This class implements INotifyPropertyChanged.
Loose Example:
private int _Example;
public int Example
{
get
{
return _Example;
}
set
{
_Example = value;
OnPropertyChange("Example");
}
}
And the OnPropertyChange function:
protected void OnPropertyChange(string p_Property)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(p_Property));
}
}
public event PropertyChangedEventHandler PropertyChanged;
Binding (many of these)
Second_Class_Control.DataBindings.Clear();
Second_Class_Control.DataBindings.Add("My_Property", FirstClass, "Example");
In the main form I've set up binds to display and react to these values. One of those happens to land on another property in a another class. I happened to place a breakpoint in the set function of this property, and noticed it was being called any time any property from the first class changed.
Is this the correct behavior? I don't notice any performance hits but I plan on having many instances of these classes running together and wasn't expecting this.
Thanks
Hmm.. I noticed that you have the your OnPropertyChange virtual. Why is this, are you making a override somewhere?
I usually creates it like this :
public class ViewModelBase : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string propertyName)
{
if (this.PropertyChanged != null)
{
this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
then for the usage :
public class MainWindowViewModel : ViewModelBase
{
private string name;
public string Name
{
get { return name; }
set { name = value; OnPropertyChanged("Name"); }
}
}

Categories

Resources