So I have this object:
public class SomeObject: INotifyPropertyChanged
{
public decimal AlertLevel {
get {
return alertLevel;
}
set {
if(alertLevel == value) return;
alertLevel = value;
OnPropertyChanged("AlertLevel");
}
private void OnPropertyChanged(string propertyName) {
if(PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
Suppose I am changing this object on a thread that is NOT the GUI thread. How can I have this object raise the PropertyChanged event on the same thread as the GUI when I don't have a reference to any GUI component in this class?
Normally the event subscriber should be responsible for marshalling the calls to the UI thread if necessary.
But if the class in question is UI specific (a.k.a view model), as soon it is created on the UI thread, you can capture the SynchronizationContext and use it for raising the event like this:
public class SomeObject : INotifyPropertyChanged
{
private SynchronizationContext syncContext;
public SomeObject()
{
syncContext = SynchronizationContext.Current;
}
private decimal alertLevel;
public decimal AlertLevel
{
get { return alertLevel; }
set
{
if (alertLevel == value) return;
alertLevel = value;
OnPropertyChanged("AlertLevel");
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(string propertyName)
{
var handler = PropertyChanged;
if (handler != null)
{
if (syncContext != null)
syncContext.Post(_ => handler(this, new PropertyChangedEventArgs(propertyName)), null);
else
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
}
Alternatively you can pass SynchronizationContext via constructor.
Yet another way is to keep the object intact, but data bind to it via intermediate synchronized binding source as described here Update elements in BindingSource via separate task.
for WPF - Add the following references:
PresentationFramework.dll
WindowsBase.dll
In your background thread - wrap the code that needs access to UI into a dispatcher.Invoke()
using System.Windows;
using System.Windows.Threading;
...
//this is needed because Application.Current will be NULL for a WinForms application, since this is a WPF construct so you need this ugly hack
if (System.Windows.Application.Current == null)
new System.Windows.Application();
Application.Current.Dispatcher.BeginInvoke(new Action(() =>
{
//Do Your magic here
}), DispatcherPriority.Render);
for WinForms use
Dispatcher.CurrentDispatcher.Invoke(DispatcherPriority.Render, new Action(() => {
//Do Your magic here
}));
An even better idea, without using any WPF references:
public class GUIThreadDispatcher {
private static volatile GUIThreadDispatcher itsSingleton;
private WeakReference itsDispatcher;
private GUIThreadDispatcher() { }
public static GUIThreadDispatcher Instance
{
get
{
if (itsSingleton == null)
itsSingleton = new GUIThreadDispatcher();
return itsSingleton;
}
}
public void Init(Control ctrl) {
itsDispatcher = new WeakReference(ctrl);
}
public void Invoke(Action method) {
ExecuteAction((Control ctrl) => DoInGuiThread(ctrl, method, forceBeginInvoke: false));
}
public void BeginInvoke(Action method) {
ExecuteAction((Control ctrl) => DoInGuiThread(ctrl, method, forceBeginInvoke: true));
}
private void ExecuteAction(Action<Control> action) {
if (itsDispatcher.IsAlive) {
var ctrl = itsDispatcher.Target as Control;
if (ctrl != null) {
action(ctrl);
}
}
}
public static void DoInGuiThread(Control ctrl, Action action, bool forceBeginInvoke = false) {
if (ctrl.InvokeRequired) {
if (forceBeginInvoke)
ctrl.BeginInvoke(action);
else
ctrl.Invoke(action);
}
else {
action();
}
}
}
}
And initialize like this:
private void MainForm_Load(object sender, EventArgs e) {
//setup the ability to use the GUI Thread when needed via a static reference
GUIThreadDispatcher.Instance.Init(this);
...
}
And use like this:
public class SomeObject: INotifyPropertyChanged
{
public decimal AlertLevel {
get {
return alertLevel;
}
set {
if(alertLevel == value) return;
alertLevel = value;
OnPropertyChanged("AlertLevel");
}
private void OnPropertyChanged(string propertyName) {
GUIThreadDispatcher.Instance.BeginInvoke(() => {
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
});
}}
Found an even better answer without having to use a WeakReference to the form control and NO WPF References based on https://lostechies.com/gabrielschenker/2009/01/23/synchronizing-calls-to-the-ui-in-a-multi-threaded-application/ and Ivan's answer above:
public class GUIThreadDispatcher {
private static volatile GUIThreadDispatcher itsSingleton;
private SynchronizationContext itsSyncContext;
private GUIThreadDispatcher() {}
/// <summary>
/// This needs to be called on the GUI Thread somewhere
/// </summary>
public void Init() {
itsSyncContext = AsyncOperationManager.SynchronizationContext;
}
public static GUIThreadDispatcher Instance
{
get
{
if (itsSingleton == null)
itsSingleton = new GUIThreadDispatcher();
return itsSingleton;
}
}
public void Invoke(Action method) {
itsSyncContext.Send((state) => { method(); }, null);
}
public void BeginInvoke(Action method) {
itsSyncContext.Post((state) => { method(); }, null);
}
}
}
And initialize like this:
private void MainForm_Load(object sender, EventArgs e) {
//setup the ability to use the GUI Thread when needed via a static reference
GUIThreadDispatcher.Instance.Init();
...
}
And use like this:
public class SomeObject: INotifyPropertyChanged
{
public decimal AlertLevel {
get {
return alertLevel;
}
set {
if(alertLevel == value) return;
alertLevel = value;
OnPropertyChanged("AlertLevel");
}
private void OnPropertyChanged(string propertyName) {
GUIThreadDispatcher.Instance.BeginInvoke(() => {
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
});
}}
This turned out to be a clean implementation (relatively). Just had to include a reference to WindowsBase.dll which turns out to be a WPF library so eh..., not extremely pleased with it but it's a solution...:
public class GUIThreadDispatcher {
private static volatile GUIThreadDispatcher itsSingleton;
private Dispatcher itsDispatcher;
private GUIThreadDispatcher() { }
public static GUIThreadDispatcher Instance
{
get
{
if (itsSingleton == null)
itsSingleton = new GUIThreadDispatcher();
return itsSingleton;
}
}
public void Init() {
itsDispatcher = Dispatcher.CurrentDispatcher;
}
public object Invoke(Action method, DispatcherPriority priority = DispatcherPriority.Render, params object[] args) {
return itsDispatcher.Invoke(method, priority, args);
}
public DispatcherOperation BeginInvoke(Action method, DispatcherPriority priority = DispatcherPriority.Render, params object[] args) {
return itsDispatcher.BeginInvoke(method, priority, args);
}
Then initialize it like this:
static class Program {
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main() {
GUIThreadDispatcher.Instance.Init(); //setup the ability to use the GUI Thread when needed via a static reference
Application.Run(new MainForm());
}
}
And then use it like this:
public class SomeObject: INotifyPropertyChanged
{
public decimal AlertLevel {
get {
return alertLevel;
}
set {
if(alertLevel == value) return;
alertLevel = value;
OnPropertyChanged("AlertLevel");
}
private void OnPropertyChanged(string propertyName) {
GUIThreadDispatcher.Instance.BeginInvoke(() => {
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
});
}}
Related
I have .NET Core app with WPF. I am trying to convert it to use DI and MVVM pattern.
I am stuck now. At the application startup I subscribe for some events. Event handler process it and change title of all opened windows.
Something like:
protected void OnSomething(object sender, EventArgs args)
{
App.Current.Dispatcher.BeginInvoke(new Action(() =>
{
foreach (Window window in App.Current.Windows)
{
if (window.Title.EndsWith("something"))
{
window.Title = window.Title.Substring(0, window.Title.Length - "something".Length);
}
}
}));
}
protected void OnSomethingElse(object sender, EventArgs args)
{
App.Current.Dispatcher.BeginInvoke(new Action(() =>
{
foreach (Window window in App.Current.Windows)
{
if (!window.Title.EndsWith("something"))
{
window.Title = window.Title + "something";
}
}
}));
}
I would like to move window title construction into ViewModel of given window. Lets say:
public string WindowTitle => "My title" + (currentState ? "something" : string.Empty);
What is the best approach to update title of all opened windows? I believe that I can not change title in the way I am doing it right now. And I feel current way is not the right/best way.
This is my approach.
Create a resource like this:
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:system="clr-namespace:System;assembly=mscorlib">
[...]
<system:String x:Key="AppTitle">Default App Title</system:String>
[...]
</ResourceDictionary>
And set that resource as your window title:
<Window
[...]
Title="{DynamicResource AppTitle}">
[...]
</Window>
Note: it should be a DynamicResource.
Then create a utility method like this:
public class Utils {
public static void ChangeAppTitle(string title) {
Application.Current.Resources["AppTitle"] = title;
}
}
Now we can access to this method in entire application.
And finally you can use it like this:
public class SampleViewModel {
private void SampleMethod() {
Utils.ChangeAppTitle("New app title");
}
}
For this scenario I would use my self-written EventService. This EventService looks like:
public class EventService<T>
{
private static EventService<T> instance;
private static readonly object instanceLock = new object();
private readonly Dictionary<string, List<Action<T>> > events;
private EventServiceExt()
{
events = new Dictionary<string, List<Action<T>>>();
}
public static EventService<T> Instance
{
get
{
lock (instanceLock)
{
if (instance == null)
{
instance = new EventService<T>();
}
}
return instance;
}
}
public void Subscribe(string eventName, Action<T> action)
{
lock (instanceLock)
{
if (events.ContainsKey(eventName))
events[eventName].Add(action);
else
events.Add(eventName, new List<Action<T>>{ action });
}
}
public void Raise(string eventName, T parameter)
{
lock (instanceLock)
{
if (events != null && (events.ContainsKey(eventName) && events[eventName] != null))
{
var actions = events[eventName];
if (actions != null)
{
foreach (var action in actions.Where(action => action != null))
{
action(parameter);
}
}
}
}
}
/// <summary>
///
/// </summary>
/// <param name="eventName"></param>
/// <param name="action"></param>
public void Unsubscribe(string eventName, Action<T> action)
{
lock (instanceLock)
{
if (events.ContainsKey(eventName) && events[eventName] != null && action != null)
{
List<Action<T>> actions = events[eventName];
actions.Remove(action);
if (actions.Count == 0)
events.Remove(eventName);
}
}
}
}
With this you can subscribe to an event in each window like:
EventService<string>.Instance.Subscribe("windowtitlechanged", OnWindowTitleChanged);
And if you want to change the window-title you just have to raise the event with:
EventService<string>.Instance.Raise("windowtitlechanged", "My new window-title");
I have a custom class inheriting from ObservableCollection and INotifyPropertyChanged (i.e. the custom class also has properties) that serves as a Collection<T> where T also inherits from INotifyPropertyChanged:
public class CustomCollection<T> : ObservableCollection<T>, INotifyPropertyChanged where T: INotifyPropertyChanged {
private string _name;
public string Name {
get {
return _name;
}
set {
if (_name != value) {
_name = value;
NotifyPropertyChanged("Name");
}
}
}
private int _total;
public int Total {
get {
return _total;
}
set {
if (_total != value) {
_total = value;
NotifyPropertyChanged("Total");
}
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(String propertyName) {
PropertyChangedEventHandler handler = PropertyChanged;
if (null != handler) {
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
And T item class:
public class DataItem : INotifyPropertyChanged {
private string _fname;
public string Fname {
get {
return _fname;
}
set {
if (value != _fname) {
_fname = value;
NotifyPropertyChanged("Fname");
}
}
}
private int_value;
public int Value {
get {
return _value;
}
set {
if (value != _value) {
_value = value;
NotifyPropertyChanged("Value");
}
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(String propertyName) {
PropertyChangedEventHandler handler = PropertyChanged;
if (null != handler) {
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
}
And the ViewModel:
public class ViewModel : ViewModelBase {
private readonly IService _dataService;
private bool _isLoading;
public bool IsLoading {
get {
return _isLoading;
}
private set {
_isLoading = value;
RaisePropertyChanged("IsLoading");
}
}
private CustomCollection<DataItem> _items;
public CustomCollection<DataItem> Items
{
get
{
return _items;
}
set
{
_items= value;
RaisePropertyChanged("Items");
}
}
public ViewModel(IService dataService) {
_dataService = dataService;
}
public void Refresh() {
if (!this.IsLoading) {
this.IsLoading = true;
_dataService.RefreshData(
this, (error) => {
if (error != null) {
return;
}
if (!IsInDesignMode)
this.IsLoading = false;
}
);
}
}
public void GetData() {
if (Games == null) {
Games = new CustomCollection<DataItem>();
} else {
Games.Clear();
}
if (!this.IsLoading) {
this.IsLoading = true;
_dataService.GetData(
this, (error) => {
if (error != null) {
return;
}
if (!IsInDesignMode)
this.IsLoading = false;
}
);
}
}
And I have bound the CustomCollection<T> to a control in my View (xaml). Everything works fine initially, upon navigating to the page, the ViewModel calls for a DataService to retrieve the data and populate the CustomCollection<T>. However, when refreshing the data, the View is not updated until all the data has been iterated over and refreshed/updated!
Here is the code for the refresh/updated (keep in mind, I'm retrieving the data via a web service, and for the purposes of testing have just manually updated the Value property in DataItem at each passover of the CustomCollection<T>):
public async RefreshData(ViewModel model, Action<Exception> callback) {
if (model.Items == null) return;
// ... retrieve data from web service here (omitted) ...
foreach (DataItem item in retrievedItems) { // loop for each item in retrieved items
DataItem newItem = new DataItem() { Fname = item.Fname, Value = item.Value };
if (model.Items.contains(newItem)) { // override for .Equals in CustomCollection<T> allows for comparison by just Fname property
model.Items[model.Items.IndexOf(newItem)].Value += 10; // manual update
} else {
model.Items.Add(newItem);
}
System.Threading.Thread.Sleep(1000); // 1 second pause to "see" each item updated sequentially...
}
callback(null);
}
So in summary, how can I make it so updating Value of my DataItem will instantly reflect in the View, given my current setup of CustomCollection<DateItem>? Something to do with async perhaps? I mean, when Sleep(1000) gets called, the UI does not hang, maybe this has something to do with it?
Any ideas on how to fix this? As you might have guessed, this issue is also present when first retrieving the data (but is barely noticeable as data is retrieved/processed during the navigation to the View).
Note: I'm using the MVVMLight Toolkit.
Thanks.
Ok from below which one is best approach or any other better ways ?
Both are working (credits not belongs to me)
public static MTObservableCollection<string> ocEventsCollection = new MTObservableCollection<string>();
public static void AddMsgToEvents(string srMessage)
{
lock (ocEventsCollection)
ocEventsCollection.Insert(0, srMessage);
}
public class MTObservableCollection<T> : ObservableCollection<T>
{
public override event NotifyCollectionChangedEventHandler CollectionChanged;
protected override void OnCollectionChanged(NotifyCollectionChangedEventArgs e)
{
NotifyCollectionChangedEventHandler CollectionChanged = this.CollectionChanged;
if (CollectionChanged != null)
foreach (NotifyCollectionChangedEventHandler nh in CollectionChanged.GetInvocationList())
{
DispatcherObject dispObj = nh.Target as DispatcherObject;
if (dispObj != null)
{
Dispatcher dispatcher = dispObj.Dispatcher;
if (dispatcher != null && !dispatcher.CheckAccess())
{
dispatcher.BeginInvoke(
(Action)(() => nh.Invoke(this,
new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset))),
DispatcherPriority.DataBind);
continue;
}
}
nh.Invoke(this, e);
}
}
}
And the second approach
public static ObservableCollection<string> ocEventsCollection = new ObservableCollection<string>();
public static void AddMsgToEvents(string srMessage)
{
Application.Current.Dispatcher.BeginInvoke(new Action(() =>
{
ocEventsCollection.Insert(0, srMessage);
}));
}
I was wondering if there is an 'easy'/good way to check if a property has changed. Like in the hierarchy below when Child.Name has changed (isDirty) I would like to know.
GrantParent
- Parent
-- Child
In my current situation I need to navigate through the model to see if anything has changed.
ps: I'm using IChangeTracking.
Been thinking about caching a hash of the serialized object. (too slow?)
Or creating changedevent which call's the parent until it reaches the grantparent. (chatty?)
public class Parent: BaseEntity
{
private Child _child;
public Child Child
{
get { return _child; }
set { _child = value; OnPropertyChanged("Child"); }
}
}
public class Child : BaseEntity
{
private int _id;
public int Id {
get { return _id; }
set { _id = value; OnPropertyChanged("Id"); }
}
}
[DataContract]
[Serializable]
public abstract class BaseEntity : INotifyPropertyChanged
{
protected BaseEntity()
{
PropertyChanged += PropertyChangedEventHandler;
}
private void PropertyChangedEventHandler(object sender, PropertyChangedEventArgs e)
{
if (e != null && !String.Equals(e.PropertyName, "IsChanged", StringComparison.Ordinal))
{
this.IsChanged = true;
}
}
protected void OnPropertyChanged<T>(Expression<Func<T>> property)
{
MemberExpression me = property.Body as MemberExpression;
if (me == null || me.Expression != property.Parameters[0]
|| me.Member.MemberType != MemberTypes.Property)
{
throw new InvalidOperationException(
"Now tell me about the property");
}
var handler = PropertyChanged;
if (handler != null) handler(this,
new PropertyChangedEventArgs(me.Member.Name));
}
[Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public bool IsChanged
{
get
{
lock (_notifyingObjectIsChangedSyncRoot)
{
return _notifyingObjectIsChanged;
}
}
protected set
{
lock (_notifyingObjectIsChangedSyncRoot)
{
if (!Boolean.Equals(_notifyingObjectIsChanged, value))
{
_notifyingObjectIsChanged = value;
if (IsDirtyChanged != null)
IsDirtyChanged();
this.OnPropertyChanged("IsChanged");
}
}
}
}
private bool _notifyingObjectIsChanged;
private readonly object _notifyingObjectIsChangedSyncRoot = new Object();
public void AcceptChanges()
{
this.IsChanged = false;
}
}
In the end I used a compare on the XML model from the XML serializer I already used. I did't 'need' instant change detection once a second (or so) would be enough. Now I check the XML model with the one I had since the last save.
You'll need to have each of the properties keep track of it themselves, and either store some information indicating what properties have changed, or possibly firing off an event when an item is changed.
essentially each property will have logic similar to this:
public class MyClass : INotifyPropertyChanged
{
private int _value;
public int Value
{
get
{
return _value;
}
set
{
_value = value;
PropertyChanged(this, new PropertyChangedEventArgs("Value"));
}
}
public event PropertyChangedEventHandler PropertyChanged;
}
This will allow you to add an event handler to the PropertyChanged event so that code will be fired when a property is changed.
I recently worked on a project where we had all of the nodes/leaves implement a node.Modified property and used INotifyPropertyChanged to raise state change of node.Modified. Then all of the parents subscribed to their children's property change and if node.Modified was ever set true, then they'd set their own node.Modified to true.
Like you say, it's a little chatty, but hasn't come close to becoming a performance bottleneck for us since we're not seeing thousands of changes every second and our hierarchy is only 3 levels deep.
Here's a quick sample:
class Node : INotifyPropertyChanged
{
public Node()
{
Children = new List<Node>();
}
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string name)
{
var temp = PropertyChanged;
if (temp != null)
temp(this, new PropertyChangedEventArgs(name));
}
public IList<Node> Children { get; private set; }
public void AddChild(Node node)
{
node.PropertyChanged += ChildPropertyChanged;
Children.Add(node);
}
void ChildPropertyChanged(object sender, PropertyChangedEventArgs args)
{
if (args.PropertyName == "Modified")
Modified |= ((Node)sender).Modified;
}
bool _modified = false;
public bool Modified
{
get { return _modified; }
set
{
if (_modified != value)
{
_modified = value;
OnPropertyChanged("Modified");
}
}
}
EDIT: There is another way using a sort of message bus. It may not be perfect, but it is another approach to the problem, so I will share that as well. I quickly hacked up a trivial Msg bus...
static class Bus<T>
{
public static Dictionary<object, Action<object, T>> Subscriptions = new Dictionary<object, Action<object, T>>();
public static void Raise(object sender, T message)
{
foreach (Action<object, T> action in Subscriptions.Values)
{
action(sender, message);
}
}
public static void Subscribe(object subscriber, Action<object, T> action)
{
Subscriptions[subscriber] = action;
}
public static void Unsubscribe(object subscriber)
{
if (Subscriptions.ContainsKey(subscriber))
Subscriptions.Remove(subscriber);
}
}
public class WasModified { }
And the modified Node
class Node
{
public Node()
{
Children = new List<Node>();
}
public IList<Node> Children { get; private set; }
bool _modified = false;
public bool Modified
{
get { return _modified; }
set
{
if (_modified != value)
{
_modified = value;
if (_modified == true)
Bus<WasModified>.Raise(this, new WasModified());
}
}
}
}
Finally, it's use.
static void Main(string[] args)
{
Node parent = new Node();
Bus<WasModified>.Subscribe(parent, (s,a)=> parent.Modified = true);
Node child = new Node();
Node gchild = new Node();
parent.Children.Add(child);
parent.Children.Add(gchild);
gchild.Modified = true;
Console.WriteLine(parent.Modified);
Console.ReadLine();
}
The message bus doesn't need to bubble up to parent objects and you don't need to recurse into them each time you want to see if Modified was changed, so perhaps it's what you're looking for.
I just recently discovered an INotifyPropertyChange interface. I managed to implement this interface in my clss and everything works fine. However I was wondering if it is possible to intercept this event in code and fire a function
Let's say that I have a function
DoStuff()
and I wan't to fire this function everytime property1, property2 or property3 changes.
Of course I could put this function in set block in my class but this is not a good idea(I think).
If you mean to internal method that'll handle this event you can do it by registering to the event in the class constructor. For example:
public class AnswerViewModel : IAnswerViewModel
{
public event PropertyChangedEventHandler PropertyChanged;
private string content;
public AnswerViewModel()
{
PropertyChanged += (sender, args) => DoStuff();
}
public string Content
{
get { return content; }
set
{
content = value;
PropertyChanged(this, new PropertyChangedEventArgs("Content"));
}
}
public void DoStuff()
{
// this method will be called whenever PropertyChanged event raised
}
}
If the intercepting method belongs to other class:
public class PropertiesInterceptor
{
private readonly AnswerViewModel viewModel;
private readonly List<string> propertiesToIntercept =
new List<string> { "property1", "property2", "property3" };
public PropertiesInterceptor(AnswerViewModel viewModel)
{
this.viewModel = viewModel;
viewModel.PropertyChanged += OnPropertyChanged;
}
private void OnPropertyChanged(object sender, PropertyChangedEventArgs args)
{
if (propertiesToIntercept.Contains(args.PropertyName))
{
DoStuff();
}
}
private void DoStuff()
{
// Do something with viewModel
}
}
Intercept the PropertyChanged Event:
http://msdn.microsoft.com/en-us/library/system.componentmodel.inotifypropertychanged.propertychanged.aspx
You could fire the method from a RaisePropertyChanged() method:
public int Property1
{
get { return this.property1; }
set
{
if (this.property1 != value)
{
this.property1 = value;
RaisePropertyChanged("Property1");
}
}
}
private void RaisePropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
DoStuff(); // Call DoStuff here.
}
Stealing Elisha's answer to answer your question in Merlyn's answer
public class AnswerViewModel : IAnswerViewModel
{
public event PropertyChangedEventHandler PropertyChanged;
private string property1;
private string property2;
private string propertyX;
public AnswerViewModel()
{
PropertyChanged += (sender, args) =>
{
if(args.PropertyName == "Property1" || args.PropertyName == "Property2")
DoStuff();
}
}
public string Property1
{
get { return content; }
set
{
property1 = value;
PropertyChanged(this, new PropertyChangedEventArgs("Property1"));
}
}
public string Property2
{
get { return content; }
set
{
property2 = value;
PropertyChanged(this, new PropertyChangedEventArgs("Property2"));
}
}
public string PropertyX
{
get { return content; }
set
{
propertyX = value;
PropertyChanged(this, new PropertyChangedEventArgs("PropertyX"));
}
}
public void DoStuff()
{
// this method will be called whenever PropertyChanged event raised from Property1 or Property2
}
}
If the class DoStuff is in is a member you can do
private otherClass
public AnswerViewModel()
{
PropertyChanged += (sender, args) =>
{
if(args.PropertyName == "Property1" || args.PropertyName == "Property2")
otherClass.DoStuff();
}
}
Otherwise you can just have otherClass register a event on its own in your main code.
Did you need it to replace the existing NotifyPropertyChanged event handlers, or just get called when NotifyPropertyChanged is called?
If you mean the second, you can simply register an event handler
edit
You can add an event handler that gets called on NotifyPropertyChanged, checks if the property parameter is equal to Property1, Property2, or Property3, and only then forwards it to the actual function you want to call.