Prevent self-callbacks when changing observed collection - c#

Let's suppose I have an observable collection and two clients that want to:
change it,
observe it and react on state change.
Now, if Client1 changes collection state (for example: adds new item), the collection will fire 'CollectionChanged' event. Since both clients are registered for this event notifications, Client1's handling method will be executed.
In order to avoid self-callback on Client1, I unsubscribe from an event, do my action and subscribe again. This is painful - I must remember about suspending Client1's subscription every time Client1 touches the collection and it just seems like a bad smell. Is there a better way (design pattern, external library) that would help me in callbacks management?
Although in my example I mentioned ObservableCollection and CollectionChanged event, I believe my question is more generic and comes down to: "how to exclude an entity that caused event trigger from event callback".
Thanks in advance!
Problem keeps reoccuring in my solution, bumping the question in a hope someone might help out.

I ran into your problem some times ago I didn't find a proper solution except for this one.
The idea is that when you change the collection you also pass an instance of the object changing it.
Then when the Collection fires the event, it also passes the reference.
So all observers may know which instance did the change, and check for equality.
Here is a basic example of this implementation:
class Program
{
private static MyCollection Collection;
private static MyCollectionModifier Modif1;
private static MyCollectionModifier Modif2;
static void Main(string[] args)
{
Collection = new MyCollection();
Modif1 = new MyCollectionModifier("Modifier 1", Collection);
Modif2 = new MyCollectionModifier("Modifier 2", Collection);
Modif1.AddItem("Test1");
Modif2.AddItem("Test2");
Console.ReadKey();
}
}
public class MyCollectionItemAddedEventArgs:EventArgs
{
public Object ChangeSource { get; set;}
public int newIndex {get;set;}
}
public delegate void MyCollectionItemAddedEventHandler(object sender, MyCollectionItemAddedEventArgs e);
public class MyCollection
{
private List<String> _myList;
public String this[int Index]
{
get { return _myList[Index]; }
}
public event MyCollectionItemAddedEventHandler ItemAdded;
public MyCollection()
{
_myList = new List<string>();
}
protected virtual void OnMyCollectionItemAdded(MyCollectionItemAddedEventArgs e)
{
if (ItemAdded != null)
ItemAdded(this, e);
}
public void AddItem(String Item, object ChangeSource = null)
{
_myList.Add(Item);
var e = new MyCollectionItemAddedEventArgs();
e.ChangeSource = ChangeSource;
e.newIndex = _myList.Count;
OnMyCollectionItemAdded(e);
}
}
public class MyCollectionModifier
{
private MyCollection _collection;
public string Name { get; set; }
public MyCollectionModifier(string Name, MyCollection Collection)
{
this.Name = Name;
_collection = Collection;
_collection.ItemAdded += Collection_ItemAdded;
}
public void AddItem(string Item)
{
_collection.AddItem(Item, this);
}
void Collection_ItemAdded(object sender, MyCollectionItemAddedEventArgs e)
{
if (e != null)
{
if (this.Equals(e.ChangeSource))
{
Console.WriteLine("{0} : I changed the collection", Name);
}
else
{
Console.WriteLine("{0} : Somebody else changed the collection", Name);
}
}
}
}

I've encountered this problem before as well.
Best solution I could come up with is to create extension methods that take the handler of the caller and then automate the unsubscribe/subscribe around the called method, that way you don't have to remember to do it each time and it does not end up cluttering your code either
public static void Add<T>(this ObservableCollection<T> self, T itemToAdd, NotifyCollectionChangedEventHandler handler)
{
self.CollectionChanged -= handler;
self.Add(itemToAdd);
self.CollectionChanged += handler;
}
It does take some effort to create the extensions initially but at least you won't forget to resubscribe. Only real extra code is then around invoking the method
public class ObserverClass
{
public ObserverClass()
{
ObservableIntegers.CollectionChanged += ObservableIntegersOnCollectionChanged;
//Add item to collection while preventing self-handling the callback
ObservableIntegers.Add(1, ObservableIntegersOnCollectionChanged);
}
private void ObservableIntegersOnCollectionChanged(object sender, NotifyCollectionChangedEventArgs notifyCollectionChangedEventArgs)
{
// Handle collection change
}
public ObservableCollection<int> ObservableIntegers { get; set; }
}

Related

How do I add and subtract event handlers inside a derived abstract class?

Short version
In my abstract class MyCbo_Abstract (derived from ComboBox class), I want to create a custom property that when set will subtract all the control's event handlers, set the base property value, then re-add all the control's event handlers.
What I have so far
I have a concrete ComboBox class derived from an abstract ComboBox class derived from Microsoft's ComboBox class.
public abstract class MyCbo_Abstract : ComboBox
{
public MyCbo_Abstract() : base()
{
}
}
public partial class MyCboFooList : MyCbo_Abstract
{
public MyCboFooList() : base()
{
}
}
My main Form class subscribes to certain base ComboBox events.
Note: The designer has: this.myCboFooList = new MyCboFooList();
public partial class FormMain : Form
{
public FormMain()
{
myCboFooList.SelectedIndexChanged += myCboFooList_SelectedIndexChanged;
}
private void myCboFooList_SelectedIndexChanged(object sender, EventArgs e)
{
// do stuff
}
}
There are times when I want to suppress the invocation of defined event handlers, e.g., when I programmatically set a ComboBox object's SelectedIndex property.
Instead of having to remember to write the code to subtract and re-add event handlers each time I want to modify the SelectedIndex property and suppress its events, I want to create a custom property SelectedIndex_NoEvents that when set will subtract all the control's event handlers, set the base property value SelectedIndex, then re-add all the control's event handlers.
The problem
My problem is that I don't know how to iterate over a EventHandlerList because it has no GetEnumerator. And, in looking at the list in the debugger, saveEventHandlerList is a weird chained thing that I can't figure out how to otherwise traverse.
public abstract class MyCbo_Abstract : ComboBox
{
int selectedIndex_NoEvents;
public int SelectedIndex_NoEvents
{
get
{
return base.SelectedIndex;
}
set
{
EventHandlerList saveEventHandlerList = new EventHandlerList();
saveEventHandlerList = Events;
//foreach won't work - no GetEnumerator available. Can't use for loop - no Count poprerty
foreach (EventHandler eventHandler in saveEventHandlerList)
{
SelectedIndexChanged -= eventHandler;
}
base.SelectedIndex = value;
//foreach won't work - no GetEnumerator available. Can't use for loop - no Count poprerty
foreach (EventHandler eventHandler in saveEventHandlerList)
{
SelectedIndexChanged += eventHandler;
}
saveEventHandlerList = null;
}
}
//Probably don't need this
public override int SelectedIndex
{
get
{
return base.SelectedIndex;
}
set
{
base.SelectedIndex = value;
}
}
public DRT_ComboBox_Abstract() : base()
{
}
}
Before giving you the solution that I created, let me say that this feels extremely hacky. I urge you to seriously think about another solution. There may be all kinds of crazy edge cases where this code breaks down, I haven't thoroughly tested it beyond the example code shown below.
Add the following utility class:
public class SuspendedEvents
{
private Dictionary<FieldInfo, Delegate> handlers = new Dictionary<System.Reflection.FieldInfo, System.Delegate>();
private object source;
public SuspendedEvents(object obj)
{
source = obj;
var fields = obj.GetType().GetFields(BindingFlags.Static | BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public);
foreach (var fieldInfo in fields.Where(fi => fi.FieldType.IsSubclassOf(typeof(Delegate))))
{
var d = (Delegate)fieldInfo.GetValue(obj);
handlers.Add(fieldInfo, (Delegate)d.Clone());
fieldInfo.SetValue(obj, null);
}
}
public void Restore()
{
foreach (var storedHandler in handlers)
{
storedHandler.Key.SetValue(source, storedHandler.Value);
}
}
}
You can use it like this:
var events = new SuspendedEvents(obj); //all event handlers on obj are now detached
events.Restore(); // event handlers on obj are now restored.
I used the following test setup:
void Main()
{
var obj = new TestObject();
obj.Event1 += (sender, e) => Handler("Event 1");
obj.Event1 += (sender, e) => Handler("Event 1");
obj.Event2 += (sender, e) => Handler("Event 2");
obj.Event2 += (sender, e) => Handler("Event 2");
obj.Event3 += (sender, e) => Handler("Event 3");
obj.Event3 += (sender, e) => Handler("Event 3");
Debug.WriteLine("Prove events are attached");
obj.RaiseEvents();
var events = new SuspendedEvents(obj);
Debug.WriteLine("Prove events are detached");
obj.RaiseEvents();
events.Restore();
Debug.WriteLine("Prove events are reattached");
obj.RaiseEvents();
}
public void Handler(string message)
{
Debug.WriteLine(message);
}
public class TestObject
{
public event EventHandler<EventArgs> Event1;
public event EventHandler<EventArgs> Event2;
public event EventHandler<EventArgs> Event3;
public void RaiseEvents()
{
Event1?.Invoke(this, EventArgs.Empty);
Event2?.Invoke(this, EventArgs.Empty);
Event3?.Invoke(this, EventArgs.Empty);
}
}
It produces the following output:
Prove events are attached
Event 1
Event 1
Event 2
Event 2
Event 3
Event 3
Prove events are detached
Prove events are reattached
Event 1
Event 1
Event 2
Event 2
Event 3
Event 3
There is no way to easily disable event firing of WinForm controls exposed in the .Net framework. However, the Winform controls follow a standard design pattern for events in that all event signatures are based on the EventHandler Delegate and the registered event handlers are stored in an EventHandlerList that is defined in the Control Class. This list is stored in a field (variable) named "events" and is only publicly exposed via the read-only property Events.
The class presented below uses reflection to temporarily assign null to the events field effectively removing all event handlers registered for the Control.
While it may be an abuse of the pattern, the class implements the IDisposable Interface to restore the events field on disposal of the class instance. The reason for this is to facilitate the use of the using block to wrap the class usage.
public class ControlEventSuspender : IDisposable
{
private const string eventsFieldName = "events";
private const string headFieldName = "head";
private static System.Reflection.FieldInfo eventsFieldInfo;
private static System.Reflection.FieldInfo headFieldInfo;
private System.Windows.Forms.Control target;
private object eventHandlerList;
private bool disposedValue;
static ControlEventSuspender()
{
Type compType = typeof(System.ComponentModel.Component);
eventsFieldInfo = compType.GetField(eventsFieldName, System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic);
headFieldInfo = typeof(System.ComponentModel.EventHandlerList).GetField(headFieldName, System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic);
}
private static bool FieldInfosAquired()
{
if (eventsFieldInfo == null)
{
throw new Exception($"{typeof(ControlEventSuspender).Name} could not find the field '{ControlEventSuspender.eventsFieldName}' on type Component.");
}
if (headFieldInfo == null)
{
throw new Exception($"{typeof(ControlEventSuspender).Name} could not find the field '{ControlEventSuspender.headFieldName}' on type System.ComponentModel.EventHandlerList.");
}
return true;
}
private ControlEventSuspender(System.Windows.Forms.Control target) // Force using the the Suspend method to create an instance
{
this.target = target;
this.eventHandlerList = eventsFieldInfo.GetValue(target); // backup event hander list
eventsFieldInfo.SetValue(target, null); // clear event handler list
}
public static ControlEventSuspender Suspend(System.Windows.Forms.Control target)
{
ControlEventSuspender ret = null;
if (FieldInfosAquired() && target != null)
{
ret = new ControlEventSuspender(target);
}
return ret;
}
protected virtual void Dispose(bool disposing)
{
if (!this.disposedValue)
{
if (disposing)
{
if (this.target != null)
{
RestoreEventList();
}
}
}
this.disposedValue = true;
}
public void Dispose()
{
Dispose(true);
}
private void RestoreEventList()
{
object o = eventsFieldInfo.GetValue(target);
if (o != null && headFieldInfo.GetValue(o) != null)
{
throw new Exception($"Events on {target.GetType().Name} (local name: {target.Name}) added while event handling suspended.");
}
else
{
eventsFieldInfo.SetValue(target, eventHandlerList);
eventHandlerList = null;
target = null;
}
}
}
Example usage in the button1_Click method:
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
using (ControlEventSuspender.Suspend(comboBox1))
{
comboBox1.SelectedIndex = 3; // SelectedIndexChanged does not fire
}
}
private void button2_Click(object sender, EventArgs e)
{
comboBox1.SelectedIndex = -1; // clear selection, SelectedIndexChanged fires
}
private void button3_Click(object sender, EventArgs e)
{
comboBox1.SelectedIndex = 3; // SelectedIndexChanged fires
}
private void comboBox1_SelectedIndexChanged(object sender, EventArgs e)
{
Console.WriteLine("index changed fired");
System.Media.SystemSounds.Beep.Play();
}
}
SoapBox Diatribe
Many will say that the use of Reflection to access non-public class members is dirty or some other derogatory term and that it introduces a brittleness to the code as someone may change the underlying code definition such that the code that relies on member names (magic strings) is no longer valid. This is a valid concern, but I view it as no different than code that accesses external databases.
Reflection can be thought of a query of a type (datatable) from an assembly (database) for specific fields (members: fields, properties, events). It is no more brittle than a SQL statement such as Select SomeField From SomeTable Where AnotherField=5. This type of SQL code is prevent in the world and no one thinks twice about writing it, but some external force could easily redefine the database you code relies on an render all the magic string SQL statements invalid as well.
Use of hard coded names is always at risk of being made invalid by change. You have to weigh the risks of moving forward versus the option of being frozen in fear of proceeding because someone wants to sound authoritative (typically a parroting of other such individuals) and criticize you for implementing a solution that solves the current problem.
I was hoping to write code that would programatically locate all event handler method names created using controlObject.Event += EventHandlerMethodName, but as you see in the other answers, code to do this is complicated, limited, and perhaps not able to work in all cases
This is what I came up with. It satisfies my desire to consolidate the code that subtracts and re-adds event handler method names into my abstract class, but at the expense of having to write code to store and manage event handler method names and having to write code for each control property where I want to suppress the event handler, modify the property value, and finally re-add the event handler.
public abstract class MyCbo_Abstract : ComboBox
{
// create an event handler property for each event the app has custom code for
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
private EventHandler evSelectedValueChanged;
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public EventHandler EvSelectedValueChanged { get => evSelectedValueChanged; set => evSelectedValueChanged = value; }
public MyCbo_Abstract() : base()
{
}
// Create a property that parallels the one that would normally be set in the main body of the program
public object _DataSource_NoEvents
{
get
{
return base.DataSource;
}
set
{
SelectedValueChanged -= EvSelectedValueChanged;
if (value == null)
{
base.DataSource = null;
SelectedValueChanged += EvSelectedValueChanged;
return;
}
string valueTypeName = value.GetType().Name;
if (valueTypeName == "Int32")
{
base.DataSource = null;
SelectedValueChanged += EvSelectedValueChanged;
return;
}
//assume StringCollection
base.DataSource = value;
SelectedValueChanged += EvSelectedValueChanged;
return;
}
}
}
public partial class MyCboFooList : MyCbo_Abstract
{
public MyCboFooList() : base()
{
}
}
Designer has
this.myCboFooList = new MyCboFooList();
Main form code
public partial class FormMain : Form
{
public FormMain()
{
myCboFooList.SelectedValueChanged += OnMyCboFooList_SelectedValueChanged;
myCboFooList.EvSelectedValueChanged = OnMyCboFooList_SelectedValueChanged;
}
private void OnMyCboFooList_SelectedValueChanged(object sender, EventArgs e)
{
// do stuff
}
}
And now, if I want to set a property and suppress event(s), I can write something like the following and not have to remember to re-add the event handler method name
myCboFooList._DataSource_NoEvents = null;

A design pattern to disable event handling

To simply illustrate my dilemma, let say that I have the following code:
class A
{
// May be set by a code or by an user.
public string Property
{
set { PropertyChanged(this, EventArgs.Empty); }
}
public EventHandler PropertyChanged;
}
class B
{
private A _a;
public B(A a)
{
_a = a;
_a.PropertyChanged += Handler;
}
void Handler(object s, EventArgs e)
{
// Who changed the Property?
}
public void MakeProblem()
{
_a.Property = "make a problem";
}
}
In order to perform its duty, class B have to react on A's PropertyChanged event but also is capable of alternating that property by itself in certain circumstances. Unfortunately, also other objects can interact with the Property.
I need a solution for a sequential flow. Maybe I could just use a variable in order to disable an action:
bool _dontDoThis;
void Handler(object s, EventArgs e)
{
if (_dontDoThis)
return;
// Do this!
}
public void MakeProblem()
{
_dontDoThis = true;
_a.Property = "make a problem";
_dontDoThis = false;
}
Are there a better approaches?
Additional considerations
We are unable to change A.
A is sealed.
There are also other parties connected to the PropertyChanged event and I don't know who their are. But when I update the Property from B, they shouldn't be also notified. But I'm unable to disconnect them from the event because I don't know them.
What if also more threads can interact with the Property in the mean time?
The more bullets solved, the better.
Original problem
My original problem is a TextBox (WPF) that I want to complement depending on its content and focus. So I need to react on TextChanged event and I also need to omit that event if its origin is derived from my complements. In some cases, other listeners of a TextChanged event shouldn't be notified. Some strings in certain state and style are invisible to others.
If it is so important not to handle events you initiated, maybe you should change the way you set Property to include the initiator of the change?
public class MyEventArgs : EventArgs
{
public object Changer;
}
public void SetProperty(string p_newValue, object p_changer)
{
MyEventArgs eventArgs = new MyEventArgs { Changer = p_changer };
PropertyChanged(this, eventArgs);
}
And then in your handler - simply check your are not the initiator.
I find all these changes in registration and members very problematic in terms on multi threading and extensibility.
Well essentially you are trying to break the event delegation mechanism and any "solution" to that is going to be brittle since updates to the BCL might break your code. You could set the backing field using reflection. This of course would require that you do have permissions to do this and seeing the generic framing of the question it might not always be that you have the needed permissions
public void MakeProblem()
{
if (_backingField == null) {
_backingField = = _a.GetType().GetField(fieldname)
}
_backingField.SetValue(_a,"make a problem");
}
but as I started out, you are trying to break the event delegation mechanism. The idea is that the receivers of the event are independent. Disabling might lead to so very hard to find bugs because looking at any given piece of code it looks correct but only when you realize that some devious developer has hack the delegation mechanism do you realize why the information that is shown on screen seems to be a cached version of the actual value. The debugger shows the expected value of the property but because the event was hidden the handler responsible for updating the display was never fired and hence an old version is displayed (or the log shows incorrect information so when you are trying to recreate a problem a user has reported based on the content of the log you will not be able to because the information in the log is incorrect because it was based on no one hacking the event delegation mechanism
To my opinion your solution is possible, though I would have created a nested IDisposable class inside B that does the same thing with 'using', or put the '_dontDoThis = false' inside a 'finally' clause.
class A
{
// May be set by a code or by an user.
public string Property
{
set { if (!_dontDoThis) PropertyChanged(this, EventArgs.Empty); }
}
public EventHandler PropertyChanged;
bool _dontDoThis;
}
class B
{
private class ACallWrapper : IDisposable
{
private B _parent;
public ACallWrapper(B parent)
{
_parent = parent;
_parent._a._dontDoThis = true;
}
public void Dispose()
{
_parent._a._dontDoThis = false;
}
}
private A _a;
public B(A a)
{
_a = a;
_a.PropertyChanged += Handler;
}
void Handler(object s, EventArgs e)
{
// Who changed the Property?
}
public void MakeProblem()
{
using (new ACallWrapper(this))
_a.Property = "make a problem";
}
}
On the other hand, I would've used the 'internal' modifier for these things if those two classes are inside the same assembly.
internal bool _dontDoThis;
That way, you keep a better OOP design.
Moreover, if both classes are on the same assembly, I would've written the following code inside A:
// May be set by a code or by an user.
public string Property
{
set
{
internalSetProperty(value);
PropertyChanged(this, EventArgs.Empty);
}
}
internal internalSetProperty(string value)
{
// Code of set.
}
In this case, B could access internalSetProperty without triggering to PropertyChanged event.
Thread Sync:
NOTE: The next section applies to WinForms - I don't know if it applies to WPF as well.
For thread synchronizations, because we're referring to a control. you could use the GUI thread mechanism for synchronization:
class A : Control
{
public string Property
{
set
{
if (this.InvokeRequired)
{
this.Invoke((Action<string>)setProperty, value);
reutrn;
}
setProperty(value);
}
}
private void setProperty string()
{
PropertyChanged(this, EventArgs.Empty);
}
}
Great question.
As a general case, you can not mess around with event handlers of sealed classes. Normally you could override A's hypothetical OnPropertyChanged and based on some flag either raise the event or not. Alternatively you could provide a setter method that does not raise event, as suggested by #Vadim. However, if A is sealed your best option is to add flag to a lister, just as you did. That will enable you to recognize PropertyChanged event raised by B, but you won't be able to suppress the event for other listeners.
Now, since you provided context... There is a way of doing exactly this in WPF. All that needs to be done is B's handler for TextBox.TextChanged needs to set e.Handled = _dontDoThis. That will supress notifications for all other listeners, provided B's one was added as the first one. How to make sure this happens? Reflection!
UIElement exposes only AddHandler and RemoveHandler methods, there is no InsertHandler that would allow to manually specifiy the priority for the handler. However, a quick peek into .NET source code (either download the whole thing or query what you need) reveals that AddHandler forwards arguments to an interal method EventHandlersStore.AddRoutedEventHandler, which does this:
// Create a new RoutedEventHandler
RoutedEventHandlerInfo routedEventHandlerInfo =
new RoutedEventHandlerInfo(handler, handledEventsToo);
// Get the entry corresponding to the given RoutedEvent
FrugalObjectList<RoutedEventHandlerInfo> handlers = (FrugalObjectList<RoutedEventHandlerInfo>)this[routedEvent];
if (handlers == null)
{
_entries[routedEvent.GlobalIndex] = handlers = new FrugalObjectList<RoutedEventHandlerInfo>(1);
}
// Add the RoutedEventHandlerInfo to the list
handlers.Add(routedEventHandlerInfo);
All this stuff is internal, but can be recreated using reflection:
public static class UIElementExtensions
{
public static void InsertEventHandler(this UIElement element, int index, RoutedEvent routedEvent, Delegate handler)
{
// get EventHandlerStore
var prop = typeof(UIElement).GetProperty("EventHandlersStore", BindingFlags.NonPublic | BindingFlags.Instance);
var eventHandlerStoreType = prop.PropertyType;
var eventHandlerStore = prop.GetValue(element, new object[0]);
// get indexing operator
PropertyInfo indexingProperty = eventHandlerStoreType.GetProperties(BindingFlags.NonPublic | BindingFlags.Instance)
.Single(x => x.Name == "Item" && x.GetIndexParameters().Length == 1 && x.GetIndexParameters()[0].ParameterType == typeof(RoutedEvent));
object handlers = indexingProperty.GetValue(eventHandlerStore, new object[] { routedEvent });
if (handlers == null)
{
// just add the handler as there are none at the moment so it is going to be the first one
if (index != 0)
{
throw new ArgumentOutOfRangeException("index");
}
element.AddHandler(routedEvent, handler);
}
else
{
// create routed event handler info
var constructor = typeof(RoutedEventHandlerInfo).GetConstructors(BindingFlags.NonPublic | BindingFlags.Instance).Single();
var handlerInfo = constructor.Invoke(new object[] { handler, false });
var insertMethod = handlers.GetType().GetMethod("Insert");
insertMethod.Invoke(handlers, new object[] { index, handlerInfo });
}
}
}
Now calling InsertEventHandler(0, textBox, TextBox.TextChangedEvent, new TextChangedEventHandler(textBox_TextChanged)) will make sure your handler will be the first one on the list, enabling you to suppress notifications for other listeners!
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
var textBox = new TextBox();
textBox.TextChanged += (o, e) => Console.WriteLine("External handler");
var b = new B(textBox);
textBox.Text = "foo";
b.MakeProblem();
}
}
class B
{
private TextBox _a;
bool _dontDoThis;
public B(TextBox a)
{
_a = a;
a.InsertEventHandler(0, TextBox.TextChangedEvent, new TextChangedEventHandler(Handler));
}
void Handler(object sender, TextChangedEventArgs e)
{
Console.WriteLine("B.Handler");
e.Handled = _dontDoThis;
if (_dontDoThis)
{
e.Handled = true;
return;
}
// do this!
}
public void MakeProblem()
{
try
{
_dontDoThis = true;
_a.Text = "make a problem";
}
finally
{
_dontDoThis = false;
}
}
}
Output:
B.Handler
External handler
B.Handler
I found one solution with regard to third parties, that are connected to the property and we don't want to nofify them when that property changed.
There are though the requirements:
We are capable of override the A.
The A has a virtual method that is invoked when property changed and allows to suspend the event to be raised.
The event is raised immediately when property is being changed.
The solution is to replace the A by MyA, as follows:
class A
{
// May be set by a code or by an user.
public string Property
{
set { OnPropertyChanged(EventArgs.Empty); }
}
// This is required
protected virtual void OnPropertyChanged(EventArgs e)
{
PropertyChanged(this, e);
}
public EventHandler PropertyChanged;
}
// Inject MyA instead of A
class MyA : A
{
private bool _dontDoThis;
public string MyProperty
{
set
{
try
{
_dontDoThis = true;
Property = value;
}
finally
{
_dontDoThis = false;
}
}
}
protected override void OnPropertyChanged(EventArgs e)
{
// Also third parties will be not notified
if (_dontDoThis)
return;
base.OnPropertyChanged(e);
}
}
class B
{
private MyA _a;
public B(MyA a)
{
_a = a;
_a.PropertyChanged += Handler;
}
void Handler(object s, EventArgs e)
{
// Now we know, that the event is not raised by us.
}
public void MakeProblem()
{
_a.MyProperty = "no problem";
}
}
Unfortunately we still use back bool field and we assume a single thread. To rid of the first, we could use a refactored solution suggest by EZSlaver (here). First, create a disposable wrapper:
class Scope
{
public bool IsLocked { get; set; }
public static implicit operator bool(Scope scope)
{
return scope.IsLocked;
}
}
class ScopeGuard : IDisposable
{
private Scope _scope;
public ScopeGuard(Scope scope)
{
_scope = scope;
_scope.IsLocked = true;
}
public void Dispose()
{
_scope.IsLocked = false;
}
}
Then the MyProperty might be refactored to:
private readonly Scope _dontDoThisScope = new Scope();
public string MyProperty
{
set
{
using (new ScopeGuard (_dontDoThisScope))
Property = value;
}
}

I can't register event CollectionChanged of ObservableCollection

I try to run some code when collection is changed. I keep collection as property in Data class:
public static ObservableCollection<OfferedConfiguration> DeviceAdjustedConfigurations
{
get { return deviceAdjustedConfigurations; }
set { deviceAdjustedConfigurations = value; }
}
and register it in code like that:
Data.DeviceAdjustedConfigurations.CollectionChanged += new NotifyCollectionChangedEventHandler(DeviceAdjustedConfigurationsCollectionChanged);
But after registration CollectionChanged is null and the appropriate code in delegated method is not run. In this place DeviceAdjustedConiguration already contains some data. What am I doing wrong?
You should avoid having a set property accessor for collection types, one reason being the one you experienced here with events. Another problem is if someone caches the collection and adds items to it later.
var old = obj.DeviceAdjustedConfigurations;
obj.DeviceAdjustedConfigurations = new ObservableCollection<OfferedConfiguration>();
old.Add(new OfferedConfiguration()); // what should happen here?
instead, remove the set-accessor and use the existing collection directly.
obj.DeviceAdjustedConfigurations.Add(new OfferedConfiguration());
If you really need to set the collection, you need to handle this with for instance a property change event from the class that owns the DeviceAdjustedConfigurations.
public class Item
{
public static ObservableCollection<OfferedConfiguration> DeviceAdjustedConfigurations
{
get { return deviceAdjustedConfigurations; }
set
{
if (deviceAdjustedConfigurations != value)
{
onDeviceConfigurationsChanging(deviceAdjustedConfigurations, value);
deviceAdjustedConfigurations = value;
}
}
}
public static event EventHandler<ConfigurationChangedEventArgs> DeviceConfigurationsChanging;
private static void onDeviceConfigurationsChanging(
ObservableCollection<OfferedConfiguration> oldList,
ObservableCollection<OfferedConfiguration> newList)
{
var handler = DeviceConfigurationsChanging;
if (handler != null)
{
handler(null, new ConfigurationChangedEventArgs(oldList, newList));
}
}
}
public class ConfigurationChangedEventArgs : EventArgs
{
public ConfigurationChangedEventArgs(
ObservableCollection<OfferedConfiguration> oldList,
ObservableCollection<OfferedConfiguration> newList)
{
OldList = oldList;
NewList = newList;
}
public ObservableCollection<OfferedConfiguration> OldList { get; private set; }
public ObservableCollection<OfferedConfiguration> NewList { get; private set; }
}
public class Consumer
{
public void foo()
{
Item.DeviceConfigurationsChanging += updateEvents;
}
private void updateEvents(object sender, ConfigurationChangedEventArgs args)
{
args.OldList.CollectionChanged -= onCollectionChanged;
args.NewList.CollectionChanged += onCollectionChanged;
}
private void onCollectionChanged(object sender, NotifyCollectionChangedEventArgs args) { }
}

Return value of a Property from child ViewModel to parent ViewModel

In my WPF MVVM app, using Caliburn.Micro, I have a ViewModel, CreateServiceViewModel that, on a button click, opens a GridView in a seperate window for the User to chose a Row from.
I created another ViewModel for this, MemberSearchViewModel which has two properties:
private Member selectedMember;
public Member SelectedMember
{
get { return selectedMember; }
set { selectedMember = value; }
}
private IList<Member> members;
public IList<Member> Members
{
get { return members; }
set { members = value; }
}
How do I get that SelectedMember value back to the calling ViewModel? That ViewModel has a property of Service.SelectedMember.
EventAggregator is what you could use... One of many solutions I am sure.
public class MessageNotifier{
public object Content{get;set;}
public string Message {get;set;}
}
//MEF bits here
public class HelloWorldViewModel: Screen, IHandle<MessageNotifier>{
private readonly IEventAggregator _eventAggregator
//MEF constructor bits
public YourViewModel(IEventAggregator eventAggregator){
_eventAggregator = eventAggregator;
}
public override OnActivate(){
_eventAggregator.Subscribe(this);
}
public override OnDeactivate(){
_eventAggregator.UnSubscribe(this);
}
//I Handle all messages with this signature and if the message applies to me do something
//
public void Handle(MesssageNotifier _notifier){
if(_notifier.Message == "NewSelectedItem"){
//do something with the content of the selectedItem
var x = _notifier.Content
}
}
}
//MEF attrs
public class HelloWorld2ViewModel: Screen{
private readonly IEventAggregator _eventAggregator
//MEF attrs
public HelloWorld2ViewModel(IEventAggregator eventAggregator){
_eventAggregator = eventAggregator;
}
public someobject SelectedItem{
get{ return _someobject ;}
set{ _someobject = value;
NotifyOfPropertyChange(()=>SelectedItem);
_eventAggregator.Publish(new MessageNotifier(){ Content = SelectedItem, Message="NewSelectedItem"});
}
}
One option is to utilize NotifyPropertyChanged. Since you are working with ViewModels, they most likely implement INotifyPropertyChanged, which you can make use of just as the framework does.
When your CreateServiceViewModel creates the MemberSearchViewModel, it would just subscribe to the PropertyChanged event:
//This goes wherever you create your child view model
var memberSearchViewModel = new MemberSearchViewModel(); //Or using a service locator, if applicable
memberSearchViewModel.PropertyChanged += OnMemberSearchPropertyChanged;
private void OnMemberSearchPropertyChanged(object sender, PropertyChangedEventArgs e)
{
if(e.PropertyName == "SelectedMember")
{
//Code to respond to a change in the Member
}
}
And then in your MemberSearchViewModel, you simply raise the NotifyPropertyChanged event when the user has selected a member from the grid.
EDIT:
As #DNH correctly notes in the comments, using event handlers like this can lead to memory leaks if not properly cleaned up. So when you are finished with the MemberSearchViewModel, make sure to unsubscribe to the PropertyChanged event. So for example, if you only need it until the user selects a member, you could put it inside the Property Changed Handler itself (I've switched it to use a class-level variable to hold the ViewModel):
private void OnMemberSearchPropertyChanged(object sender, PropertyChangedEventArgs e)
{
if(e.PropertyName == "SelectedMember")
{
//Code to respond to a change in the Member
//Unsubscribe so the view model can be garbage collected
_memberSearchViewModel.PropertyChanged -= OnMemberSearchPropertyChanged;
_memberSearchViewModel = null;
}
}
One option would be to store MemberSearchViewModel as a field of CreateServiceViewModel and define CreateServiceViewModel.SelectedMember property as follows:
public Member SelectedMember
{
get
{
return _memberSearchViewModel.SelectedMember;
}
set
{
_memberSearchViewModel.SelectedMember = value;
}
}
How about?
public interface INotifyMe<T>
{
T ResultToNotify { get; set; }
}
public class CreateServiceViewModel : ViewModelBase, INotifyMe<Member>
{
// implement the interface as you like...
}
public class MemberSearchViewModel : ViewModelBase
{
public MemberSearchViewModel(INotifyMe<Member> toBeNotified)
{
// initialize field and so on...
}
}
Now you could let listen CreateServiceViewModel to its own property and you won't have to think about the removal of the event listener.
Well of course to do the more classical way you could alternatively use an interface like this.
public interface INotifyMe<T>
{
void Notify(T result);
}
As a follow-up to my comment, here's an example using Prism - I've never used Caliburn.
Create an event - the event's payload will be your SelectedMember:
public class YourEvent:CompositePresentationEvent<YourEventPayload>{}
Publish the event:
EventAggregator.GetEvent<YourEvent>().Publish(YourEventPayload);
Subscribe to the event:
EventAggregator.GetEvent<YourEvent>().Subscribe((i) => ...);

How to make only farthest child receive shared event?

I have run into a bit of a design issue with my code.
I have a object that creates a child object (the child could then create another child, etc), and both objects subscribe to the same event.
But, I only want the most child object to receive the event.
Overview of what my project is:
I am creating a IVR system. When a user calls into the system, the user will have X menu choices. Based on what the user chooses they will have a sub menu of choices, and so on and so on. I am using State Machines for this. Every State Machine needs to "listen" for when the user presses a number on their phone. But only the current State Machine needs to process the entered number. Each State Machine can create a new State Machine to represent the sub menu.
Here is some sample code:
Base class:
public delegate void DoSomething(object sender, EventArgs data);
public class Base
{
public event DoSomething myEvent;
private IObject foo;
public Base ()
{
foo = new myObjectA(this);
}
public void SomeAction()
{
((myObjectA)foo).CreateChild();
}
public void EventFired()
{
if (myEvent != null)
{
myEvent(this, new EventArgs());
}
}
}
ObjectA:
class myObjectA : IObject
{
private Base theCallingObject;
private IObject child;
public myObjectA (Base _base)
{
theCallingObject = _base;
theCallingObject.myEvent += new DoSomething(theCallingObject_myEvent);
}
public void CreateChild()
{
child = new myObjectB(theCallingObject);
}
void theCallingObject_myEvent(object sender, EventArgs data)
{
// Handle event
MessageBox.Show("myObjectA");
}
}
ObjectB:
class myObjectB : IObject
{
private Base theCallingObject;
public myObjectB (Base _base)
{
theCallingObject = _base;
theCallingObject.myEvent += new DoSomething(theCallingObject_myEvent);
}
void theCallingObject_myEvent(object sender, EventArgs data)
{
// Handle event
MessageBox.Show("myObjectB");
}
}
Now when I do this:
Base blah = new Base();
blah.SomeAction();
blah.EventFired();
I get message boxes for both A and B.
I need to implement Base so that only myObjectB gets the event.
I will have hundreds of myObject's so I need a implementation at the Base level and NOT the myObject level. Plus, handling it at the myObject level would still require the event to be fired causing performance issues if there are hundreds of objects.
One solution I have considered is when myObjectA creates the child, unsubscribe from the event, then resubscribe when we get back to the myObjectA level. However I feel something better could be done.
Anyone have any ideas?
Edit: Using payo's input I have come up with this:
public delegate void DoSomething(object sender, EventArgs data);
public class Base
{
private IObject foo;
private List<DoSomething> _myEventStorage;
public event DoSomething myEvent
{
add
{
_myEventStorage.Insert(0, value);
}
remove
{
_myEventStorage.Remove(value);
}
}
public Base ()
{
_myEventStorage = new List<DoSomething>();
foo = new myObjectA(this);
}
public void SomeAction()
{
((myObjectA)foo).CreateChild();
}
public void EventFired()
{
_myEventStorage[0].Invoke(this, new EventArgs());
}
}
you would need to explicitly implement myEvent (add/remove) handlers and track the "farthest" independently of the registered observers. then you can send the notification to that single instance.
For events, each subscriber is queued up (put at end of list), a FIFO model. You want the most-child object to 'own' the event, not just subscribe and be part of some abstract list of other unknown objects.
I would provide a new model that represents what you are trying to do. This might be what Jason recommended: (he posted his answer as I was typing this out)
public class Base
{
private DoSomething _myEventStorage;
public event DoSomething myEvent
{
add
{
_myEventStorage = value;
}
remove
{
_myEventStorage -= value;
}
}
...
public void EventFired()
{
if (_myEventStorage != null)
{
_myEventStorage(this, new ChainEventArgs());
}
}
}
This calls last ONLY. Another option (to add to this custom add/remove) would be to provide a derived EventArgs:
public class ChainEventArgs : EventArgs
{
public bool Handled { get; set; }
}
public delegate void DoSomething(object sender, ChainEventArgs data);
...
public event DoSomething myEvent
{
add
{
var temp = _myEventStorage;
_myEventStorage = null;
_myEventStorage += value;
_myEventStorage += temp; // now all are called, but FILO
}
remove
{
_myEventStorage -= value;
}
}
At this point, you can either check Handled on each IObject
void theCallingObject_myEvent(object sender, ChainEventArgs data)
{
if (data.Handled)
return;
if (I_want_to_block_parents)
data.Handled = true;
// else leave it false
}
Or, add some complexity to your Base class and stop calling up the chain (let's the children have no need to check Handled). I'll show the solution with a List<> of delegates, but some MulticaseDelegate casts and calls could do the same. I just feel the List<> code might be more readable/maintainable.
public class Base
{
private List<DoSomething> _myEventStorage;
public event DoSomething myEvent
{
add
{
_myEventStorage.Insert(0, value);
}
remove
{
_myEventStorage.Remove(value);
}
}
...
public void EventFired()
{
var args = new ChainEventArgs();
foreach (var handler in _myEventStorage)
{
handler(this, args);
if (args.Handled)
break;
}
}
}

Categories