Can I avoid explicitly calling RaisePropertyChanged for dependent properties? - wpf

I have
public override bool RelatedProperty
{
get { return this.SomeProperty > 0; }
}
public int SomeProperty
{
get { return this.someProperty; }
protected set
{
this.Set<int>(ref this.someProperty, value);
this.RaisePropertyChanged(nameof(this.RelatedProperty));
}
}
where RelatedProperty is obviously dependent on SomeProperty.
Is there a better way to update the binding, than invoking RaisePropertyChanged for RelatedProperty, from the setter of SomeProperty?

Is there a better way to update the binding, than invoking RaisePropertyChanged for RelatedProperty, from the setter of SomeProperty?
No. At least not using MvvmLight and the imperative approach of implementing properties.
If you were using a reactive UI framework such as ReactiveUI you would handle property changes in a functional way:
public class ReactiveViewModel : ReactiveObject
{
public ReactiveViewModel()
{
this.WhenAnyValue(x => x.SomeProperty).Select(_ => SomeProperty > 0)
.ToProperty(this, x => x.RelatedProperty, out _relatedProperty);
}
private int _someProperty;
public int SomeProperty
{
get { return _someProperty; }
set { this.RaiseAndSetIfChanged(ref _someProperty, value); }
}
private readonly ObservableAsPropertyHelper<bool> _relatedProperty;
public bool RelatedProperty
{
get { return _relatedProperty.Value; }
}
}
You can read more about this in the docs for ReactiveUI and on the creator Paul Betts' blog if you are interested:
https://docs.reactiveui.net/en/fundamentals/functional-reactive-programming.html
http://log.paulbetts.org/creating-viewmodels-with-reactiveobject/

Related

mvvm update calculated fields

Do you know best practices in wpf+mvvm to update Calculated fields?
What I can do instead OnPropertyChanged(nameof(Summary))?
Also calculated field can be in another viewmodel and this viewmodel should not know about all dependences.
This is my code :
public class Model
{
public int Prop1 { get; set; }
public int Prop2 { get; set; }
public int Summary => Prop1 + Prop2;
}
public class ViewModel : BaseViewModel
{
public Model Model { get; }
public int Prop1
{
get
{
return Model.Prop1;
}
set
{
Model.Prop1 = value;
OnPropertyChanged();
OnPropertyChanged(nameof(Summary));
}
}
public int Prop2
{
get
{
return Model.Prop2;
}
set
{
Model.Prop2 = value;
OnPropertyChanged();
OnPropertyChanged(nameof(Summary));
}
}
public int Summary => Model.Summary;
}
Calling OnPropertyChanged on the calculated property is perfectly acceptable. If you have a relatively simple model like the one you wrote that'll be enough.
If you have multiple calculated properties on the model, you might consider creating a method to call all of them from a single place, instead of calling each one from every property.
Something like this:
public int Prop1
{
get
{
return _prop1;
}
set
{
_prop1 = value;
OnPropertyChanged();
NotifyCalculatedProperties();
}
}
public int Calc1 { get { /* ... */ } }
public int Calc2 { get { /* ... */ } }
public int Calc3 { get { /* ... */ } }
public void NotifyCalculatedProperties()
{
OnPropertyChanged(nameof(Calc1));
OnPropertyChanged(nameof(Calc2));
OnPropertyChanged(nameof(Calc3));
}
In case the calculated properties exist in a different model, you can register in that Model\VM to the source's PropertyChanged event, and then invoke the change notification there.
Like that:
void ModelPropertyChanged(object sender, PropertyChangedEventArgs e)
{
if(e.PropertyName == "Prop1")
OnPropertyChanged(nameof(Calc1));
OnPropertyChanged(nameof(Calc2));
OnPropertyChanged(nameof(Calc3));
}
Just don't forget to unsubscribe when the Model\VM changes, or you'll have a memory leak on your hands.
Lastly, you can always use the Messenger to pass messages between unrelated VMs, though you should use caution since it's a very powerful tool, and can easily be misused.
I don't know what MVVM framework you're using, but each has it's own implementation. You can find more general details on the Messenger pattern here: https://msdn.microsoft.com/en-us/magazine/jj694937.aspx

ActionCollection of Blend custom Behavior is always empty

I have created Blend behavior, but when I add children to it in xaml, they does not appear in the collection. What might be the reason of that ?
When app is running, Actions collection does not contain any action, though it certainly should.
<Helpers:EnterKeyUpEventBehavior>
<Helpers:CloseFlyoutAction />
</Helpers:EnterKeyUpEventBehavior>
[ContentProperty(Name = "Actions")]
class EnterKeyUpEventBehavior : DependencyObject, IBehavior
{
public static readonly DependencyProperty ActionsProperty = DependencyProperty.Register(
"Actions", typeof (ActionCollection), typeof (EnterKeyUpEventBehavior), new PropertyMetadata(default(ActionCollection)));
public ActionCollection Actions
{
get
{
var actions = (ActionCollection) GetValue(ActionsProperty);
if (actions == null)
{
actions = new ActionCollection();
base.SetValue(ActionsProperty, actions);
}
return actions;
}
set { SetValue(ActionsProperty, value); }
}
private TextBox _associatedTextBox;
public DependencyObject AssociatedObject
{
get { return _associatedTextBox; }
}
public void Attach(DependencyObject associatedObject)
{
_associatedTextBox = associatedObject as TextBox;
if(_associatedTextBox == null)
throw new ArgumentException("This Behavior only works with TextBox control!");
_associatedTextBox.KeyUp += _associatedTextBox_KeyUp;
Actions = new ActionCollection();
}
void _associatedTextBox_KeyUp(object sender, Windows.UI.Xaml.Input.KeyRoutedEventArgs e)
{
if (e.Key == VirtualKey.Enter)
{
Interaction.ExecuteActions(_associatedTextBox, Actions, null);
}
}
public void Detach()
{
_associatedTextBox.KeyUp -= _associatedTextBox_KeyUp;
}
}
public ActionCollection Actions
{
get { return (ActionCollection) GetValue(ActionsProperty); }
set { SetValue(ActionsProperty, value); }
}
public EnterKeyUpEventBehavior()
{
Actions = new ActionCollection();
}
The xaml parsing and instatiation mechanism does not use your getter and setter, it uses GetValue(ActionsProperty) and SetValue(ActionsProperty) directly, circumventing your "lazy init".
In my experience when I want to use a collection type property as ContentProperty I have to either use a plain List or a DependencyObjectCollection (this is needed when you want the DataContext inheritance mechanism to function properly).
either:
public List<Action> Actions
{
get { return (List<Action>) GetValue(ActionsProperty); }
set { SetValue(ActionsProperty, value); }
}
public EnterKeyUpEventBehavior()
{
Actions = new List<Action>();
}
or:
public DependencyObjectCollection Actions
{
get { return (DependencyObjectCollection) GetValue(ActionsProperty); }
set { SetValue(ActionsProperty, value); }
}
public EnterKeyUpEventBehavior()
{
Actions = new DependencyObjectCollection();
}

Why does WPF seem to bypass TypeDescriptionProviderAttribute when INotifyPropertyChanged is implemented?

I'm trying to use the [TypeDescriptionProviderAttribute] in order to give my class a custom type descriptor. This works, but when I implement INotifyPropertyChanged WPF seems to ignore the custom type descriptor and go straight for the CLR property (if it exists). Here's a snippet, I'll paste the full example later on:
//[TypeDescriptionProvider(typeof(MyProvider))]
class MyModel : Object
//, INotifyPropertyChanged
//, ICustomTypeDescriptor
{
public string TheProperty { get { return "CLR - TheProperty"; } }
I bind a TextBlock to TheProperty. When I...
Leave everything commented
I see "CLR - TheProperty" as expected.
Use [TypeDescriptionProvider]
I see "MyPropertyDescriptor - TheProperty" as expected.
Use ICustomTypeDescriptor
I see "MyPropertyDescriptor - TheProperty" as expected.
Use ICustomTypeDescriptor and INotifyPropertyChanged
I see "MyPropertyDescriptor - TheProperty" as expected.
Use [TypeDescriptionProvider] and INotifyPropertyChanged
I see "CLR - TheProperty". Why is this? The weird thing is that custom properties without a CLR property are shown normally. My custom type descriptor also returns a "MyPropertyDescriptor - AnotherProperty" which works in all cases because there is no CLR AnotherProperty defined.
In summary, given this XAML
<StackPanel>
<TextBlock Text="{Binding TheProperty}" />
<TextBlock Text="{Binding AnotherProperty}" />
</StackPanel>
AnotherProperty always works as expected because the model does not have a CLR property named "AnotherProperty". TheProperty works as expected except when [TypeDescriptionProvider] and INotifyPropertyChanged are both used.
Here's the full code. It's a bit long but most of it is irrelevant, it's just required by System.ComponentModel
public partial class TestWindow : Window
{
public TestWindow()
{
InitializeComponent();
DataContext = new MyModel();
}
}
//[TypeDescriptionProvider(typeof(MyProvider))]
class MyModel : Object
//, INotifyPropertyChanged
//, ICustomTypeDescriptor
{
public string TheProperty { get { return "CLR - TheProperty"; } }
public event PropertyChangedEventHandler PropertyChanged;
public AttributeCollection GetAttributes()
{
return TypeDescriptor.GetAttributes(this);
}
public string GetClassName()
{
return TypeDescriptor.GetClassName(this);
}
public string GetComponentName()
{
return TypeDescriptor.GetComponentName(this);
}
public TypeConverter GetConverter()
{
return TypeDescriptor.GetConverter(this);
}
public EventDescriptor GetDefaultEvent()
{
return TypeDescriptor.GetDefaultEvent(this);
}
public PropertyDescriptor GetDefaultProperty()
{
return TypeDescriptor.GetDefaultProperty(this);
}
public object GetEditor(Type editorBaseType)
{
return TypeDescriptor.GetEditor(this, editorBaseType);
}
public EventDescriptorCollection GetEvents(Attribute[] attributes)
{
return TypeDescriptor.GetEvents(this, attributes);
}
public EventDescriptorCollection GetEvents()
{
return TypeDescriptor.GetEvents(this);
}
public PropertyDescriptorCollection GetProperties(Attribute[] attributes)
{
return TypeDescriptor.GetProperties(this, attributes);
}
public PropertyDescriptorCollection GetProperties()
{
return MyTypeDescriptor.GetCustomProperties();
}
public object GetPropertyOwner(PropertyDescriptor pd)
{
return this;
}
}
class MyProvider : TypeDescriptionProvider
{
public override ICustomTypeDescriptor GetTypeDescriptor(Type objectType, object instance)
{
return new MyTypeDescriptor();
}
}
class MyTypeDescriptor : CustomTypeDescriptor
{
public override PropertyDescriptorCollection GetProperties()
{
return GetCustomProperties();
}
public static PropertyDescriptorCollection GetCustomProperties()
{
return new PropertyDescriptorCollection(
new[] {
new MyPropertyDescriptor("TheProperty"),
new MyPropertyDescriptor("AnotherProperty")
});
}
}
class MyPropertyDescriptor : PropertyDescriptor
{
public MyPropertyDescriptor(string propName)
: base(propName, null)
{
}
public override bool CanResetValue(object component)
{
return false;
}
public override Type ComponentType
{
get { return typeof(MyModel); }
}
public override object GetValue(object component)
{
return "MyPropertyDescriptor - " + Name;
}
public override bool IsReadOnly
{
get { return true; }
}
public override Type PropertyType
{
get { return typeof(string); }
}
public override void ResetValue(object component)
{
throw new InvalidOperationException("cannot reset value");
}
public override void SetValue(object component, object value)
{
throw new InvalidOperationException("property is readonly");
}
public override bool ShouldSerializeValue(object component)
{
return true;
}
}
Old question, but for people looking for an answer..
Problem is in System.Windows.PropertyPath.ResolvePropertyName(String, Object, Type, Object, Boolean) private method. I have found it in PresentationFramework.dll in .NET 4.0.
Extracted from .NET Reflector:
object propertyHelper = DependencyProperty.FromName(str, ownerType);
if ((propertyHelper == null) && (item is ICustomTypeDescriptor))
{
propertyHelper = TypeDescriptor.GetProperties(item)[str];
}
if ((propertyHelper == null) && ((item is INotifyPropertyChanged) || (item is DependencyObject)))
{
propertyHelper = this.GetPropertyHelper(ownerType, str);
}
if (propertyHelper == null)
{
propertyHelper = TypeDescriptor.GetProperties(item)[str];
}
if (propertyHelper == null)
{
propertyHelper = this.GetPropertyHelper(ownerType, str);
}
if ((propertyHelper == null) && throwOnError)
{
throw new InvalidOperationException(SR.Get("PropertyPathNoProperty", new object[] { ownerType.Name, str }));
}
return propertyHelper;
As you can see, retrieving property identifier (DependencyProperty / PropertyDescriptor / PropertyInfo) goes like this:
Try get DependencyProperty,
If item implements ICustomTypeDescriptor, use TypeDescriptor to get PropertyDescriptor,
If item implements INotifyPropertyChanged or is DependencyObject, use System.Reflection to get PropertyInfo,
Else use TypeDescriptor to get PropertyDescriptor,
Else use System.Reflection to get PropertyInfo,
Else throw exception or return null.
So System.Reflection/PropertyInfo gets precedence over TypeDescriptor/PropertyDescriptor if item implements INotifyPropertyChanged interface. I believe they choose this strategy for performance reasons because PropertyInfo is much more lighter than PropertyDescriptor.
Solution to your problem would be to implement ICustomTypeDescriptor (preferably explicitly) so that it transfers ICustomTypeDescriptor method calls to appropriate TypeDescriptor method calls but not with object parameter, but Type parameter (this.GetType()). This way your TypeDescriptionProvider will be used.

PropertyChanged not firing

I have a strange situation:
in a .NET CF project there is a class (call it A) which has the following structure:
public partial class A: Form, INotifyPropertyChanged
{
//for simplicity stripping off everything unrelated to this problem
private int _SelectedRowsCount = 0;
public int SelectedRowsCount
{
get { return _SelectedRowsCount; }
set
{
_SelectedRowsCount = value;
OnPropertyChanged("SelectedRowsCount");
}
}
public bool enableCollectionButton
{
get { return SelectedRowsCount > 0; }
}
//....
//
//
void SomeMethod()
{
//for simplicity:
SelectedRowsCount = 1; //<- HERE NOT FIRING Propertychanged for enableCollectionButton
}
}
The class implements correctly the INotifyPropertyChanged interface which makes the the SelectedRowsCount property to fire a property changed notification (i evaluated this with the debugger).
The enableCollectionButton property is databound to some control like so:
someButton.DataBindings.Add("Enabled", this, "enableCollectionButton");
But the enableCollectionButton property does not change (though depending on the value of SelectedRowsCount). This property should be evaluated on a change of the SelectedRowsCount property, BUT IS NOT!!!
Why is this not functioning, what do i miss??
Thanks in advance
Try this
public partial class A: Form, INotifyPropertyChanged
{
//for simplicity stripping off everything unrelated to this problem
private int _SelectedRowsCount = 0;
public int SelectedRowsCount
{
get { return _SelectedRowsCount; }
set
{
_SelectedRowsCount = value;
OnPropertyChanged("SelectedRowsCount");
OnPropertyChanged("enableCollectionButton"); //This changes too !
}
}
public bool enableCollectionButton
{
get { return SelectedRowsCount > 0; }
}
}
What happens is that you're binding to the enableCollectionButton property, but you're not notifying the BindingManager of the change to enableCollectionButton, rather of the change to SelectedRowsCount. The BindingManager doesn't know they're related!
Also try using Microsoft's naming conventions, enableCollectionButton should be EnableCollectionButton

PropertyChanged notification for calculated properties

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.

Resources