I think this is the question, anyway. I am using a RelayCommand, which decorates an ICommand with two delegates. One is Predicate for the _canExecute and the other is Action for the _execute method.
---Background motivation --
The motivation has to do with unit testing ViewModels for a WPF presentation. A frequent pattern is that I have one ViewModel that has an ObservableCollection, and I want a unit test to prove the data in that collection is what I expect given some source data (which also needs to be converted into a collection of ViewModels). Even though the data in both collections looks the same in the debugger, it looks like the test fails due to an equality failure on the ViewModel's RelayCommand. Here's an example of the failing unit test:
[Test]
public void Creation_ProjectActivities_MatchFacade()
{
var all = (from activity in _facade.ProjectActivities
orderby activity.BusinessId
select new ActivityViewModel(activity, _facade.SubjectTimeSheet)).ToList();
var models = new ObservableCollection<ActivityViewModel>(all);
CollectionAssert.AreEqual(_vm.ProjectActivities, models);
}
--- Back to delegate equality ----
Here is the code for the RelayCommand - it's basically a direct rip-off of Josh Smith's idea, with an implementation for equality that I added in an attempt to solve this issue:
public class RelayCommand : ICommand, IRelayCommand
{
readonly Action<object> _execute;
readonly Predicate<object> _canExecute;
/// <summary>Creates a new command that can always execute.</summary>
public RelayCommand(Action<object> execute) : this(execute, null) { }
/// <summary>Creates a new command which executes depending on the logic in the passed predicate.</summary>
public RelayCommand(Action<object> execute, Predicate<object> canExecute) {
Check.RequireNotNull<Predicate<object>>(execute, "execute");
_execute = execute;
_canExecute = canExecute;
}
[DebuggerStepThrough]
public bool CanExecute(object parameter) { return _canExecute == null ? true : _canExecute(parameter); }
public event EventHandler CanExecuteChanged
{
add { CommandManager.RequerySuggested += value; }
remove { CommandManager.RequerySuggested -= value; }
}
public void Execute(object parameter) { _execute(parameter); }
public override bool Equals(object obj)
{
if (ReferenceEquals(null, obj)) return false;
if (ReferenceEquals(this, obj)) return true;
if (obj.GetType() != typeof(RelayCommand)) return false;
return Equals((RelayCommand)obj);
}
public bool Equals(RelayCommand other)
{
if (ReferenceEquals(null, other)) return false;
if (ReferenceEquals(this, other)) return true;
return Equals(other._execute, _execute) && Equals(other._canExecute, _canExecute);
}
public override int GetHashCode()
{
unchecked
{
return ((_execute != null ? _execute.GetHashCode() : 0) * 397) ^ (_canExecute != null ? _canExecute.GetHashCode() : 0);
}
}
}
In a unit test where I've effectively set the _execute delegate to the same method (_canExecute is null in both cases), the unit test fails at this line:
return Equals(other._execute, _execute) && Equals(other._canExecute, _canExecute)
Debugger output:
?_execute
{Method = {Void <get_CloseCommand>b__0(System.Object)}}
base {System.MulticastDelegate}: {Method = {Void CloseCommand>b__0(System.Object)}}
?other._execute
{Method = {Void <get_CloseCommand>b__0(System.Object)}}
base {System.MulticastDelegate}: {Method = {Void CloseCommand>b__0(System.Object)}}
Can anyone explain what I am missing and what the fix is?
---- EDITED REMARKS ----
As Mehrdad pointed out, the get_CloseCommand from the debug session looks a bit weird at first. It really is just a property get, but it does raise the point as to why the equality of the delegate is problematic if I need to do tricks to make it work.
Some of the point of MVVM is to expose whatever might be useful in a presentation as properties, so you can use WPF binding. The particular class I was testing has a WorkspaceViewModel in it's heirarchy, which is just a ViewModel that already has a close command property. Here is the code:
public abstract class WorkspaceViewModel : ViewModelBase
{
/// <summary>Returns the command that, when invoked, attempts to remove this workspace from the user interface.</summary>
public ICommand CloseCommand
{
get
{
if (_closeCommand == null)
_closeCommand = new RelayCommand(param => OnRequestClose());
return _closeCommand;
}
}
RelayCommand _closeCommand;
/// <summary>Raised when this workspace should be removed from the UI.</summary>
public event EventHandler RequestClose;
void OnRequestClose()
{
var handler = RequestClose;
if (handler != null)
handler(this, EventArgs.Empty);
}
public bool Equals(WorkspaceViewModel other) {
if (ReferenceEquals(null, other)) return false;
if (ReferenceEquals(this, other)) return true;
return Equals(other._closeCommand, _closeCommand) && base.Equals(other);
}
public override int GetHashCode() {
unchecked {
{
return (base.GetHashCode() * 397) ^ (_closeCommand != null ? _closeCommand.GetHashCode() : 0);
}
}
}
}
You can see that the close command is a RelayCommand, and that I monkeyed with equals to make the unit test work.
#Merhdad
Here is the unit test that only works when I use Trickster's delegate.Method in the equality comparison.
[TestFixture]
public class WorkspaceViewModelTests
{
private WorkspaceViewModel vm1;
private WorkspaceViewModel vm2;
private class TestableModel : WorkspaceViewModel
{
}
[SetUp]
public void SetUp() {
vm1 = new TestableModel();
vm1.RequestClose += OnWhatever;
vm2 = new TestableModel();
vm2.RequestClose += OnWhatever;
}
private void OnWhatever(object sender, EventArgs e) { throw new NotImplementedException(); }
[Test]
public void Equality() {
Assert.That(vm1.CloseCommand.Equals(vm2.CloseCommand));
Assert.That(vm1.Equals(vm2));
}
}
----- LATEST EDITS TO USE MERHDAD"S IDEA
debugger out put
?valueOfThisObject
{Smack.Wpf.ViewModel.RelayCommand}
base {SharpArch.Core.DomainModel.ValueObject}: {Smack.Wpf.ViewModel.RelayCommand}
_canExecute: null
_execute: {Method = {Void _executeClose(System.Object)}}
?valueToCompareTo
{Smack.Wpf.ViewModel.RelayCommand}
base {SharpArch.Core.DomainModel.ValueObject}: {Smack.Wpf.ViewModel.RelayCommand}
_canExecute: null
_execute: {Method = {Void _executeClose(System.Object)}}
?valueOfThisObject.Equals(valueToCompareTo)
false
This is the result after changing the code to:
public ICommand CloseCommand
{
get
{
if (_closeCommand == null)
_closeCommand = new RelayCommand(_executeClose);
return _closeCommand;
}
}
RelayCommand _closeCommand;
void _executeClose(object param) {
OnRequestClose();
}
Are you creating the delegate out of anonymous functions or something? These are the exact delegate equality rules according to C# specification (§7.9.8):
Delegate equality operators
Two delegate instances are considered equal as follows:
If either of the delegate instances is null, they are equal if and only if both are null.
If the delegates have different runtime type they are never equal.
If both of the delegate instances have an invocation list (§15.1), those instances are equal if and only if their invocation lists are the same length, and each entry in one’s invocation list is equal (as defined below) to the corresponding entry, in order, in the other’s invocation list.
The following rules govern the equality of invocation list entries:
If two invocation list entries both refer to the same static method then the entries are equal.
If two invocation list entries both refer to the same non-static method on the same target object (as defined by the reference equality operators) then the entries are equal.
Invocation list entries produced from evaluation of semantically identical anonymous-function-expressions with the same (possibly empty) set of captured outer variable instances are permitted (but not required) to be equal.
So, in your case, it's possible that the delegate instances are referring to the same method in two different objects, or referring to two anonymous methods.
UPDATE: Indeed, the problem is that you are not passing the same method reference when you are calling new RelayCommand(param => OnCloseCommand()). After all, the lambda expression specified here is actually an anonymous method (you are not passing a method reference to OnCloseCommand; you are passing a reference to an anonymous method which takes a single parameter and calls OnCloseCommand). As mentioned in the last line of the specification quotation above, it's not necessary that comparing those two delegates return true.
Side Note: The getter of the CloseCommand property would be simply called get_CloseCommand and not <get_CloseCommand>b__0. This is the compiler generated method name for the anonymous method inside get_CloseCommand method (the CloseCommand getter). This further proves the point I mentioned above.
I don't know anything now about other lines but what if
CollectionAssert.AreEqual(_vm.ProjectActivities, models);
fails just because ReferenceEquality is used?
You have overridden the comparison for RelayCommand but not for ObservableCollection.
And it looks like in case of Delegates Reference equality is used too.
Try to compare by Delegate.Method instead.
Related
I'm trying to understand why calling the function (code below...) of NotifyProportyChanged from AddNoteToList to update the view whenever item added to list isn't working.
It works great when I'm doing an assignment on the list, but if I'm trying to add items and then call manually the NotifyProportyChanged it doesn't.
I know I should use ObservableCollection to solve the problem, but I would like to know why this implementation I wrote is not doing the job.
public class MainWindowVM : INotifyPropertyChanged
{
public Model.MainWindowModel Model { get; set; }
public List<DataProtocol.Note> _notesListVM;
public List<DataProtocol.Note> NotesListVM
{
get
{
return _notesListVM;
}
set
{
_notesListVM = value;
NotifyProportyChanged("NotesListVM");
}
}
//dp:
public event PropertyChangedEventHandler PropertyChanged;
public void NotifyProportyChanged(string propName)
{
if (this.PropertyChanged != null)
this.PropertyChanged(this, new PropertyChangedEventArgs(propName));
}
//Command:
public RelayCommand AddNoteCommand { get; set; }
public void AddNoteToList(object parm)
{
string value = parm.ToString();
NotesListVM.Add(new DataProtocol.Note(value));
NotifyProportyChanged("NotesListVM");
}
public MainWindowVM()
{
Model = new PL.Model.MainWindowModel();
NotesListVM = Model.NotesList;
AddNoteCommand = new RelayCommand(AddNoteToList);
}
}
Even though you are raising PropertyChanged, the object instance NotesListVM hasn't actually changed, only its content has. WPF is optimized enough to realize this and do nothing.
If you really wanted to do it your way, you'd need create and assign a new list each time.
As you say, use ObservableCollection instead, which will fire CollectionChanged.
ItemsControl.ItemsSource is a dependency property, and will only register a change when you actually set the value to a different collection instance - sending a PropertyChanged event isn't sufficient.
As you noted, using an ObservableCollection is the correct way to have your bound U.I. controls detect that items have been added / removed from your list of items.
I'm using the PropertyObserver class in my code to avoid doing string comparisons in PropertyChanged event handling and factor out the handling of null or string.Empty as its argument (Which indicates that all properties of an object has changed).
This class uses PropertyChangedEventManager to register callbacks in the target object and implements IWeakEventListener to respond every time PropertyChanged event is invoked on the source object.
But during the creation of a unit test I found out that the IWeakEventListener.ReceiveWeakEvent() is called N number of times, with N as the number of the registered callbacks. This only occurs when null or string.Empty is specified, not when a valid property name is given in the PropertyChanged event.
Does anyone knows why this is happening and how to fix it? My goal is to do a foreach of the registered handlers ONE time when null is given, so I can update my target object by getting all the properties of the source object. But when ReceiveWeakEvent() is called N times then the foreach will be repeated N times!
To illustrate it, the following is a simplified version of the PropertyObserver class and the source class (I'm using MVVM Light's ObservableObject for INotifyPropertyChanged implementation):
public class PropertyObserver : IWeakEventListener {
public bool ReceiveWeakEvent(Type managerType, object sender, EventArgs e) {
if (managerType == typeof(PropertyChangedEventManager)) {
string propertyName = ((PropertyChangedEventArgs)e).PropertyName;
if (string.IsNullOrEmpty(propertyName)) {
Console.WriteLine ("Foreach registered handlers and invoke one by one");
} else {
Console.WriteLine ("Invoke handler for property {0}", propertyName);
}
return true;
}
return false;
}
}
public class ViewModel : ObservableObject {
private int mProp1;
private int mProp2;
public int Prop1 {
get { return mProp1; }
set {
mProp1 = value;
RaisePropertyChanged("Prop1");
}
}
public int Prop2 {
get { return mProp2; }
set {
mProp2 = value;
RaisePropertyChanged("Prop2");
}
}
public void RaiseAllPropertyChanged() {
RaisePropertyChanged(null);
}
}
And in a console app's Main we can call them like so:
var vm = new ViewModel();
var obs = new PropertyObserver();
// Normally this is done inside the PropertyObserver class.
PropertyChangedEventManager.AddListener(vm, obs, "Prop1");
PropertyChangedEventManager.AddListener(vm, obs, "Prop2");
vm.Prop1 = 1; // Results in a console line "Invoke handler for property Prop1"
vm.Prop2 = 2; // Results in a console line "Invoke handler for property Prop2"
// Results in two console lines: "Foreach registered handlers and invoke one by one", expected is only 1!
vm.RaiseAllPropertyChanged();
Okay, I didn't understand the AddListener() method before. I only need to register a listener once:
PropertyChangedEventManager.AddListener(vm, obs, string.Empty);
to listen to all PropertyChanged events of a source object. Doing this will produce the correct working of the PropertyObserver class:
vm.Prop1 = 1; // "Invoke handler for property Prop1"
vm.Prop2 = 2; // "Invoke handler for property Prop2"
// Now results in one console line "Foreach registered handlers and invoke one by one"
vm.RaisePropertyChanged();
Each registered listener with non-empty third argument (the property name) will respond to only the specified property name and null or string.Empty. So that's why the foreach was invoked twice on the original code.
Background:
I am writing a WPF application, strictly following the MVVM pattern. I have a BaseRepository class as a generic interface for connecting to different databases (EF is not an option), and everything works fine; this is just a technical question.
I use a wrapped ObservableCollection called NotifyingCollection, to subscribe to IEditableObject's ItemEndEdit events (my ViewModelBase entity wrapper implements INotifyPropertyChanged and IEditableObject members).
The provided code sample throws an "'EditItem' is not allowed for this view" exception when the ReadAll method is called upon editing an item in my WPF DataGrid. However, if I replace the line in the method with the commented out part, it works perfectly!
Question:
In other words, it looks like relaying the Linq.Enumerable.Where extension method instead of returning an IEnumerable version of the collection removes functionality from the custom collection; why would that happen if they are both IEnumerable?
Code Sample:
namespace MyCompany.Common.Abstracts
{
using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.ComponentModel;
using System.Data.Common;
using System.Diagnostics;
using System.Linq;
using System.Linq.Expressions;
using MyCompany.Common.Extensions;
using MyCompany.Common.Utilities;
public abstract class BaseRepository<TEntity> : IDisposable where TEntity : ViewModelBase
{
protected BaseRepository()
{
this.EntitySet = new NotifyingCollection<TEntity>();
this.EntitySet.ItemEndEdit += new ViewModelBase.ItemEndEditEventHandler(ItemEndEdit);
this.EntitySet.CollectionChanged += new NotifyCollectionChangedEventHandler(CollectionChanged);
}
protected abstract NotifyingCollection<TEntity> EntitySet { get; set; }
protected virtual void PropertyChanged(object sender, PropertyChangedEventArgs e)
{
Debug.WriteLine(String.Format("Modify '{0}'", e.PropertyName), "PropertyChanged");
}
protected virtual void ItemEndEdit(IEditableObject sender)
{
this.Update(sender as TEntity);
}
protected virtual void CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
var collection = (e.Action == NotifyCollectionChangedAction.Remove) ?
e.OldItems : e.NewItems;
foreach (TEntity entity in collection)
{
switch (e.Action)
{
case NotifyCollectionChangedAction.Add:
entity.PropertyChanged += this.PropertyChanged;
this.Create(entity);
break;
case NotifyCollectionChangedAction.Remove:
entity.PropertyChanged -= this.PropertyChanged;
this.Delete(entity);
break;
default:
Debug.WriteLine(String.Format("{0} '{1}'", e.Action.ToString(), entity.DisplayName), "CollectionChanged");
break;
}
}
}
public virtual bool Create(TEntity entity)
{
Debug.WriteLine(String.Format("Create '{0}'", entity.DisplayName), "CollectionChanged");
return true;
}
public virtual IEnumerable<TEntity> Read(Expression<Func<TEntity, bool>> filter)
{
return this.EntitySet.Where(filter.Compile());
}
public virtual IEnumerable<TEntity> ReadAll()
{
return this.Read(x => true);
//return this.EntitySet;
}
public virtual bool Update(TEntity entity)
{
Debug.WriteLine(String.Format("Update '{0}'", entity.DisplayName), "ItemEndEdit");
return true;
}
public virtual bool Delete(TEntity entity)
{
Debug.WriteLine(String.Format("Delete '{0}'", entity.DisplayName), "CollectionChanged");
return true;
}
public virtual IEnumerable<TColumn> GetColumn<TColumn>(string columnName)
{
var lookupTable = this.Read(x => x != null);
List<TColumn> column = new List<TColumn>();
foreach (TEntity record in lookupTable)
{
column.Add(record.GetPropValue<TColumn>(columnName));
}
var result = column.Distinct();
foreach (TColumn element in result)
{
yield return element;
}
}
public abstract int Commit();
public abstract DbTransaction BeginTransaction();
public abstract void Dispose();
}
}
From MichaelLPerry's blog: Common mistakes while using ObservableCollection
ObservableCollection is imperative. Linq is declarative. The two
cannot be used together without extra help.
Imperative code explicitly acts upon something. When using an
ObservableCollection, you explicitly call Add, Remove, and other
methods to change the collection. You have to decide exactly when and
how to take these actions.
Declarative code implicitly generates something. When using linq, you
declare what a collection will look like, how it will be filtered,
mapped, and transformed. You don’t explicitly modify the collection.
These two paradigms don’t mix. Once you use linq on an
ObservableCollection, it is no longer observable. There are
open-source projects like Bindable Linq that bridge the gap. But
without that extra help, people are often surprised when things don’t
work.
The reurn value of EntitySet.Where method won't be the original NotifyingCollection<TEntity> anymore.
This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
typesafe NotifyPropertyChanged using linq expressions
I'm working on a large team application which is suffering from heavy use of magic strings in the form of NotifyPropertyChanged("PropertyName"), - the standard implementation when consulting Microsoft. We're also suffering from a great number of misnamed properties (working with an object model for a computation module that has hundreds of stored calculated properties) - all of which are bound to the UI.
My team experiences many bugs related to property name changes leading to incorrect magic strings and breaking bindings. I wish to solve the problem by implementing property changed notifications without using magic strings. The only solutions I've found for .Net 3.5 involve lambda expressions. (for example: Implementing INotifyPropertyChanged - does a better way exist?)
My manager is extremely worried about the performance cost of switching from
set { ... OnPropertyChanged("PropertyName"); }
to
set { ... OnPropertyChanged(() => PropertyName); }
where the name is extracted from
protected virtual void OnPropertyChanged<T>(Expression<Func<T>> selectorExpression)
{
MemberExpression body = selectorExpression.Body as MemberExpression;
if (body == null) throw new ArgumentException("The body must be a member expression");
OnPropertyChanged(body.Member.Name);
}
Consider an application like a spreadsheet where when a parameter changes, approximately a hundred values are recalculated and updated on the UI in real-time. Is making this change so expensive that it will impact the responsiveness of the UI? I can't even justify testing this change right now because it would take about 2 days worth of updating property setters in various projects and classes.
I did a thorough test of NotifyPropertyChanged to establish the impact of switching to the lambda expressions.
Here were my test results:
As you can see, using the lambda expression is roughly 5 times slower than the plain hard-coded string property change implementation, but users shouldn't fret, because even then it's capable of pumping out a hundred thousand property changes per second on my not so special work computer. As such, the benefit gained from no longer having to hard-code strings and being able to have one-line setters that take care of all your business far outweighs the performance cost to me.
Test 1 used the standard setter implementation, with a check to see that the property had actually changed:
public UInt64 TestValue1
{
get { return testValue1; }
set
{
if (value != testValue1)
{
testValue1 = value;
InvokePropertyChanged("TestValue1");
}
}
}
Test 2 was very similar, with the addition of a feature allowing the event to track the old value and the new value. Because this features was going to be implicit in my new base setter method, I wanted to see how much of the new overhead was due to that feature:
public UInt64 TestValue2
{
get { return testValue2; }
set
{
if (value != testValue2)
{
UInt64 temp = testValue2;
testValue2 = value;
InvokePropertyChanged("TestValue2", temp, testValue2);
}
}
}
Test 3 was where the rubber met the road, and I get to show off this new beautiful syntax for performing all observable property actions in one line:
public UInt64 TestValue3
{
get { return testValue3; }
set { SetNotifyingProperty(() => TestValue3, ref testValue3, value); }
}
Implementation
In my BindingObjectBase class, which all ViewModels end up inheriting, lies the implementation driving the new feature. I've stripped out the error handling so the meat of the function is clear:
protected void SetNotifyingProperty<T>(Expression<Func<T>> expression, ref T field, T value)
{
if (field == null || !field.Equals(value))
{
T oldValue = field;
field = value;
OnPropertyChanged(this, new PropertyChangedExtendedEventArgs<T>(GetPropertyName(expression), oldValue, value));
}
}
protected string GetPropertyName<T>(Expression<Func<T>> expression)
{
MemberExpression memberExpression = (MemberExpression)expression.Body;
return memberExpression.Member.Name;
}
All three methods meet at the OnPropertyChanged routine, which is still the standard:
public virtual void OnPropertyChanged(object sender, PropertyChangedEventArgs e)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
handler(sender, e);
}
Bonus
If anyone's curious, the PropertyChangedExtendedEventArgs is something I just came up with to extend the standard PropertyChangedEventArgs, so an instance of the extension can always be in place of the base. It leverages knowledge of the old value when a property is changed using SetNotifyingProperty, and makes this information available to the handler.
public class PropertyChangedExtendedEventArgs<T> : PropertyChangedEventArgs
{
public virtual T OldValue { get; private set; }
public virtual T NewValue { get; private set; }
public PropertyChangedExtendedEventArgs(string propertyName, T oldValue, T newValue)
: base(propertyName)
{
OldValue = oldValue;
NewValue = newValue;
}
}
Personally I like to use Microsoft PRISM's NotificationObject for this reason, and I would guess that their code is reasonably optimized since it's created by Microsoft.
It allows me to use code such as RaisePropertyChanged(() => this.Value);, in addition to keeping the "Magic Strings" so you don't break any existing code.
If I look at their code with Reflector, their implementation can be recreated with the code below
public class ViewModelBase : INotifyPropertyChanged
{
// Fields
private PropertyChangedEventHandler propertyChanged;
// Events
public event PropertyChangedEventHandler PropertyChanged
{
add
{
PropertyChangedEventHandler handler2;
PropertyChangedEventHandler propertyChanged = this.propertyChanged;
do
{
handler2 = propertyChanged;
PropertyChangedEventHandler handler3 = (PropertyChangedEventHandler)Delegate.Combine(handler2, value);
propertyChanged = Interlocked.CompareExchange<PropertyChangedEventHandler>(ref this.propertyChanged, handler3, handler2);
}
while (propertyChanged != handler2);
}
remove
{
PropertyChangedEventHandler handler2;
PropertyChangedEventHandler propertyChanged = this.propertyChanged;
do
{
handler2 = propertyChanged;
PropertyChangedEventHandler handler3 = (PropertyChangedEventHandler)Delegate.Remove(handler2, value);
propertyChanged = Interlocked.CompareExchange<PropertyChangedEventHandler>(ref this.propertyChanged, handler3, handler2);
}
while (propertyChanged != handler2);
}
}
protected void RaisePropertyChanged(params string[] propertyNames)
{
if (propertyNames == null)
{
throw new ArgumentNullException("propertyNames");
}
foreach (string str in propertyNames)
{
this.RaisePropertyChanged(str);
}
}
protected void RaisePropertyChanged<T>(Expression<Func<T>> propertyExpression)
{
string propertyName = PropertySupport.ExtractPropertyName<T>(propertyExpression);
this.RaisePropertyChanged(propertyName);
}
protected virtual void RaisePropertyChanged(string propertyName)
{
PropertyChangedEventHandler propertyChanged = this.propertyChanged;
if (propertyChanged != null)
{
propertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
public static class PropertySupport
{
// Methods
public static string ExtractPropertyName<T>(Expression<Func<T>> propertyExpression)
{
if (propertyExpression == null)
{
throw new ArgumentNullException("propertyExpression");
}
MemberExpression body = propertyExpression.Body as MemberExpression;
if (body == null)
{
throw new ArgumentException("propertyExpression");
}
PropertyInfo member = body.Member as PropertyInfo;
if (member == null)
{
throw new ArgumentException("propertyExpression");
}
if (member.GetGetMethod(true).IsStatic)
{
throw new ArgumentException("propertyExpression");
}
return body.Member.Name;
}
}
If you're concerned that the lambda-expression-tree solution might be too slow, then profile it and find out. I suspect the time spent cracking open the expression tree would be quite a bit smaller than the amount of time the UI will spend refreshing in response.
If you find that it is too slow, and you need to use literal strings to meet your performance criteria, then here's one approach I've seen:
Create a base class that implements INotifyPropertyChanged, and give it a RaisePropertyChanged method. That method checks whether the event is null, creates the PropertyChangedEventArgs, and fires the event -- all the usual stuff.
But the method also contains some extra diagnostics -- it does some Reflection to make sure that the class really does have a property with that name. If the property doesn't exist, it throws an exception. If the property does exist, then it memoizes that result (e.g. by adding the property name to a static HashSet<string>), so it doesn't have to do the Reflection check again.
And there you go: your automated tests will start failing as soon as you rename a property but fail to update the magic string. (I'm assuming you have automated tests for your ViewModels, since that's the main reason to use MVVM.)
If you don't want to fail quite as noisily in production, you could put the extra diagnostic code inside #if DEBUG.
Actually we discussed this aswell for our projects and talked alot about the pros and cons. In the end, we decided to keep the regular method but used a field for it.
public class MyModel
{
public const string ValueProperty = "Value";
public int Value
{
get{return mValue;}
set{mValue = value; RaisePropertyChanged(ValueProperty);
}
}
This helps when refactoring, keeps our performance and is especially helpful when we use PropertyChangedEventManager, where we would need the hardcoded strings again.
public bool ReceiveWeakEvent(Type managerType, object sender, System.EventArgs e)
{
if(managerType == typeof(PropertyChangedEventManager))
{
var args = e as PropertyChangedEventArgs;
if(sender == model)
{
if (args.PropertyName == MyModel.ValueProperty)
{
}
return true;
}
}
}
One simple solution is to simply pre-process all files before compilation, detect the OnPropertyChanged calls that are defined in set { ... } blocks, determine the property name and fix the name parameter accordingly.
You could do this using an ad-hoc tool (that would be my recommendation), or use a real C# (or VB.NET) parser (like those which can be found here: Parser for C#).
I think it's reasonable way to do it. Of course, it's not very elegant nor smart, but it has zero runtime impact, and follows Microsoft rules.
If you want to save some compile time, you could have both ways using compilation directives, like this:
set
{
#if DEBUG // smart and fast compile way
OnPropertyChanged(() => PropertyName);
#else // dumb but efficient way
OnPropertyChanged("MyProp"); // this will be fixed by buid process
#endif
}
Question is simple: how can I trigger a change on the dataObject without acutaly changing the dataObject, and see this change on the visual?
DataObject:
ProductData : INotifyPropertyChanged
{
private ProductPartData myProductPartData;
public ProductPartData ProductPartData
{
get
{
return myProductPartData;
}
set
{
if (value != myProductPartData)
{
myProductPartData = value;
OnNotifyPropertyChanged("ProductPartData");
}
}
}
}
DataTemplate:
<DataTemplate
DataType="{x:Type ProductData}"
>
<VisualProduct
ProductPartData="{Binding Path=ProductPartData, Mode=OneWay}"
/>
</DataTemplate>
And now in a VM I have:
product.OnNotifyPropertyChanged("ProductPartData");
Problem:
Even if the getter for ProductPart is called when I execute OnNotifyPropertyChanged, the visual is not notified, because is the same instance of the ProductPartData.
How do I trigger a change seen by the Visual without changing the instance?
Thank you,
Daniel,
A solution is to use UpdateTarget() method of the BindingExpression class, this way the target of the binding gets refreshed no matter what; of course, your converter will also be hit - if any. Since I'm guessing you don't have access to your visual in the Product, you could use an attached property and in its callback, you can get the BindingExpression and call UpdateTarget() on it.
Note that I'm using a simple TextBlock as the visual of the data object.
public class BindingHelper
{
public static bool GetRefreshBinding(DependencyObject obj)
{
return (bool) obj.GetValue(RefreshBindingProperty);
}
public static void SetRefreshBinding(DependencyObject obj, bool value)
{
obj.SetValue(RefreshBindingProperty, value);
}
// Using a DependencyProperty as the backing store for RefreshBinding. This enables animation, styling, binding, etc...
public static readonly DependencyProperty RefreshBindingProperty =
DependencyProperty.RegisterAttached("RefreshBinding", typeof(bool), typeof(BindingHelper), new UIPropertyMetadata(false, OnRefreshBindingPropertyChanged));
static void OnRefreshBindingPropertyChanged(DependencyObject o, DependencyPropertyChangedEventArgs ea)
{
TextBlock elem = o as TextBlock;
if (elem != null)
{
BindingExpression bEx = elem.GetBindingExpression(TextBlock.TextProperty);
if (bEx != null)
{
bEx.UpdateTarget();
}
}
}
}
Also, in your data object that you can create a new bool property(let's name it ShouldRefresh) that is bound to the attached property within the template - this will trigger the AP's property changing:
<DataTemplate DataType="{x:Type local:ProductData}">
<TextBlock Text="{Binding Path=Name, Converter={StaticResource BlankConverter}}"
local:BindingHelper.RefreshBinding="{Binding Path=ShouldRefresh}"/>
</DataTemplate>
So, this way, whenever you want to update the target through binding, you can set:
ShouldRefresh = !ShouldRefresh
in your data class.
HTH.
If you raise a PropertyChanged event and the new value of the property is equal to the value WPF already has, it will simply ignore you. You have a couple of options:
The "fast" way is to set the property to null and then back to the correct value again, ensuring PropertyChanged events are raised each time. It's dirty but it works every time.
The "right" way is to force a binding refresh as discussed in this post by Jaime Rodriguez. Because your visual is data-templated though getting the "dependencyObject" to pass into the call in that post is a little tricky. You may end up needing to use the template's FindName method as discussed in this post by Josh Smith.
We encountered this kind of issue with data coming from a database and converted to a DTO (data transfert object).
Our base class for DTO override Object's method such as Equals() and GetHashCode() as follow:
public override Boolean Equals(Object obj)
{
// Null reference
if (null == obj)
return false;
// Same reference
if (Object.ReferenceEquals(this, obj))
return true;
EntityDTOBase<TEntity> entiteObj = obj as EntityDTOBase<TEntity>;
if (null == entiteObj)
return false;
else
return Equals(entiteObj);
}
public Boolean Equals(EntityDTOBase<TEntity> other)
{
// Null reference
if (null == other)
return false;
// Same reference
if (Object.ReferenceEquals(this, other))
return true;
// No Id: cannot be compared, return false
if (this.id == TypeHelper.DefaultValue<long>())
return false;
// Id comparison
if (this.id != other.id)
return false;
return true;
}
public override Int32 GetHashCode()
{
return this.id.GetHashCode();
}
So the problem was when we load again the same entity from the database, since the ID is the same, some binding were not properly updated.
This particular issue was circumvented by adding an additional virtual EqualsExtended() method which default implementation simply returns true:
protected virtual Boolean EqualsExtended(EntityDTOBase<TEntity> other)
{
return true;
}
public Boolean Equals(EntityDTOBase<TEntity> other)
{
/// Same code as before (except last line):
return EqualsExtended(other);
}
Now in any implementation of our DTO class we can add some logic to make Equals() returning false in some situations, for example by adding a timestamp when data is retrieved from the database :
protected override Boolean EqualsExtended(EntityDTOBase<Act> other
{
if (this.Timestamp != other.Timestamp)
{
return false;
}
return true;
}
Long story short, one way to workaround this issue is to make your class instance look different whenever you want the GUI to update accordingly.
The problem might be that you are returning GuiProductPartData typed myProductPartData with ProductPartData typed ProductPartData? But in any case this shouldn't be like this :)
Also it's not a great practice to have the variable name same as the type, so you shouldn't have a ProductPartData ProductPartData property.
Naming conventions aside (and assuming just typos on the typing) the problem probably resides inside your ProductPartData class. Does it implement INotifyPropertyChanged as well?