The XAML {Binding} construct is very handy because it handles all of the PropertyChanged issues automatically. It is really impressive when you hand it a path to an object, through .NET data structures, and everything is automatically updated for you.
I would like to use the same thing in C#. I would like to have a property that is derived from the value of another property. Example
class Foo
{
public Bar Bar = new Bar();
public string ItGetter
{
get
{
return Bar.Baz.It;
}
}
}
class Bar
{
public Baz Baz = new Baz();
}
class Baz
{
public string It { get { return "You got It!"; } }
}
If you call ItGetter on a Foo, you get the It value from Baz. This works fine, except that it is not invalidated--i.e., if It changed, there would be no change notifications on the ItGetter. Furthermore, if the Foo.Bar or Bar.Baz references are changed, you would also not get change noficiations.
I can add the appropriate IChangeNotify code on the properties, but my question is: How do I code the ItGetter property such that it will call its PropertyChanged event when any of the references in the path, or the It value change? I'm hoping I don't have to manually setup property changed events on all the items in the path....
Thanks for any help!
Eric
You could take a look at dependancy properties. They allow you to define properties in the WPF property system that are backed with stacks of metadata and a detailed value resolution system.
Importantly for you they allow they allow you to register for property changed events, and they allow you to make values dependant on other stuff.
There are some other good artcile around such as 'Demystifying dependency properties' by Josh Smith and 'Dependency Properties' by Christian Mosers
You might also want to read Dependency Property Callbacks and Validation
Here is the full code that does what I was looking for, using Dependency Properties, as Simon mentioned:
// This class exists to encapsulate the INotifyPropertyChanged requirements
public class ChangeNotifyBase : DependencyObject, INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string property)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(property));
}
}
public class Foo : ChangeNotifyBase
{
public Foo()
{
Bar = new Bar();
var binding = new Binding("Bar.Baz.It");
binding.Source = this;
binding.Mode = BindingMode.TwoWay;
BindingOperations.SetBinding(this, ItGetterProperty, binding);
}
/// <summary>
/// The ItGetter dependency property.
/// </summary>
public bool ItGetter
{
get { return (bool)GetValue(ItGetterProperty); }
set { SetValue(ItGetterProperty, value); }
}
public static readonly DependencyProperty ItGetterProperty =
DependencyProperty.Register("ItGetter", typeof(bool), typeof(Foo));
// Must do the OnPropertyChanged to notify the dependency machinery of changes.
private Bar _bar;
public Bar Bar { get { return _bar; } set { _bar = value; OnPropertyChanged("Bar"); } }
}
public class Bar : ChangeNotifyBase
{
public Bar()
{
Baz = new Baz();
}
private Baz _baz;
public Baz Baz { get { return _baz; } set { _baz = value; OnPropertyChanged("Baz"); } }
}
public class Baz : ChangeNotifyBase
{
private bool _it;
public bool It { get { return _it; } set { _it = value; OnPropertyChanged("It"); } }
}
If you now register for events on ItGetter, you will get notified if any of these things change:
Baz.It
Foo.Bar (I.e., change the reference)
Bar.Baz " "
If you set on of the object references (Foo.Bar or Bar.Baz) to null, the value of ItGetter changes to false.
Related
I'm trying to make a "Dirty" implementation with Catel.
I have a viewmodel, with a [Model] property and a few [ViewModelToModel] mapped to it.
And I added a boolean member _canGetDirty, that when set to true allows viewmodel properties to prompt a service for saving.
So my logic is that if the model property changes, _canGetDirty is set to false, so the viewmodel properties change without getting dirty, and when the model is done changing we set _canGetDirty to true anew.
The problem is that the PropertyChanged event for the model property is called before the viewmodel properties are changed, hence _canGetDirty is always true, and my service is called for saving whenever I load a new model.
How to work around this issue?
public class MyViewModel : ViewModelBase
{
private IMyService _myService;
private bool _canGetDirty;
public MyViewModel(MyModel myModel, IMyService myService)
{
MyModel = myModel;
_myService = myService;
}
[Model]
public MyModel MyModel
{
get { return GetValue<MyModel>(MyModelProperty); }
set
{
_canGetDirty = false;
SetValue(MyModelProperty, value);
}
}
[ViewModelToModel("MyModel")]
public string Prop1
{
get { return GetValue<string>(Prop1Property); }
set { SetValue(Prop1Property, value); }
}
[ViewModelToModel("MyModel")]
public string Prop2
{
get { return GetValue<string>(Prop2Property); }
set { SetValue(Prop2Property, value); }
}
[ViewModelToModel("MyModel")]
public string Prop3Contains
{
get { return GetValue<string>(Prop3Property); }
set { SetValue(Prop3Property, value); }
}
#region Registering
public static readonly PropertyData Prop1Property = RegisterProperty("Prop1", typeof(string), null, PropertyToSaveChanged);
public static readonly PropertyData Prop2Property = RegisterProperty("Prop2", typeof(string), null, PropertyToSaveChanged);
public static readonly PropertyData Prop3Property = RegisterProperty("Prop3", typeof(string), null, PropertyToSaveChanged);
public static readonly PropertyData MyModelProperty = RegisterProperty("MyModel", typeof(MyModel), null, MyModelChanged);
#endregion
#region Property Changed Handlers
private static void MyModelChanged(object sender, PropertyChangedEventArgs e)
{
(sender as MyViewModel)._canGetDirty = true;
}
private static void PropertyToSaveChanged(object sender, PropertyChangedEventArgs e)
{
var vm = sender as MyViewModel;
if (vm._canGetDirty)
vm._myService.AskForSaving();
}
#endregion
}
Edit: some explanation on how Catel works in this context.
Registered properties changes:
We register properties that will notify updates with RegisterProperty:
public static readonly PropertyData Prop1Property = RegisterProperty("Prop1",
typeof(string), null, PropertyToSaveChanged);
The last parameter is a callback function called when the registered property changes.
Automatic update of model's properties:
We set a property as a Model:
[Model]
public MyModel MyModel
{
get { return GetValue<MyModel>(MyModelProperty); }
set
{
_canGetDirty = false;
SetValue(MyModelProperty, value);
}
}
This class contains a few properties (Prop1, Prop2, Prop3). Catel allows us to automatically update them from the viewmodel by mapping them with ViewModelToModel:
[ViewModelToModel("MyModel")]
public string Prop1
{
get { return GetValue<string>(Prop1Property); }
set { SetValue(Prop1Property, value); }
}
First of all, I recommend using Catel.Fody, that simplifies your property registration a lot (and yes, it also supports change callbacks ;-) ).
Why would the model externally change? When the model changes (which gets injected into your ctor), it should recreate a new VM and thus you can start with a new slate.
Back to this issue: did you test whether your setter is actually being called? It could be possible that Catel internally directly calls SetValue (equal to the behavior of dependency properties) instead of calling the wrapper in your vm. Catel works this way:
You update the model
The change callback calls (so you set _canBeDirty => true)
The vm notices that the model has been changed and updates the exposes / linked properties.
My suspicion is basically that you are setting _canBeDirty => true too early.
Assuming that ViewModelBase adheres to INotifyPropertyChanged subscribe to the classes' INotifyPropertyChanged event and set the dirty flag there instead of subscribing to individual change events.
By definition that should happen after any value is set.
Example as
public MyViewModel(MyModel myModel, IMyService myService)
{
...
this.PropertyChanged += (sender, args) =>
{
if (_canGetDirty)
_myService?.AskForSaving();
};
}
You could weed out any race condition logic on the mode with the args.PropertyName check.
I'm sure this is a very basic question but I don't even know the technical term / jargon to Google and self-educate on.
I have created a simple model implementing INotifyPropertyChanged.
public class PushNotes : INotifyPropertyChanged
{
public string CompletePushNotes { get; set; }
}
Binding in cs:
evt_pushNotes = new PushNotes()
{
CompletePushNotes = "HelloThere"
};
this.DataContext = evt_pushNotes;
//snip later in code
Helpers.UpdateCompletePushNotes();
In XAML:
<xctk:RichTextBox x:Name="PushEmail" Text="{Binding Path=CompletePushNotes, Mode=OneWay}" ScrollViewer.VerticalScrollBarVisibility="Auto" Margin="40,398,40,40">
<xctk:RichTextBox.TextFormatter>
<xctk:PlainTextFormatter />
</xctk:RichTextBox.TextFormatter>
</xctk:RichTextBox>
Helper:
internal static class Helpers
{
internal static void UpdateCompletePushNotes()
{
//duhhhh what do I do now??
//If I create a new PushNotes it will be a different instantiation....???
}
}
Now this is all fine but I have a method in a helper class that needs to change the CompletePushNotes.
Again I know this is a simplistic / newbie question but I don't know what I need to learn.
So do I make my PushNotes class static, or singleton. Is there some global binding "tree" I can walk to find my instantiated and bound PushNotes class that is attached to the UI element?
Not looking for an a handout just need to know what it is I'm looking for.
TIA
Your PushNotes class does not implement the INotifyPropertyChanged interface. Once you have implemented it, you need to modify your CompletePushNotes property to have a backing field and in the setter of the property you can raise the PropertyChanged event to notify the UI of the source property update.
public class PushNotes : INotifyPropertyChanged
{
string completePushNotes;
public string CompletePushNotes
{
get
{
return completePushNotes;
}
set
{
completePushNotes = value;
OnPropertyChanged();
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
}
}
Making the PushNotes class static will not help you. You seem to have a variable of some sort to the PushNotes instance (evt_pushNotes), so just do:
evt_pushNotes.CompletePushNotes = something;
If you have a helper class that does something, call the method in the helper class and get the value back or pass the PushNotes instance into the helper class as a parameter.
internal static class Helpers
{
internal static void UpdateCompletePushNotes(PushNotes pushNotes)
{
pushNotes.CompletePushNotes = something;
}
}
I have a class with several properties. When these are changed, a PropertyChangedEvent raised. Some properties depend on other properties, so when one of the dependencies is changed, it raises a PropertyChangedEvent for itself, and for the property that depends on it.
My problem is, that I have attached a PropertyChangedEventHandler to an instance of this class. When one of the properties that is depended upon by another property, this event handler is called several times. Is there a way to get around this?
UPDATE
A little code, just to illustrate what I'm talking about:
private bool _foo;
private bool _bar;
public bool Foo
{
get { return _foo; }
set
{
_foo = value;
OnPropertyChanged("Foo");
OnPropertyChanged("Baz");
}
}
public bool Bar
{
get { return _bar; }
set
{
_bar = value;
OnPropertyChanged("Bar");
OnPropertyChanged("Baz");
}
}
public bool Baz
{
get
{
return Foo && Bar;
}
}
When Foo is set, Baz changes as well. If I attach a PropertyChangedEventHandler, this will be called twice, when I set Foo. I only want to call it once. Is there a nice way around it?
All you need to do is think logically about this issue for a moment to get your answer. You have the following situation:
public bool Foo
{
get { return _foo; }
set
{
_foo = value;
OnPropertyChanged("Foo");
OnPropertyChanged("Baz");
}
}
public bool Bar
{
get { return _bar; }
set
{
_bar = value;
OnPropertyChanged("Bar");
OnPropertyChanged("Baz");
}
}
public bool Baz
{
get
{
return Foo && Bar;
}
}
My first comment is that if you are going to raise your INotifyPropertyChanged.PropertyChanged event more than once at a time, then you'd be better off using an implementation like this, which would enable you to raise the event for as many properties as you like at once:
protected virtual void NotifyPropertyChanged(params string[] propertyNames)
{
if (PropertyChanged != null)
{
foreach (string propertyName in propertyNames)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
...
NotifyPropertyChanged("Foo", "Bar");
So back to your problem... let's think about your requirement:
You need to notify the interface when the value of Baz changes.
Its value can be changed when either the Foo or Bar property values change... therefore you'll need to notify the interface when either of them changes, as you have.
Now, #Franck commented to say that You should from the setter of Property 1 trigger a method to update Property 2 which by changing will trigger his own property change. Let's think about that for a minute... presumably, you'd need to call this same method from both of the Foo or Bar property setters to make it work.
However, what would actually happen is that the when the Foo and Bar values are changed, the method would be called twice and so the INotifyPropertyChanged notification will still be called twice, albeit now coming from the Baz setter... so clearly, this is no improvement.
There is however, one more option that could work for you. For this to work, you'd need to add a helper method that would update the Foo or Bar property values and notify the INotifyPropertyChanged interface on behalf of the Baz property:
public void UpdateBaz(bool? foo, bool? bar)
{
if (foo != null) Foo = foo;
if (bar != null) Bar = bar;
OnPropertyChanged("Baz");
}
Of course, for this to work, you'd need to remove the call to OnPropertyChanged("Baz") from the two property handlers:
public bool Foo
{
get { return _foo; }
set { _foo = value; OnPropertyChanged("Foo"); }
}
public bool Bar
{
get { return _bar; }
set { _bar = value; OnPropertyChanged("Bar"); }
}
However, if you want the Foo and Bar properties to be updated through Binding, then you'd be back to #Franck's suggestion of calling this method twice and the two properties are updated individually. In that case, it seems as though you'll just have to put up with the Baz property being notified twice.
insert a value changed check in your setter like:
set
{
if(value != [yourfield])
{
yourfield = value;
OnPropertyChanged(Yourfield);
}
}
In my mvvm ViewModel I have such field
public int Delta { get; private set; }
However when I update it like that:
Delta = newValue;
UI is not refreshed.
I was thinking that databinding will do that for me. For example I can declare collection as ObservableCollection and then databinding will work.
However there are no ObservableInt, how to say View that it need to be refreshed then?
Probably I should raise some event "notify property changed" or something?
You have two choices:
Implement the INotifyPropertyChanged interface on your class.
Inherit from DependencyObject and implement Delta as a DependencyProperty.
The simplest option is #1. You can implement the INotifyPropertyChanged interface on your class quite easily:
public class YourClass : INotifyPropertyChanged
{
private int _delta;
public int Delta
{
get { return _delta; }
set { _delta = value; PropertyChanged?.Invoke(nameof(Delta)); }
}
public event PropertyChangedEventHandler PropertyChanged;
}
You can read more about using and implementing dependency properties on MSDN.
While we're at it with improving the answer, some of the other new additions of c# 6.0 and 7.0 help make it ever more compact:
public class Prop<T> : INotifyPropertyChanged
{
private T _value;
public T Value
{
get => _value;
set { _value = value; NotifyPropertyChanged(nameof(Value)); }
}
public event PropertyChangedEventHandler PropertyChanged;
internal void NotifyPropertyChanged(string propertyName) =>
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
This way, you aren't using any "embedded values" (i.e - the property's name) and are keeping the code refactor-safe.
And there's also no need for redundant code blocks due to c# 6.0 and 7.0's new Expression body features
Using #LBushKin's Answer, i modified it to
public class Prop<T> : INotifyPropertyChanged
{
private T _value;
public T Value
{
get { return _value; }
set { _value = value; NotifyPropertyChanged("Value"); }
}
public event PropertyChangedEventHandler PropertyChanged;
internal void NotifyPropertyChanged(String propertyName)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
and to set it up:
class MainWindow ...
// a bool with initial value of true
public static Prop<bool> optionBool { get; set; } = new Prop<bool>{ Value = true };
private void Window_Loaded(object sender, RoutedEventArgs e)
{
// connect UI to be able to use the Prop
DataContext = this;
}
and to use it:
<Grid ...
<CheckBox Content="Da Check" ... IsChecked="{Binding optionBool.Value}"/>
There is also a Collection and 2-Properties version here:
Utils.ObservableProperties.cs (this repo contains several related classes)
Just implement INotifyPropertyChanged Interface in your class and use it to raise a PropertyChanged for your Property and then UI will update. If you are using an MVVM project template then there is a good chance you already have a helper method implemented you only need to use it.
MSDN INotifyPropertyChanged
GalaSoft MVVM Light Toolkit
The ObservableCollection raises events automatically but for your own properties you have to raise the events yourself.
A good example is here: http://www.codeproject.com/Tips/228352/Naming-Properties-in-MVVM?display=Print
I'd suggest using mvvm light: http://mvvmlight.codeplex.com, I used it in silverlight and wpf applications. Very easy to use and provides a messageing system between model, view model and view.
Adding on to https://stackoverflow.com/a/8316100/5725669, there is a new and easy way to do this without remembering to keep track of PropertyChanged?.Invoke(nameof(Delta)); in every location
public class YourClass : INotifyPropertyChanged
{
private int _delta;
public int Delta
{
get { return _delta; }
set {
_delta = value;
// Call OnPropertyChanged whenever the property is updated
OnPropertyChanged();
}
}
// Declare the event
public event PropertyChangedEventHandler PropertyChanged;
public YourClass()
{
}
// Create the OnPropertyChanged method to raise the event
// The calling member's name will be used as the parameter.
protected void OnPropertyChanged([CallerMemberName] string name = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
}
}
It makes use of CallerMemberName for skipping manual entries for property name. More details on this MSDN Doc
I'm using Caliburn and the MVVM pattern in a WPF application, and am trying to use as many conventions as possible. One issue I'm running into is when I have to wire-up some property-changed notifications on an imported class.
Say I have a ViewModel like so:
class ViewModelA
{
private readonly IViewModelB _b;
public ViewModelA(IViewModelB b)
{
_b = b;
}
public bool CanGo
{
get { return _b.MyBoolProperty; }
}
public void Go()
{
//Does something here
}
}
What is the recommended (correct) way to call NotifyOfPropertyChange(() => CanGo) when the PropertyChanged event for MyBoolProperty is fired off ViewModelB?
In the past I've used a PropertyObserver type class to manage this.
Or am I designing this scenario completely wrong?
If your "sub"-model is exposed with a public property, you could use DependenciesAttribute to track changes:
class ViewModelA
{
public IViewModelB B {get; private set;}
public ViewModelA(IViewModelB b)
{
B = b;
}
public bool CanGo
{
get { return B.MyBoolProperty; }
}
[Dependencies("B.MyBoolProperty")]
public void Go()
{
//Does something here
}
}
To work properly, the whole property path should be composed of notifying objects.
You can also put a final "*"
[Dependencies("B.*")]
to indicate that all properties of B should cause the precondition re-evaluation; note that "*" only acts on the end of the proprerty path and just for one level of depth (it doesn't track changes on sub-models of B).