I'm developing an application in c# WPF.
I use a class PropertyChangedNotifier to manage INotifyPropertyChanged (see that link).
I use a classical ViewModelBase :
public class ViewModelBase : INotifyPropertyChanged, IRequestClose
{
public PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = _propertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
}
In my MainViewModel : ViewModelBase I have a PropertyChangedNotifier<MainViewModel> working like this :
class MainViewModel : ViewModelBase
{
private PropertyChangedNotifier<MainViewModel> _notifier;
public new event PropertyChangedEventHandler PropertyChanged
{
add { _notifier.PropertyChanged += value; }
remove { _notifier.PropertyChanged -= value; }
}
public MainViewModel()
{
_notifier = new PropertyChangedNotifier<MainViewModel>(this);
}
protected new void OnPropertyChanged(string propertyName)
{
_notifier.OnPropertyChanged(propertyName);
}
}
But changes are not detected, when a value changes, my MainWindows doesn't refresh (without using PropertyChangedNotifier it works). I saw that system initiates windows by using WindowsBase.dll!System.ComponentModel.PropertyChangedEventManager.StartListening(object source) and then I saw that my ViewModelBase.PropertyChanged is not null when the constructor for MainViewModel is called.
Is it possible to make something like this :
public MainViewModel()
{
_notifier = new PropertyChangedNotifier<MainViewModel>(this);
_notifier.PropertyChanged = base.PropertyChanged;
}
And will that fix my bug ?
Edit:
PropertyChangeNotifier from link :
[AttributeUsage( AttributeTargets.Property )]
public class DepondsOnAttribute : Attribute
{
public DepondsOnAttribute( string name )
{
Name = name;
}
public string Name { get; }
}
public class PropertyChangedNotifier<T> : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public PropertyChangedNotifier( T owner )
{
mOwner = owner;
}
public void OnPropertyChanged( string propertyName )
{
var handler = PropertyChanged;
if( handler != null ) handler( mOwner, new PropertyChangedEventArgs( propertyName ) );
List<string> dependents;
if( smPropertyDependencies.TryGetValue( propertyName, out dependents ) )
{
foreach( var dependent in dependents ) OnPropertyChanged( dependent );
}
}
static PropertyChangedNotifier()
{
foreach( var property in typeof( T ).GetProperties() )
{
var dependsOn = property.GetCustomAttributes( true )
.OfType<DepondsOnAttribute>()
.Select( attribute => attribute.Name );
foreach( var dependency in dependsOn )
{
List<string> list;
if( !smPropertyDependencies.TryGetValue( dependency, out list ) )
{
list = new List<string>();
smPropertyDependencies.Add( dependency, list );
}
if (property.Name == dependency)
throw new ApplicationException(String.Format("Property {0} of {1} cannot depends of itself", dependency, typeof(T).ToString()));
list.Add( property.Name );
}
}
}
private static readonly Dictionary<string, List<string>> smPropertyDependencies = new Dictionary<string, List<string>>();
private readonly T mOwner;
}
The problem is that when my MainViewModel is created, the base.PropertyChanged is null. The constructor is called, fields/properties initialized (some call OnPropertyChanged) and after the MainView.InitializeComponent() add a handler in base.PropertyChanged (I attached a debugger to see that). This adding is done in the ViewModelBase, ignoring the public new event PropertyChangedEventHandler PropertyChanged in MainViewModel.
I've the idea to check if base.PropertyChanged has been modify and copy it.
I've done this, I don't know if there is a good idea, what do you think ?
public class ViewModelBase : INotifyPropertyChanged, IRequestClose
{
protected event PropertyChangedEventHandler _propertyChanged;
protected bool propertyAdded { get; private set; }
public event PropertyChangedEventHandler PropertyChanged
{
add
{
_propertyChanged += value;
propertyAdded = true;
}
remove { _propertyChanged -= value; }
}
protected void OnPropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = _propertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
public Delegate[] Get_PropertyChanged()
{
isPropertyAdded = false;
Delegate[] delegates = new Delegate[0];
if (_propertyChanged != null)
{
delegates = _propertyChanged.GetInvocationList();
_propertyChanged = null;
}
return delegates;
}
}
and
class MainViewModel : ViewModelBase
{
private PropertyChangedNotifier<MainViewModel> _notifier;
public new event PropertyChangedEventHandler PropertyChanged
{
add { _notifier.PropertyChanged += value; }
remove { _notifier.PropertyChanged -= value; }
}
public MainViewModel()
{
_notifier = new PropertyChangedNotifier<MainViewModel>(this);
}
protected new void OnPropertyChanged(string propertyName)
{
if (isPropertyAdded)
{
foreach (Delegate d in Get_PropertyChanged())
{
_notifier.PropertyChanged += (PropertyChangedEventHandler)d;
}
}
_notifier.OnPropertyChanged(propertyName);
}
}
Seems like a convoluted solution to a simple NotifyPropertyChanged("dependentProperty"), although I do understand the desire to engineer fancy solutions. It would make sense if it had some performance/loc benefits, but it doesn't. It just adds complexity.
Related
Solution link: https://github.com/fallingsappy/portfolio/tree/master/DDrop
Hi, everyone! I have a problem with IsEnabled binding of TabItem. It kinda hard to explain, but I try. I have a collection called User. User contains UserSeries collection of Series class. UserSeries have another collection called DropPhotosSeries. Also UserSeries have property called:
private bool _canDrawPlot;
public bool CanDrawPlot
{
get
{
return _dropPhotosSeries?.Where(x => x.Drop.RadiusInMeters != null).ToList().Count > 1 && _dropPhotosSeries?.Where(x => x.Drop.RadiusInMeters == null).ToList().Count == 0;
}
set
{
_canDrawPlot = value;
OnPropertyChanged(new PropertyChangedEventArgs("CanDrawPlot"));
}
}
Based on this property. TabItem should be enabled or disabled. But this TabItem doesn't seems to notice changes of the property. I've tried many approaches, but still nothing. Here some XAML:
<TabItem IsEnabled="{Binding ElementName=AppMainWindow, Path=User.IsAnySelectedSeriesCantDrawPlot}" Name="CombinedSeriesPlot" Header="Общий график серий">
<uc:ScatterPlot User="{Binding User, ElementName=AppMainWindow, UpdateSourceTrigger=PropertyChanged}"/>
</TabItem>
Some of the codebehind:
public class User : INotifyPropertyChanged
{
private ObservableCollection<Series> _userSeries;
public ObservableCollection<Series> UserSeries
{
get
{
return _userSeries;
}
set
{
_userSeries = value;
OnPropertyChanged(new PropertyChangedEventArgs("UserSeries"));
}
}
private bool _isAnySelectedSeriesCantDrawPlot;
[NotMapped]
public bool IsAnySelectedSeriesCantDrawPlot
{
get
{
return _userSeries?.Where(x => x?.CanDrawPlot == false).ToList().Count > 0;
}
set
{
_isAnySelectedSeriesCantDrawPlot = value;
OnPropertyChanged(new PropertyChangedEventArgs("IsAnySelectedSeriesCantDrawPlot"));
}
}
public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChanged(PropertyChangedEventArgs e)
{
PropertyChanged?.Invoke(this, e);
}
}
I've tried to create dependent property in UserClass, but i think property in UserSeries should be enough somehow:
public class Series : INotifyPropertyChanged
{
private ObservableCollection<DropPhoto> _dropPhotosSeries;
public ObservableCollection<DropPhoto> DropPhotosSeries
{
get
{
return _dropPhotosSeries;
}
set
{
_dropPhotosSeries = value;
OnPropertyChanged(new PropertyChangedEventArgs("DropPhotosSeries"));
}
}
private bool _canDrawPlot;
public bool CanDrawPlot
{
get
{
return _dropPhotosSeries?.Where(x => x.Drop.RadiusInMeters != null).ToList().Count > 1 && _dropPhotosSeries?.Where(x => x.Drop.RadiusInMeters == null).ToList().Count == 0;
}
set
{
_canDrawPlot = value;
OnPropertyChanged(new PropertyChangedEventArgs("CanDrawPlot"));
}
}
public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChanged(PropertyChangedEventArgs e)
{
PropertyChanged?.Invoke(this, e);
}
}
Maybe DropPhoto should throw some event, that CanDrowProperty need to update itself and update TabItem? I really don't know. But here is DropPhoto class:
public class DropPhoto : INotifyPropertyChanged
{
private Drop _drop;
public Drop Drop
{
get
{
return _drop;
}
set
{
_drop = value;
OnPropertyChanged(new PropertyChangedEventArgs("Drop"));
}
}
public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChanged(PropertyChangedEventArgs e)
{
PropertyChanged?.Invoke(this, e);
}
}
It's contains Drop class, which has RadiusInMeters property:
public class Drop : INotifyPropertyChanged
{
private double? _radiusInMeters;
public double? RadiusInMeters
{
get
{
return _radiusInMeters;
}
set
{
_radiusInMeters = value;
OnPropertyChanged(new PropertyChangedEventArgs("RadiusInMeters"));
}
}
public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChanged(PropertyChangedEventArgs e)
{
PropertyChanged?.Invoke(this, e);
}
}
For full solution. Please check github link.
It's hard to find out what exactly wrong in your code but I'll try to answer directly to the question with simple example code
public class FirstClass : INotifyPropertyChanged
{
SecondClass secondClass = new SecondClass();
private bool _firstProperty;
public bool FirstProperty
{
get => _firstProperty;
set
{
_firstProperty = value;
OnPropertyChanged(nameof(FirstProperty));
}
}
private void SecondClass_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
if (e.PropertyName == nameof(secondClass.SecondProperty)) FirstProperty = secondClass.SecondProperty;
}
public FirstClass()
{
secondClass.PropertyChanged += SecondClass_PropertyChanged;
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propertyName) =>
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
public class SecondClass : INotifyPropertyChanged
{
private bool _secondProperty;
public bool SecondProperty
{
get => _secondProperty;
set
{
_secondProperty = value;
OnPropertyChanged(nameof(SecondProperty));
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propertyName) =>
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
In this example any change of SecondProperty will cause change of the FirstProperty.
So lets say I have class:
SurfaceLoad.cs
public class SurfaceLoad : Load
{
private double force;
public double Force{ get => force; set{force = value;}}
public SurfaceLoad(double f){
Force = f;
}
public SurfaceLoad(){
Force = 0.0;
}
}
And I have my ViewModel:
SurfaceLoadViewModel.cs
public class SurfaceLoadViewModel{
private SurfaceLoad surfaceLoad = new SurfaceLoad();
public double Force{
get => surfaceLoad.Force;
set{
surfaceLoad.Force = value;
NotifyPropertChanged("Force");
}
}
public SurfaceLoadViewModel(){
}
}
As I already found out, in a good MVVM-manner I have to put all the accessors for my SurfaceLoad-members into the ViewModel, as the Model itself should not contain any interaction-logic.
Question:
Now I have multiple implementations of Load (SurfaceLoad, PointLoad, AreaLoad,...). All of those are a member of the class called LoadContainer, which is only used to manage a package of loads, which occur at the same time.
How do I efficiently manage all those types in a ViewModel? Do I have to wrap a Property around each and every value?
You need to implement INotifyPropertyChanged interface in SurfaceLoad class.
public class SurfaceLoad : Load, INotifyPropertyChanged
{
private double force;
public double Force
{
get { return force; }
set
{
force = value;
NotifyPropertyChanged("Force");
}
}
public SurfaceLoad(double f)
{
Force = f;
}
public SurfaceLoad()
{
Force = 0.0;
}
public event PropertyChangedEventHandler PropertyChanged;
protected void NotifyPropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = this.PropertyChanged;
if (handler != null)
{
var e = new PropertyChangedEventArgs(propertyName);
handler(this, e);
}
}
protected void NotifyPropertyChanged<T>(Expression<Func<T>> propertySelector)
{
var propertyChanged = PropertyChanged;
if (propertyChanged != null)
{
string propertyName = GetPropertyName(propertySelector);
propertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
The first two comments have already helped me a lot.
In addition, I did some research and came to the conclusion that it is much more efficient to write the "NotifyPropertyChanged" handlers into the models, since this procedure does not work against the mvvm-principles!
I have the visibility of a progress bar bound to The following property within my viewmodel:
public string CalcProgVisibility
{
get
{
return Calculation.CalcProgVisibility;
}
set
{
}
}
Calculation is my model, which can change the value. When the value changes within the model, what do I need to do to make sure the view is aware of this change?
EDIT:
Here is the property within my model too. I am using onpropertychanged but its not making it to the view.
I am changing the value within the model, the view is bound to my viewmodel and the viewmodel si trying to return a value taken from the model. I am updating the value on the model, and cannot push the fact that it has updated the value all the way down to the view, I can only get the viewmodel to see it has changed...
I updated the entire code. I hope it's clear now.
Define your control BindingMode = TwoWay
<TextBox Visibility="{Binding Path=CalcProgVisibility, Mode=TwoWay}"...
and call the OnPropertyChanged method on the setter of the property in your view model and also in your model
//Model
public class Calculation : INotifyPropertyChanged
{
private string _calcProgVisibility;
public string CalcProgVisibility
{
get { return _calcProgVisibility; }
set
{
_calcProgVisibility = value;
OnPropertyChanged("CalcProgVisibility");
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string propertyName)
{
RaisePropertyChanged(propertyName);
}
private void RaisePropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler == null) return;
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
//ViewModel
public class ViewModel : INotifyPropertyChanged
{
public ViewModel(Calculation model)
{
this.CalcProgVisibility = model.CalcProgVisibility;
model.PropertyChanged += (s, e) => UpdateEntity(s as Calculation);
}
private void UpdateEntity(Calculation source)
{
CalcProgVisibility = source.CalcProgVisibility;
}
private string _calcProgVisibility;
public string CalcProgVisibility
{
get { return _calcProgVisibility; }
set
{
_calcProgVisibility = value;
OnPropertyChanged("CalcProgVisibility");
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string propertyName)
{
RaisePropertyChanged(propertyName);
}
private void RaisePropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler == null) return;
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
Your Viewmodel has to implement the INotifyPropertyChanged Interface. To fire it in your case your viewmodel must also be aware of changes in your model object. So your model object could also implement INotifyPropertyChanged, or you use some form of the observer pattern.
If your model implements INotifyPropertyChanged, your viewmodel must manually register for this event and implement an handler. This could in turn trigger the PropertyChange event of the viewmodel then.
Another but in my opinion ugly way would be to scan (per timer or background thread) through your viemodel and check if a value changed since the last scan and then trigger a property changed event.
The first solution could look like this:
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System.ComponentModel;
using System.Runtime.CompilerServices;
namespace StackOverflow
{
[TestClass]
public class IntegrationTest
{
[TestMethod]
public void NotifyPropertyChangeShouldFireOnViewModelWhenModelChanges()
{
//Arrange
Model model = new Model();
ViewModel sut = new ViewModel(model);
bool notifyPropertyChangeOnViewModelWasCalled = false;
sut.PropertyChanged += (sender, e) => { notifyPropertyChangeOnViewModelWasCalled = true; };
//Act
model.CalcValue = 4711;
//Assert
Assert.IsTrue(notifyPropertyChangeOnViewModelWasCalled, "NotifyPropertyChange was not fired on ViewModel");
}
}
public class ObjectWithNotifyPropertyChanged : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected void RaisePropertyChanged([CallerMemberName]string propertyName = "")
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
}
public class Model : ObjectWithNotifyPropertyChanged
{
private double calcValue;
public double CalcValue
{
get
{
return calcValue;
}
set
{
if (calcValue != value)
{
calcValue = value;
RaisePropertyChanged();
}
}
}
}
public class ViewModel : ObjectWithNotifyPropertyChanged
{
public ViewModel(Model model)
{
this.model = model;
model.PropertyChanged += model_PropertyChanged;
}
void model_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
switch (e.PropertyName)
{
case "CalcValue":
RaisePropertyChanged("CalcValue");
break;
}
}
private Model model;
public double CalcValue
{
get
{
return model.CalcValue;
}
}
}
}
I am doing my first project in .net and I am trying to use MVVM pattern. How can I share data between my ViewModel and my ICommand class? My ViewModel look like this:
public class WorkClass : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private Book mybook;
private ObservableCollection<Book> bookList;
public Book Mybook
{
get { return mybook; }
set
{
book = value;
OnPropertyChanged("Mybook");
}
}
public ObservableCollection<Book> BookList
{
get { return bookList; }
private set
{
bookList = value;
OnPropertyChanged("BookList");
}
}
testCommand2 test = new testCommand2();
public ICommand Test { get { return test; } }
private class testCommand2 : ICommand
{
public void Execute(object parameter)
{
// Do stuff here
}
public bool CanExecute(object parameter)
{
return true;
}
public event EventHandler CanExecuteChanged
{
add { CommandManager.RequerySuggested += value; }
remove { CommandManager.RequerySuggested -= value; }
}
}
protected void OnPropertyChanged(string name)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(name));
}
}
}
I implemented ICommand as inner class, but I tried also an "stand-alone" class but couldn't get it work either. I want my command "test" to work with and share data with WorkClass properties BookList and Mybook. (Book is class I defined containing only some string properties)
My app has two windows now, in the mainwindow I create instance of WorkClass and insert it to its datacontext. Then I pass it through datacontext to other window like this (is this correct?):
Window1 w = new Window1();
w.DataContext = this.DataContext;
w.ShowDialog();
I want my commands from those windows to perform changes on the data from this WorkClass instance. I hope it is clear what I am asking. I know it would be best to use RelayCommand or DelegateCommand classes which are available through the internet, but since this is project to the school I am afraid it would be taken as copying someone else's work.
If you need me to post some more from the code or explain anything I will edit.
Thank you for help.
You need to initialize a command by a view model instance and delegate command execution to the method of the View Model class
public class WorkClass : INotifyPropertyChanged
{
public WorkClass()
{
test = new testCommand2(this);
}
// Will be called on command execturion
public Test2Action()
{
}
public event PropertyChangedEventHandler PropertyChanged;
private Book mybook;
private ObservableCollection<Book> bookList;
public Book Mybook
{
get { return mybook; }
set
{
book = value;
OnPropertyChanged("Mybook");
}
}
public ObservableCollection<Book> BookList
{
get { return bookList; }
private set
{
bookList = value;
OnPropertyChanged("BookList");
}
}
testCommand2 test;
public ICommand Test { get { return test; } }
private class testCommand2 : ICommand
{
private readonly WorkClass _wc;
public testCommand2(WorkClass wc)
{
_wc = wc;
}
public void Execute(object parameter)
{
_wc.Test2Action();
}
public bool CanExecute(object parameter)
{
return true;
}
public event EventHandler CanExecuteChanged
{
add { CommandManager.RequerySuggested += value; }
remove { CommandManager.RequerySuggested -= value; }
}
}
protected void OnPropertyChanged(string name)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(name));
}
}
}
I have a WPF dialog that is bound to a list of ObservableCollection<MyEntity> type. In the dialog, I want the "OK" button to be enabled only if changes are made to the ObservableCollection<MyEntity> list - that includes adding/removing items from the list and modifying the individual items in the list.
For adding/removing items from the list, it is easy - I implemented a handler for the CollectionChanged event.
What I don't know how to do is when an individual item is modified. Say, MyEntity.Name="New Value", what interface does MyEntity class need to implement to make it 'observable'?
MyEntity needs to implement INotifyPropertyChanged, then when a property change occurs you fire the PropertyChanged event. Like this:
public class MyEntity : INotifyPropertyChanged
{
public bool MyFlag
{
get { return _myFlag; }
set
{
_myFlag = value;
OnPropertyChanged("MyFlag");
}
}
protected void OnPropertyChanged(string propertyName)
{
if (this.PropertyChanged != null)
{
this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
public event PropertyChangedEventHandler PropertyChanged;
}
Two ways to approach this are:
have an event listener internal to the object which then sets an IsDirty flag whenever a property changes. Then OK button is bound to a command (check out the usage of the ICommand interface), and in the CanExecute method of the command you check if any of the objects in the ObservableCollection have been set to dirty. This check can be done with a simple LINQ statement: myCollection.Any(x => x.IsDirty == true)
this method is more clunky and smelly.... have an external object listening for changes (by subscribing to the PropertyChanged event on each object), and that external listener can then enable the OK button (via databinding or by setting it directly).
I like the answer provided by slugster, here is an alternative building on slugster's answer.
If you bind to your OK button using DelegateCommnd you can add event handlers for CollectionChanged and PropertyChanged to change a simple boolean flag to control the state of the OK button.
public class MainViewModel : ViewModelBase
{
public DelegateCommand<object> RunCommand { get; set; }
public DelegateCommand<object> OkCommand { get; set; }
private bool enableOk = false;
private bool setOK = false;
private ObservableCollection<MyEntity> _entites = new ObservableCollection<MyEntity>();
public MainViewModel()
{
_entites.CollectionChanged += (s, e) =>
{
if (e.Action == NotifyCollectionChangedAction.Add)
{
// handle property changing
foreach (MyEntity item in e.NewItems)
{
((INotifyPropertyChanged)item).PropertyChanged += (s1, e1) => { if (setOK) enableOk = true; };
}
}
// handle collection changing
if (setOK) enableOk = false;
};
MyEntity me1 = new MyEntity { Name = "Name", Information = "Information", Details = "Detials" };
MyEntity me2 = new MyEntity { Name = "Name", Information = "Information", Details = "Detials" };
MyEntity me3 = new MyEntity { Name = "Name", Information = "Information", Details = "Detials" };
_entites.Add(me1);
_entites.Add(me2);
_entites.Add(me3);
// allow collection changes now to start enabling the ok button...
setOK = true;
RunCommand = new DelegateCommand<object>(OnRunCommnad, CanRunCommand);
OkCommand = new DelegateCommand<object>(OnOkCommnad, CanOkCommand);
}
private void OnRunCommnad(object obj)
{
MyEntity me = new MyEntity { Name = "Name", Information = "Information", Details = "Detials" };
// causes ok to become enabled
_entites.Add(me);
MyEntity first = _entites[0];
// causes ok to become enabled
first.Name = "Zamboni";
}
private bool CanRunCommand(object obj)
{
return true;
}
private void OnOkCommnad(object obj)
{
}
private bool CanOkCommand(object obj)
{
return enableOk;
}
}
Here is a version MyEntity (similar to the one provided by slugster):
Only the Name property fires an event in this example...
public class MyEntity : INotifyPropertyChanged
{
private string _name = string.Empty;
public string Name
{
get
{
return _name;
}
set
{
_name = value;
OnPropertyChanged("Name");
}
}
public string Information { get; set; }
public string Details { get; set; }
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
}
You should implement INotifyPropertyChanged. You could do it by the following way
(as you can see, this implementation is fully thread safe)
private readonly object _sync = new object();
public event PropertyChangedEventHandler PropertyChanged
{
add { lock (_sync) _propertyChanged += value; }
remove { lock (_sync) _propertyChanged -= value; }
} private PropertyChangedEventHandler _propertyChanged;
protected void OnPropertyChanged(Expression<Func<object>> propertyExpression)
{
OnPropertyChanged(GetPropertyName(propertyExpression));
}
protected string GetPropertyName(Expression<Func<object>> propertyExpression)
{
MemberExpression body;
if (propertyExpression.Body is UnaryExpression)
body = (MemberExpression) ((UnaryExpression) propertyExpression.Body).Operand;
else
body = (MemberExpression) propertyExpression.Body;
return body.Member.Name;
}
protected virtual void OnPropertyChanged(string propertyName)
{
var handler = _propertyChanged;
if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
}
Following the implementation I described above, you can notify about your changes by two ways
1) The first way
public int MyProperty
{
get { return _myProperty; }
set
{
if (value != __myProperty)
{
_subVersion = value;
OnPropertyChanged(MyPropertyPropertyName);
}
}
} private int _myProperty; const string MyPropertyPropertyName = "MyProperty";
2) And the second way
public int MyProperty
{
get { return _myProperty; }
set
{
if (value != _myProperty)
{
_subVersion = value;
OnPropertyChanged(() => MyProperty);
}
}
} private int _myProperty;
Another solution could be a custom observable collection that requires items to implement INotifyPropertyChanged. The user must attach a handler to the OnItemPropertyChanged event, which will be called whenever the property of an item in the collection is changed.
public class ObservableCollectionEnhanced<T> : ObservableCollection<T> where T : INotifyPropertyChanged
{
public ObservableCollectionEnhanced()
: base()
{ }
public ObservableCollectionEnhanced(IEnumerable<T> collection)
: base(collection)
{
foreach (T item in Items)
item.PropertyChanged += OnItemPropertyChanged;
}
public ObservableCollectionEnhanced(List<T> list)
: base(list)
{
foreach (T item in Items)
item.PropertyChanged += OnItemPropertyChanged;
}
public event System.ComponentModel.PropertyChangedEventHandler ItemPropertyChanged;
public void OnItemPropertyChanged(Object sender, PropertyChangedEventArgs e)
{
if (null != ItemPropertyChanged)
ItemPropertyChanged(sender, e);
}
protected override void InsertItem(int index, T item)
{
base.InsertItem(index, item);
item.PropertyChanged += OnItemPropertyChanged;
}
protected override void RemoveItem(int index)
{
T item = this.Items[index];
item.PropertyChanged -= OnItemPropertyChanged;
base.RemoveItem(index);
}
protected override void SetItem(int index, T item)
{
T oldItem = Items[index];
base.SetItem(index, item);
oldItem.PropertyChanged -= OnItemPropertyChanged;
item.PropertyChanged += OnItemPropertyChanged;
}
}
Configure the handler as follows:
public void OnItemPropertyChanged(Object sender, PropertyChangedEventArgs e)
{
System.Diagnostics.Debug.WriteLine("Update called on {0}", sender);
}
...
collection.ItemPropertyChanged += OnItemPropertyChanged;