i am looking for a solution to fire PropertyChanged of the whole property, if one of the inner properties changes. so here is the model:
public class MainStatus : ObservableObject
{
private bool _safety;
public bool Safety
{
get { return _safety; }
set
{
if (_safety == value)
return;
_safety = value;
RaisePropertyChanged("Safety");
}
}
private bool _setupMode
public bool SetupMode
{
get { return _setupMode; }
set
{
if (_setupMode == value)
return;
_setupMode = value;
RaisePropertyChanged("SetupMode");
}
}
}
it has some more properties (bool, int and string).
in the vm it is used like this:
private MainStatus _mainStatus;
public MainStatus MainStatus
{
get { return _mainStatus; }
set
{
if (_mainStatus == value)
return;
_mainStatus = value;
RaisePropertyChanged("MainStatus");
}
}
i can bind to the inner properties like this:
<DataTrigger Binding="{Binding MainStatus.Safety}" Value="true">
this all works fine. but i need to send the whole MainStatus to other VMs, if one of the inner property changes. so i changed the property like the this:
public MainStatus MainStatus
{
get { return _mainStatus; }
set
{
if (_mainStatus == value)
return;
_mainStatus = value;
RaisePropertyChanged("MainStatus");
Messenger.Default.Send<MainStatusMessage>(new MainStatusMessage() { MainStatus = _mainStatus });
}
}
but this does not work. it seems like the PropertyChanged for the MainStatus is never fired if one of the inner properties changes. how can i fix that?
In your VM, you could subscribe to Mainstatus.PropertyChanged event something like:
this.MainStatus.PropertyChanged += new PropertyChangedEventHandler(MainStatus_PropertyChanged);
//..........
void MainStatus_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
RaisePropertyChanged("MainStatus");
}
If I uderstand the question you want to raise the property changed events for ALL properties on a class that implements INotifyPropertyChanged, this can be achieved by calling the event handler with a NULL instead of the string containing the property name:
e.g. RaisePropertyChanged(null) would raise both Safety and SetUpMode on the MainStatus class defined above.
Related
I have a model implementing both INotifyPropertyChanged and INotifyDataErrorInfo. The Property changed event fires when ever I have a property modified, but for some reason when I raise the Error event handler, the UI does ever invoke the GetErrors method. This results in the validation error not being rendered to the UI.
Can someone take a look at how I have the INotifyDataErrorInfo set up and tell me if I'm doing something wrong?
Base model implementation
public class BaseChangeNotify : INotifyPropertyChanged, INotifyDataErrorInfo
{
private bool isDirty;
private Dictionary<string, List<string>> errors = new Dictionary<string, List<string>>();
public BaseChangeNotify()
{
}
public event PropertyChangedEventHandler PropertyChanged;
public event EventHandler<DataErrorsChangedEventArgs> ErrorsChanged;
public bool IsDirty
{
get
{
return this.isDirty;
}
set
{
this.isDirty = value;
this.OnPropertyChanged();
}
}
public bool HasErrors
{
get
{
return this.errors.Count(e => e.GetType() == typeof(ErrorMessage)) > 0;
}
}
public IEnumerable GetErrors(string propertyName)
{
if (string.IsNullOrEmpty(propertyName) ||
!this.errors.ContainsKey(propertyName))
{
return null;
}
return this.errors[propertyName];/*.Where(e => (e is ErrorMessage));*/
}
protected virtual void AddError(string propertyName, string error, bool isWarning = false)
{
if (!this.errors.ContainsKey(propertyName))
{
this.errors[propertyName] = new List<string>();
}
if (!this.errors[propertyName].Contains(error))
{
if (isWarning)
{
this.errors[propertyName].Add(error);
}
else
{
this.errors[propertyName].Insert(0, error);
}
this.OnErrorsChanged(propertyName);
}
}
protected virtual void RemoveError(string propertyName, string error)
{
if (this.errors.ContainsKey(propertyName) &&
this.errors[propertyName].Contains(error))
{
this.errors[propertyName].Remove(error);
if (this.errors[propertyName].Count == 0)
{
this.errors.Remove(propertyName);
}
this.OnErrorsChanged(propertyName);
}
}
public virtual void OnPropertyChanged([CallerMemberName] string propertyName = "")
{
// Perform the IsDirty check so we don't get stuck in a infinite loop.
if (propertyName != "IsDirty")
{
this.IsDirty = true; // Each time a property value is changed, we set the dirty bool.
}
if (this.PropertyChanged != null)
{
// Invoke the event handlers attached by other objects.
try
{
// When unit testing, this will always be null.
if (Application.Current != null)
{
try
{
Application.Current.Dispatcher.Invoke(() =>
this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName)));
}
catch (Exception)
{
throw;
}
}
else
{
this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
catch (Exception)
{
throw;
}
}
}
/// <summary>
/// Called when an error has changed for this instance.
/// </summary>
/// <param name="propertyName">Name of the property.</param>
public virtual void OnErrorsChanged([CallerMemberName] string propertyName = "")
{
if (string.IsNullOrWhiteSpace(propertyName))
{
return;
}
if (this.ErrorsChanged != null)
{
this.ErrorsChanged(this, new DataErrorsChangedEventArgs(propertyName));
}
}
}
Model using the implementation
public class PayItem : BaseChangeNotify
{
private Section section;
public Section Section
{
get
{
return this.section;
}
set
{
this.section = value;
this.ValidateSection();
this.OnPropertyChanged();
}
}
private void ValidateSection([CallerMemberName] string propertyName = "")
{
const string sectionError = "You must select a Section.";
if (this.Section == null || this.Section.Name.Length > 1)
{
this.AddError(propertyName, sectionError);
}
else
{
this.RemoveError(propertyName, sectionError);
}
}
The View trying to use it
<ComboBox Name="SectionComboBox"
ItemsSource="{Binding Path=ProjectSections}"
SelectedItem="{Binding Path=SelectedPayItem.Section,
NotifyOnValidationError=True,
UpdateSourceTrigger=PropertyChanged}">
The app is being wrote in WPF, and the WPF docs are pretty scarce. I've read through the Silverlight documentation on it along with a few other blog posts I found on the internet and have implemented in each of the different ways the blog authors suggest. Each time the result is the same, the GetErrors() method never gets hit by the Binding engine.
Can anyone see something that I'm doing wrong? When my model has its property set, I can step through the debugger and ultimately end up within the OnErrorsChanged event handler, and the event gets invoked. Nothing happens when it gets invoked though, so I'm stumped.
Thanks in advance for any help.
Johnathon
EDIT
Also I would like to note that I had been using IDataErrorInfo in the base class for the last couple of months without any issues. The binding worked, the errors were reported to the View and everything was happy. When I changed from IDataErrorInfo to INotifyDataErrorInfo, the validation appeared to stop communicating with the View.
The INotifyDataErrorInfo.HasErrors property must return true when raising the ErrorsChanged event. Otherwise the binding engine ignores the errors. Your HasErrors property will return false all the time. This happens because you are checking for items of type ErrorMessage but your dictionary contains items of type KeyValuePair<string, List<string>>. Besides that it is highly inefficent to count all the items. You should use .Any() instead.
By the way, the MSDN documentation of INotifyDataErrorInfo says the following:
Note that the binding engine never uses the HasErrors property,
although you can use it in custom error reporting.
This is plain wrong and it took me hours to find that out.
I am trying to figure out on how to trigger the PropertyChangedEvent when the middle layer of my binding changes. I will start with an example here:
public class MainViewModel :NotificationObject // Main DataContext
{
public SubViewModel SubVM{get; {_subVM = value; RaisePropertyChanged("SubVM");}} // observable property
public void DoChangeSubVM()
{
SubVM = new SubViewModel(); // doing this will not update the egControl
}
}
public class SubViewModel : NotificationObject
{
public Sub2ViewModel Sub2VM {get; set{_sub2VM = value; RaisePropertyChanged("Sub2VM");}} // observable property
}
public class Sub2ViewModel : NotificationObject
{
public int SomeProp {get; set {_someProp = value; RaisePropertyChanged("SomeProp");} // observable property
}
in the XAML:
<EgControl name="egControl" Content={Binding SubVM.Sub2VM.SomeProp} />
Now if I change the Sub2VM Property the egControl doesn't automagically get updated with the SomeProp value of the new Sub2VM instance. How does someone go about achieving this, with out manually having to raise all the Sub2ViewModel propertychanged events from Sub2VM property setter?
Using: Prism .NET 4.0
How does someone go about achieving this, with out manually having to raise all the Sub2ViewModel propertychanged events from Sub2VM property setter?
Answer
You have several possibilities:
Raise all property changed events in the setter, which you said you wanted to avoid. But it's a valid strategy to consider. If you know which properties are dependant on the results of another, then they will need to raise property changed in the setter for many properties.
public class myViewModel
{
private string _FirstName
public string FirstName
{
get { return_FirstName };
set
{
_FirstName = value;
RaisePropertyChanged("FirstName");
RaisePropertyChanged("FullName");
}
}
}
Raise all property changed events in the method, after the new ViewModel has been constructed.
public class myViewModel
{
private string _FirstName
public string FirstName
{
get { return_FirstName };
set
{
_FirstName = value;
RaisePropertyChanged("FirstName");
}
}
public void UpdateFirstName(string firstName)
{
_FirstName = firstName;
RaisePropertyChanged("FirstName");
RaisePropertyChanged("FullName");
}
}
Use the setters to set some properties, thus triggering the already present property changed event.
public class myViewModel
{
private string _FirstName
public string FirstName
{
get { return_FirstName };
set
{
_FirstName = value;
RaisePropertyChanged("FirstName");
}
}
public Person ClonePerson(Person rootPerson)
{
Person clone = new Person()
{
FirstName = rootPerson.FirstName;
LastName = rootPerson.LastName;
}
return clone;
}
}
Make a method that raises all property changed events, and call it in edge cases where you need to raise multiple changed events.
public class myViewModel
{
private string _FirstName
public string FirstName
{
get { return_FirstName };
set
{
_FirstName = value;
this.RaiseAllPropertyChanges();
}
}
public void RaiseAllPropertyChanges()
{
RaisePropertyChanged("FirstName");
RaisePropertyChanged("FullName");
}
}
The end result is this: For any bound UI element to know that it must update, the property changed event for that property must be raised.
Okay, not sure about Prism, but generally speaking all three classes should be implementing property change notification. The simplist way to do so is with INotifyPropertyChanged. So SubViewModel should be more like:
public class SubViewModel : INotifyPropertyChanged
{
private Sub2ViewModel sub2vm;
public Sub2ViewModel Sub2VM
{
get
{
return sub2vm;
}
set
{
sub2vm = value;
OnPropertyChanged("Sub2VM");
}
}
protected void OnPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
public event PropertyChangedEventHandler PropertyChanged;
}
Without property change notification, the UI doesn't know when to udpate a bound property.
One way to go about it is to create a constructor for both the SubViewModel and Sub2ViewModel that initializes all the properties to some default value. This will ensure that your properties are initialized and give you the ability to set the initial values.
I have the following class structure
class Top : NotifyPropertyChanged
{
private List<Inner> innerList;
public bool IsInnerTrue
{
get
{
foreach (Inner inner in innerList)
{
if (inner.IsTrue)
return true;
}
return false;
};
}
}
class Inner : NotifyPropetyChanged
{
private bool isTrue;
public bool IsTure
{
get
{
return isTrue;
}
set
{
isTrue = value;
NotifyPropretyChanged("IsTrue");
}
}
}
In my view I'm binding to the IsInnerTrue property of the Top class. My problem is that I can't figure out how to fire the PropertyChanged event for IsInnerTrue when the IsTrue property of one of the Inner objects changes value.
Does anyone have a suggestion, short of setting up an event handler for each Inner object?
Here are two options. One, subscribe to the inner PropertyChanged events:
class Top : NotifyPropertyChanged
{
private List<Inner> innerList = new List<Inner>();
public bool IsInnerTrue
{
get
{
foreach (Inner inner in innerList)
{
if (inner.IsTrue)
return true;
}
return false;
}
}
public void Add(Inner inner)
{
innerList.Add(inner);
inner.PropertyChanged += InnerPropertyChanged;
}
public void Remove(Inner inner)
{
innerList.Remove(inner);
inner.PropertyChanged -= InnerPropertyChanged;
}
private void InnerPropertyChanged(object sender, PropertyChangedEventArgs e)
{
if (e.PropertyName == "IsTrue")
RaisePropertyChanged("IsInnerTrue");
}
}
Or two, use a dependency tracking library like Update Controls. These libraries can detect that IsInnerTrue depends upon IsTrue, and will fire the top-level property changed event when the inner property changes.
I have a entity class in a C# library class and linked to Silverlight class library
(entities must be in C# class because of legacy compatiblity with other systems)
Example (C# library):
public class TestClass
{
private string _testValue;
public string TestValue
{
get { return _testValue; }
set
{
if (_testValue!= value)
{
_testValue = value;
OnPropertyChanged("TestValue");
}
}
}}
This class is linked to Silverlight class library.
On a MVVM there is a property
private TestClass _testProp = new TestClass();
public TestClass TestProp
{
get
{
return _testProp ;
}
set
{
if (value != _testProp )
{
_testProp = value;
RaisePropertyChanged("TestProp");
PressCommand.CanExecuteChanged();
}
}
}
The property is binded to a control in XAML
<TextBox Text="{Binding TestProp.TestValue, Mode=TwoWay}">
<Button Content="Press" Command="{Binding PressCommand}" />
I want to control the button with RelayCommands CanExecute depended on the TestValue in TestClass...
PressCommand = new RelayCommand(() =>
{
DoSomething();
}, () => TestProp.TestValue != string.empty);
However, if the TestValue in changed (different then empty string), PressCommand CanExecute doen't seem to notice the change and is not enabled, making it unusable...
Is it possible to use the CanExecute with this kind of set-tu
What you need to do is call PressCommand.CanExecuteChanged() when the value changes. To do this nicely listen for the property change of the value in the property
VM
public TestClass TestProp
{
get
{
return _testProp ;
}
set
{
if (value != _testProp )
{
if(_testProp != null)
{
_testProp.PropertyChanged -= TestPropChanged;
}
_testProp = value;
if(_testProp != null)
{
_testProp.PropertyChanged += TestPropChanged;
}
RaisePropertyChanged("TestProp");
PressCommand.CanExecuteChanged();
}
}
}
private void TestPropChanged(object sender, PropertyChangedEventArgs e)
{
//Can check for specific property if required...
PressCommand.CanExecuteChanged();
}
I'm developing an application in Silverlight2 and trying to follow the Model-View-ViewModel pattern. I am binding the IsEnabled property on some controls to a boolean property on the ViewModel.
I'm running into problems when those properties are derived from other properties. Let's say I have a Save button that I only want to be enabled when it's possible to save (data has been loaded, and we're currently not busy doing stuff in the database).
So I have a couple of properties like this:
private bool m_DatabaseBusy;
public bool DatabaseBusy
{
get { return m_DatabaseBusy; }
set
{
if (m_DatabaseBusy != value)
{
m_DatabaseBusy = value;
OnPropertyChanged("DatabaseBusy");
}
}
}
private bool m_IsLoaded;
public bool IsLoaded
{
get { return m_IsLoaded; }
set
{
if (m_IsLoaded != value)
{
m_IsLoaded = value;
OnPropertyChanged("IsLoaded");
}
}
}
Now what I want to do is this:
public bool CanSave
{
get { return this.IsLoaded && !this.DatabaseBusy; }
}
But note the lack of property-changed notification.
So the question is: What is a clean way of exposing a single boolean property I can bind to, but is calculated instead of being explicitly set and provides notification so the UI can update correctly?
EDIT: Thanks for the help everyone - I got it going and had a go at making a custom attribute. I'm posting the source here in case anyone's interested. I'm sure it could be done in a cleaner way, so if you see any flaws, add a comment or an answer.
Basically what I did was made an interface that defined a list of key-value pairs to hold what properties depended on other properties:
public interface INotifyDependentPropertyChanged
{
// key,value = parent_property_name, child_property_name, where child depends on parent.
List<KeyValuePair<string, string>> DependentPropertyList{get;}
}
I then made the attribute to go on each property:
[AttributeUsage(AttributeTargets.Property, AllowMultiple = true, Inherited = false)]
public class NotifyDependsOnAttribute : Attribute
{
public string DependsOn { get; set; }
public NotifyDependsOnAttribute(string dependsOn)
{
this.DependsOn = dependsOn;
}
public static void BuildDependentPropertyList(object obj)
{
if (obj == null)
{
throw new ArgumentNullException("obj");
}
var obj_interface = (obj as INotifyDependentPropertyChanged);
if (obj_interface == null)
{
throw new Exception(string.Format("Type {0} does not implement INotifyDependentPropertyChanged.",obj.GetType().Name));
}
obj_interface.DependentPropertyList.Clear();
// Build the list of dependent properties.
foreach (var property in obj.GetType().GetProperties())
{
// Find all of our attributes (may be multiple).
var attributeArray = (NotifyDependsOnAttribute[])property.GetCustomAttributes(typeof(NotifyDependsOnAttribute), false);
foreach (var attribute in attributeArray)
{
obj_interface.DependentPropertyList.Add(new KeyValuePair<string, string>(attribute.DependsOn, property.Name));
}
}
}
}
The attribute itself only stores a single string. You can define multiple dependencies per property. The guts of the attribute is in the BuildDependentPropertyList static function. You have to call this in the constructor of your class. (Anyone know if there's a way to do this via a class/constructor attribute?) In my case all this is hidden away in a base class, so in the subclasses you just put the attributes on the properties. Then you modify your OnPropertyChanged equivalent to look for any dependencies. Here's my ViewModel base class as an example:
public class ViewModel : INotifyPropertyChanged, INotifyDependentPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propertyname)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyname));
// fire for dependent properties
foreach (var p in this.DependentPropertyList.Where((x) => x.Key.Equals(propertyname)))
{
PropertyChanged(this, new PropertyChangedEventArgs(p.Value));
}
}
}
private List<KeyValuePair<string, string>> m_DependentPropertyList = new List<KeyValuePair<string, string>>();
public List<KeyValuePair<string, string>> DependentPropertyList
{
get { return m_DependentPropertyList; }
}
public ViewModel()
{
NotifyDependsOnAttribute.BuildDependentPropertyList(this);
}
}
Finally, you set the attributes on the affected properties. I like this way because the derived property holds the properties it depends on, rather than the other way around.
[NotifyDependsOn("Session")]
[NotifyDependsOn("DatabaseBusy")]
public bool SaveEnabled
{
get { return !this.Session.IsLocked && !this.DatabaseBusy; }
}
The big caveat here is that it only works when the other properties are members of the current class. In the example above, if this.Session.IsLocked changes, the notification doesnt get through. The way I get around this is to subscribe to this.Session.NotifyPropertyChanged and fire PropertyChanged for "Session". (Yes, this would result in events firing where they didnt need to)
The traditional way to do this is to add an OnPropertyChanged call to each of the properties that might affect your calculated one, like this:
public bool IsLoaded
{
get { return m_IsLoaded; }
set
{
if (m_IsLoaded != value)
{
m_IsLoaded = value;
OnPropertyChanged("IsLoaded");
OnPropertyChanged("CanSave");
}
}
}
This can get a bit messy (if, for example, your calculation in CanSave changes).
One (cleaner? I don't know) way to get around this would be to override OnPropertyChanged and make the call there:
protected override void OnPropertyChanged(string propertyName)
{
base.OnPropertyChanged(propertyName);
if (propertyName == "IsLoaded" /* || propertyName == etc */)
{
base.OnPropertyChanged("CanSave");
}
}
You need to add a notification for the CanSave property change everywhere one of the properties it depends changes:
OnPropertyChanged("DatabaseBusy");
OnPropertyChanged("CanSave");
And
OnPropertyChanged("IsEnabled");
OnPropertyChanged("CanSave");
How about this solution?
private bool _previousCanSave;
private void UpdateCanSave()
{
if (CanSave != _previousCanSave)
{
_previousCanSave = CanSave;
OnPropertyChanged("CanSave");
}
}
Then call UpdateCanSave() in the setters of IsLoaded and DatabaseBusy?
If you cannot modify the setters of IsLoaded and DatabaseBusy because they are in different classes, you could try calling UpdateCanSave() in the PropertyChanged event handler for the object defining IsLoaded and DatabaseBusy.