I'm building a MVVM application in which a ToBeListened class has a couple of properties, PropertyA and PropertyB, and I want to listen to them.
public class ToBeListened : INotifyPropertyChanged
{
private double _propertyA;
private string _propertyB;
/*Here I'm omitting the update part where NotifyPropertyChanged gets called*/
public double PropertyA{get; set; }
public double PropertyB{get; set; }
public event PropertyChangedEventHandler PropertyChanged;
protected void NotifyPropertyChanged([CallerMemberName] String propertyName = "")
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
Those two properties are listened by a Listener class, so I've implemented an EventHandler in it, that listens to a ToBeListened object.
public class Listener
{
private ToBeListened toBeListenedObject;
public Listener()
{
toBeListenedObject = new ToBeListened();
toBeListenedObject.PropertyChanged += newPropertyChangedEventHandler(PropertyListener_PropertyChanged);
}
private void PropertyListener_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
switch(e.PropertyName)
{
case "PropertyA":
{
/*...DO SOMETHING...*/
}
case "PropertyB":
{
/*...Do something else...*/
}
}
The thing is, I don't really like this solution I've found. A switch-case isn't polymorphism-friendly, so
is there a better way to do this? Maybe something that uses overloading? (Like private void PropertyListener_PropertyChanged(double sender, PropertyChangedEventArgs e)
most of all, is it right to code a ViewModel like this?
I like Josh Smith's PropertyObserver, which you can get at http://mvvmfoundation.codeplex.com/ (some documentation from Josh at https://joshsmithonwpf.wordpress.com/2009/07/11/one-way-to-avoid-messy-propertychanged-event-handling/). It's a nice class that encapsulates the plumbing logic you're talking about, so you can focus on just handling changes to certain properties. So in your case, you could write code like:
var observer = new PropertyObserver<ToBeListened>(toBeListenedObject)
.RegisterHandler(tbl => tbl.PropertyA, tbl => HandlePropertyA(tbl))
.RegisterHandler(tbl => tbl.PropertyB, tbl => HandlePropertyB(tbl));
You can start using it by installing the MVVM Foundation nuget package into your solution. The ID is MvvmFoundation.Wpf.
In the past I've used a little class derived from Dictionary<string, Action> for this purpose. It was something like this:
public class PropertyChangedHandler : Dictionary<string, Action>
{
public PropertyChangedHandler(INotifyPropertyChanged source)
{
source.PropertyChanged += Source_PropertyChanged;
}
private void Source_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
Action toDo;
if (TryGetValue(e.PropertyName, out toDo))
{
toDo();
}
}
}
Then your listener looks like this:
public class Listener
{
private ToBeListened toBeListenedObject = new ToBeListened();
PropertyChangedHandler handler;
public Listener()
{
handler = new PropertyChangedHandler(toBeListenedObject)
{
{ "PropertyA", DoA },
{ "PropertyB", DoB }
};
}
private void DoB()
{
}
private void DoA()
{
}
}
This is just an example to give you an idea - it can be expanded for more complex purposes, of course.
i think MVVM Light framework (or library?) has what you need. Take a look at their ObservableObject class http://www.mvvmlight.net/help/SL5/html/d457231f-6af7-601d-fa1f-1fe7c9f60c57.htm
Basically what it does, is making your object observable.
Related
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; }
}
I will explain what I am trying to do first.
I have a quite a few of DataGrids and each DataGrid use different classes for there data type and instead of subscribing an Event handler for each one, I was hoping to make a generic event handler and get the type from from the sender object.
I am using EntityFramework Database First
One example of one of the classes:
public partial class StaffData : INotifyPropertyChanged
{
public long ID { get; set; }
public string StaffNameFirst { get; set; }
public string StaffNameSecond { get; set; }
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName]string propertyName = null)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
My ViewModel:
VeiwModelBase holds the INotifyPropertyChanged data.
public class MasterViewModel : ViewModelBase
{
public static ObservableCollection<StaffData> MasterDataBinding
{
get { return _mMasterData; }
private set
{
if (value == _mMasterData)
return;
_mMasterData = value;
OnPropertyChanged();
}
}
public MasterViewModel()
{
_mMasterData.CollectionChanged += master_CollectionChanged;
}
public void master_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
//asign PropertyChanged event here
}
private void master_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
Type foo = sender.GetType().GetGenericArguments()[0];
var newRowData = sender as foo;
SaveData(newRowData);
}
private static void SaveData(object newRowData)
{
Type foo = newRowData.GetType().GetGenericArguments()[0];
var originalData = dataBaseEntities.foo.FirstOrDefault(p => p.ID == newRowData.ID);
entities.Entry(originalData).CurrentValues.SetValues(newRowData);
dataBaseEntities.SaveChanges();
}
}
These are the two methods above which I can't seem to figure this out, I have tried countless ways using Getype with not much success (I left my last attempt in hopefully to illustrate what I am trying to do). I have commented out how I am normally going about this:
private void master_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
Type foo = sender.GetType().GetGenericArguments()[0];
var newRowData = sender as foo;
//var newRowData = sender as StaffData
SaveData(newRowData);
}
//private static void SaveData(StaffData newRowData)
private static void SaveData(object newRowData)
{
Type foo = newRowData.GetType().GetGenericArguments()[0];
var originalData = dataBaseEntities.foo.FirstOrDefault(p => p.ID == newRowData.ID);
//var originalData = dataBaseEntities.StaffData.FirstOrDefault(p => p.ID == newRowData.ID);
entities.Entry(originalData).CurrentValues.SetValues(newRowData);
entities.SaveChanges();
}
When trying to use the variable as a type I get this error,
Error CS0118 'foo' is a variable but is used like a
type
Is there a way to get the type when you don't know which datagrid will implement the PropertyChanged event and use it so as you can make a generic event handler for all the Datagrid controls?
Or am I going about this the wrong way?
Not sure if I really understand your question, but you could check the type of the sender argument at runtime and call an appropriate method like this:
private void master_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
if (sender is StaffData)
{
DoSomething((StaffData)sender);
}
else if (sender is SomeOtherData)
{
DoSomething((SomeOtherData)sender);
}
...
}
private void DoSomething(StaffData data)
{
...
}
private void DoSomething(SomeOtherData data)
{
...
}
However, I'd prefer to have different PropertyChanged handler methods for different sender types.
You cant get the type inside the propertyChanged event handler but you can get the property name from PropertyChangedEventArgs.
Something like:
private void OnPropertyChanged(object sender, PropertyChangedEventArgs args)
{
if (args.PropertyName == "SomePropertyName")
{
//... do your stuf
}
}
I have a class which facilitates the discovery of HID devices, when a device is detected an event is raised and subsequently detected by another class which will be ultimately responsible for the creation of an object to represent the HID device. The creation class raises an event of its own with the newly created HID object.
With this in mind I have a couple of design queries:
1)
I have done some research into the "best practices" with regards to the creation of an unknown number or type of object at runtime for which the Abstract Factory design pattern features frequently in the results. Does the Abstract Factory design pattern fit the scenario I have, or is there something else I should be doing?
2)
The HidFinder class raises an event to notify those interested (mostly the HidCreator class) that a device has been discovered. The HidCreator class then raises an event containing the newly created HID device. This seems like the correct method, however, confirmation either way would be appreciated.
Below is a dumbed down example of the code in question.
public class HidFinder
{
public event EventHandler<HidFoundArgs> HidFoundHandler;
private void DeviceAdded(object sender, EventArrivedEventArgs e)
{
OnHidFoundHandler(new HidFoundArgs());
}
protected virtual void OnHidFoundHandler(HidFoundArgs e)
{
EventHandler<HidFoundArgs> handler = this.HidFoundHandler;
if (handler != null)
{
handler(this, e);
}
}
}
public class HidCreator
{
private readonly HidFinder hidFinder;
public event EventHandler<IHidDevice> HidDeviceCreatedHandler;
public HidCreator(HidFinder hidFinder)
{
this.hidFinder = hidFinder;
this.hidFinder.HidFoundHandler += HidFinderOnHidFoundHandler;
}
private void HidFinderOnHidFoundHandler(object sender, HidFoundArgs hidFoundArgs)
{
// Create a new HID
var newHidDevice = Factory.CreateMethod();
OnHidDeviceCreatedHandler(newHidDevice);
}
protected virtual void OnHidDeviceCreatedHandler(IHidDevice e)
{
EventHandler<IHidDevice> handler = this.HidDeviceCreatedHandler;
if (handler != null)
{
handler(this, e);
}
}
}
It generally looks as good design, but I would make two changes:
Factory seems like some global object, it would be better to use Dependency Injection, for better unit testing for example.
Change Factory.CreateMethod to use parameters, as we don't know what exact implementation of IHidDevice will be created and if we won't need some additional info from HidFoundArgs
Code after changes:
public class HidCreator
{
private readonly HidFinder hidFinder;
private readonly IHidDeviceFactory factory;
public event EventHandler<IHidDevice> HidDeviceCreatedHandler;
public HidCreator(IHidDeviceFactory factory, HidFinder hidFinder)
{
this.factory = factory;
this.hidFinder = hidFinder;
this.hidFinder.HidFoundHandler += HidFinderOnHidFoundHandler;
}
private void HidFinderOnHidFoundHandler(object sender, HidFoundArgs hidFoundArgs)
{
// Create a new HID
var newHidDevice = factory.Create(HidFoundArgs.ToCreationParameters(hidFoundArgs));
OnHidDeviceCreatedHandler(newHidDevice);
}
protected virtual void OnHidDeviceCreatedHandler(IHidDevice e)
{
EventHandler<IHidDevice> handler = this.HidDeviceCreatedHandler;
if (handler != null)
{
handler(this, e);
}
}
}
public interface IHidDeviceFactory
{
IHidDevice Create(HidCreationParameters parameters);
...
}
public class HidCreationParameters
{
...
}
public class HidFoundArgs
{
public static HidCreationParameters ToCreationParameters(HidFoundArgs args)
{
...
}
}
Basically I've got a command binding for the command itself assigned to Window.CommandBindings:
<CommandBinding Command="local:TimerViewModel.AddTimer"
CanExecute="local:TimerViewModel.AddTimer_CanExecute"
Executed="local:TimerViewModel.AddTimer_Executed" />
local is a namespace generated by default pointing to the namespace of the application. What I'm trying to achieve here is to have the command handling inside the TimerViewModel but I keep getting the following error:
CanExecute="local:TimerViewModel.AddTimer_CanExecute" is not valid. 'local:TimerViewModel.AddTimer_CanExecute' is not a valid event handler method name. Only instance methods on the generated or code-behind class are valid.
The TimerViewModel is pretty simple though but I believe I am missing something:
public class TimerViewModel : ViewModelBase
{
public TimerViewModel()
{
_timers = new ObservableCollection<TimerModel>();
_addTimer = new RoutedUICommand("Add Timer", "AddTimer", GetType());
}
private ObservableCollection<TimerModel> _timers;
public ObservableCollection<TimerModel> Timers
{
get { return _timers; }
}
private static RoutedUICommand _addTimer;
public static RoutedUICommand AddTimer
{
get { return _addTimer; }
}
public void AddTimer_CanExecute(object sender, CanExecuteRoutedEventArgs e)
{
e.CanExecute = true;
}
public void AddTimer_Executed(object sender, ExecutedRoutedEventArgs e)
{
_timers.Add(new TimerModel(TimeSpan.FromSeconds((new Random()).Next())));
}
}
Can anyone point out the mistakes I'm making?
Also take a look at Josh Smith's RelayCommand. Using it would enable you to write the above like this:
public class TimerViewModel : ViewModelBase {
public TimerViewModel() {
Timers = new ObservableCollection<TimerModel>();
AddTimerCommand = new RelayCommand(() => AddTimer());
}
public ObservableCollection<TimerModel> Timers {
get;
private set;
}
public ICommand AddTimerCommand {
get;
private set;
}
private void AddTimer() {
Timers.Add(new TimerModel(TimeSpan.FromSeconds((new Random()).Next())));
}
}
Take a look at http://www.wpftutorial.net/DelegateCommand.html for an example of how to implement the delegate command for WPF. It allows you to hook up Execute and CanExecute as event handlers. If you're using RoutedUICommand directly you need to derive a custom command from it and override Execute and CanExecute with your functions.
basically:
public delegate void RecvCommandHandler (ChatApplication sender, byte[] content);
event RecvCommandHandler[] commands = new RecvCommandHandler[255];
I want to activate a different method/function for each command number, but I am really uncertain of the syntax. How am I supposed to do it?
I think I'll go with just an array of delegates for this one, but the question is still interesting.
You could create an array of a class with operator overloading to simulate the behavior you are interested in...
public delegate void EventDelegate(EventData kEvent);
public class EventElement
{
protected event EventDelegate eventdelegate;
public void Dispatch(EventData kEvent)
{
if (eventdelegate != null)
{
eventdelegate(kEvent);
}
}
public static EventElement operator +(EventElement kElement, EventDelegate kDelegate)
{
kElement.eventdelegate += kDelegate;
return kElement;
}
public static EventElement operator -(EventElement kElement, EventDelegate kDelegate)
{
kElement.eventdelegate -= kDelegate;
return kElement;
}
}
public EventElement[] commands = new EventElement[255];
commands[100] += OnWhatever;
commands[100].Dispatch(new EventData());
commands[100] -= OnWhatever;
There's really no concept of an array of events - it's like talking about an array of properties. Events are really just methods which let you subscribe and unsubscribe handlers. If you need to be able to do this by index, I suggest you just have a pair of methods. (AddCommandHandler(int, RecvCommandHandler) and RemoveCommandHandler(int, RecvCommandHandler)). That won't support the normal event handling syntactic sugar, of course, but I don't see that there's a lot of alternative.
The other option is to specify and index in the delegate prototype and have one event handler that "delegates" to the others, e.g.:
public delegate void RecvCommandHandler (int id, ChatApplication sender, byte[] content);
// ...
private RecvCommandHandler[] internalhandlers;
public void MyCommandHandler(int id, ChatApplication sender, byte[] content)
{
internalHandlers[id](id, sender, content);
}
I was just looking for the same answer, however my class is also event sender for WPF, so it should look as much as normal C#/WPF event sender class. So I simply added this:
To sender:
enum with properties name -- this is lame workaround for lack of nameof
one additional method to record requests
To receiver:
request event for given enum
The code, sender:
public enum Properties
{
NetworkFileName,
DatasetFileName,
LearningWatch
}
private string network_filename;
public string NetworkFileName
{
get { return network_filename; }
private set
{
if (network_filename != value)
{
network_filename = value;
OnPropertyChanged(Properties.NetworkFileName.ToString());
}
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string name)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(name));
}
}
public void OnChange(Properties prop, Action<object, PropertyChangedEventArgs> action)
{
PropertyChanged += new PropertyChangedEventHandler((obj, args) => { if (args.PropertyName == prop.ToString()) action(obj, args); });
}
And to the receiver:
private void OnNetworkLoaded(object sender, PropertyChangedEventArgs e)
{
SetTitle();
}
...
ExpManager.OnChange(ExperimentManager.Properties.DatasetFileName, OnDatasetLoaded);
It is still ugly, but at least:
I don't have to deal with "ifs" in receiver
I can easily create multiple event handlers
it is compatible with WPF
no magic strings (I hate those)
Disadvantage:
obsfuscation ruins this (but I have special class for that case, this project is just for me, so no problem here)