I have a ViewModel that encapsulates some properties that are being edited in an options dialog. I can't actually save them to the settings until they hit the Ok button, which will end up calling Commit on this particular ViewModel.
A single property in my ViewModel looks like this:
public bool SomeProperty
{
get
{
return m_SomeProperty;
}
set
{
if (m_SomeProperty != value)
{
m_SomeProperty = value;
NotifyPropertyChanged("SomeProperty");
}
}
}
private bool m_SomeProperty = Properties.Settings.Default.SomeProperty;
So the normal implementation for Commit would be to do this:
public void Commit()
{
Properties.Settings.Default.SomeProperty = m_SomeProperty;
// Add other properties here...
}
This isn't so bad, but the reason I don't like this is that if you add a new property, you have to add code for it in two places. I try to avoid that when possible.
At first I thought I could declare a private event called OnCommit and have the Commit method raise that event, and have the code for each property add an event handler for the event and do the writing to the settings there, but I don't know how to do that without adding the event handlers in the constructor anyway, which doesn't help the situation.
Any ideas? Does anyone have an elegant way to do what I'm trying to do?
EDIT: Thanks to sixlettervariables for the answer. I took that idea and incorporated it into SoapBox Core and open sourced the result. Check out the Options Dialog to see how it works.
Perhaps maintain a list of Actions to execute?
private List<Action> commitActions = new List<Action>();
public bool SomeProperty
{
get
{
return m_SomeProperty;
}
set
{
if (m_SomeProperty != value)
{
m_SomeProperty = value;
lock (commitActions)
{
commitActions.Add(
() => Properties.Settings.Default.SomeProperty = value);
}
NotifyPropertyChanged("SomeProperty");
}
}
}
Then update your Commit code to loop through the actions.
public void Commit()
{
List<Action> commits;
lock (commitActions)
{
commits = new List<Action>(commitActions);
commitActions.Clear();
}
foreach (var commit in commits)
{
commit();
}
}
Could you use reflection to determine which properties your class has and iterate through them?
Related
I'm creating a asp.net core application which is using the new HostedService-feature of asp.net core 2.1. Some of those services registered via services.AddHostedService() are dependant on other services and I'm looking for a way to notify the "child-services" when the "parent-service" gets ready.
My first implementation looked like this:
// Parent class
public delegate void OnServiceReadyHandler(object sender);
public event OnServiceReadyHandler OnServiceReady;
public void Init()
{
// Do stuff
OnServiceReady?.Invoke(this);
}
// Child class
_parentService.OnServiceReady += (sender) =>
{
// Do stuff
};
But this leads to a problem when the child class subscribes to the OnServiceReady-Event after it already fired, therefore never getting notified.
I then thought about a property on the parent-class, something like IsRead, but this might as well lead to a race condition (between reading the property and subscribing to the ready-event).
Doing lots of web stuff lately jQuerys document.ready-event came to my mind. It fires after the document got ready or immediately if it is already in ready-state. I changed my implementation to this:
// Parent class
private bool IsReady { get; set; } = false;
public delegate void OnServiceReadyHandler(object sender);
private OnServiceReadyHandler onServiceReadyHandler;
public event OnServiceReadyHandler OnServiceReady
{
add
{
if (!IsReady)
onServiceReadyHandler = (OnServiceReadyHandler)Delegate.Combine(onServiceReadyHandler, value);
else
value(this);
}
// Remove ...
}
public void Init()
{
// Do stuff
IsReady = true;
OnServiceReady?.Invoke(this);
}
// Child class
_parentService.OnServiceReady += (sender) =>
{
// Do stuff
};
This looks like it is working fine, but I'm wondering if there's a) still the chance of a race condition and the client not getting notified here and b) a more elegant solution to this problem or this implementation?
Yes, you can still have a race condition here:
if (!IsReady)
onServiceReadyHandler = (OnServiceReadyHandler)Delegate.Combine(onServiceReadyHandler, value);
You could have a case where IsReady is evaluated to false, but before the event handler can be updated, the Init() method sets IsReady to true and fires the event. Then the second line above runs and the event never gets fired again.
The example in the documentation for the add keyword shows the use of lock. That's probably what you should be using here - in the add and remove accessors, as well as in your Init() method.
Also, is there any reason you are using Delegate.Combine() here? The docs just show you can use +=.
public event OnServiceReadyHandler OnServiceReady
{
add
{
lock (onServiceReadyHandler) {
if (!IsReady)
onServiceReadyHandler += value;
else
value(this);
}
}
// Remove ...
}
public void Init()
{
// Do stuff
lock (onServiceReadyHandler) {
IsReady = true;
}
OnServiceReady?.Invoke(this);
}
I'm probably totally misunderstanding what RX is all about, but I thought it would be a neat way of allowing various client applications in my code to subscribe to notifications of changes to certain Entity Framework Code First types.
So in my UOW Commit methood I have
var changes = DbContext.ChangeTracker.Entries<EntEvent>().Where(ee => ee.State != EntityState.Unchanged);
Hub.Instance.NotifyBeforeSave(changes);
and my (rather basic) hub class looks like this...
public sealed class Hub
{
private static readonly Hub instance = new Hub();
static Hub(){}
private Hub(){}
public static Hub Instance
{
get { return instance; }
}
public IObservable<System.Data.Entity.Infrastructure.DbEntityEntry<EntEvent>> BeforeSave = new Subject<DbEntityEntry<EntEvent>>();
public void NotifyBeforeSave<T>(IEnumerable<System.Data.Entity.Infrastructure.DbEntityEntry<T>> changes) where T:class
{
var x = changes.Where(c => typeof(T) == typeof(EntEvent)) as IEnumerable<System.Data.Entity.Infrastructure.DbEntityEntry<EntEvent>>;
BeforeSave = x.ToObservable();
}
}
and then I thought I could subscribe a client (observer) by creating an instance of the following and calling attach.
public class SampleConsumer : IObserver<DbEntityEntry<EntEvent>>
{
public void attach()
{
Hub.Instance.BeforeSave.Subscribe(this);
}
public void OnNext(DbEntityEntry<EntEvent> value)
{
var x = value;
}
public void OnError(Exception error)
{
var y = error;
}
public void OnCompleted()
{
}
}
but breakpoints in OnNext and OnError never get called.
I'm probably 180deg away from where I should be, but we have to start somewhere!
The problem is that you don't have an asynchronous source.
DbContext.ChangeTracker.Entries<EntEvent>()
is a collection. You can convert it to an observable using
IEnumerble.ToObservable();
but that does not make it asynchronous. In fact, it will enumerate the collection right away upon subscription. If the collection happens to be empty, it will do nothing at all. Google the difference between cold/hot observables to understand.
You need an asynchronous source, something like an event.
I don't know EF very well, my guess is that the
((IObjectContextAdapter)DbContext).ObjectContext.SavingChanges
event might be what you need.
Good luck!
Plug in Nick's
https://github.com/NickStrupat/EntityFramework.Triggers
https://github.com/NickStrupat/EntityFramework.Rx
He has patterns with and without deriving from his context, that permit:
DbObservable<Context>.FromInserted<Person>();
I got a foreach loop, where I update a property of the objects. This property has a dependency which I update in the setter. This seems to slow my application, because the dependency takes some time and would have to be updated only once, after the foreach loop.
It is not recommended to call the update of the dependency after the loop, because the setter is used in many other places in my code. Beyond that, the object should be responsible to update it's dependency and not the calling function.
code example for clarity
//anywhere else in my other classes
private Foo[] objects;
public void UpdateFoo()
{
//update propably hundrets of small objects
foreach (Foo obj in objects)
{
obj.Property = 1;
}
}
class Foo
{
private int _property;
public int Property
{
get { return _property; }
set
{
_property = value;
//Update something anywhere else
StaticBigFoo.Update();
} }
}
class StaticBigFoo
{
public static void Update()
{
//do something longer
}
}
I'm wondering what's best practice for this szenario?
I could use a DependencyProperty and it's PropertyChanged-Callback, but then the dependency would still be updated every time.
I thought about starting something in the Dispatcher with DispatcherPriority.Background and filter it for distinct delegates (but how?)
I could use some transactional logic, but I do not know what to use there. I read something about TransactionScope, can I use it for something like this?
You could use a section defined by BeginUpdate() and EndUpdate() calls as provided by several GUI elements. What I mean is something like the following:
class Foo
{
private int _property;
public int Property
{
get { return _property; }
set
{
_property = value;
if(inUpdate)
propertyChanged = true;
else
//Update something anywhere else
StaticBigFoo.Update();
} }
static bool inUpdate = false;
static bool propertyChanged;
public static void BeginUpdate() { inUpdate = true; propertyChanged = false; }
public static void EndUpdate() { inUpdate = false; if(propertyChanged) StaticBigFoo.Update(); }
}
And then
Foo.BeginUpdate();
foreach (Foo obj in objects)
{
obj.Property = 1;
}
Foo.EndUpdate();
That allows to defer the update if needed.
Just a food for thought, properties are mainly used to get/set values of a field in a controlled fashion. Or in your case, an extra mile more which is change notification. Arguably, can have validations,lazy loading. But it seems you are doing lot more than that within the setter, which I not a best practice. Why because, we likely to access properties more often, which cause the properties to be evaluated hence the underlying logic .
I would leave setters clean and in them do just the field assignement and PropertyChanged. Then listen to PropertyChanged and do the extra stuff there (in a separate thread if you want - using task here for simplicity);
class Foo
{
//constructor
public Foo()
{
PropertyChanged += (s,args) =>
{
switch(args.PropertyName)
{
case "Property" :
Task.Factory.StartNew(() => { StaticBigFoo.Update();});
break;
....
public int Property
{ get ...
{
set
{
if(_property == value) return;
_property = value;
RaisePropertyChanged(() => Property);
......
I have a big problem with MVVM design. I am trying to catch every PropertyChanged of my inner nested objects, including futhermore propertchanged of their nested objects, inside my ViewModel but I dont know how to do it.
Here is my structure:
class MyVM
{
public MyVM()
{
this.SomeData = new SomeData();
this.SomeData.NestedObj = new MyNestedDat();
this.SomeData.Str = "This tiggers propertychanged inside MyDat class";
// this triggers propertychanged event inside MyNestedDat class
this.SomeData.NestedObj.Num = 123;
}
// and here should be a method where i catch all possibe propertychanges from my nested objets and their nested objets, how do i do that?
public MyDat SomeData
{
get;
set;
}
}
class MyDat : INotifyPropertyChanged
{
private string str;
public string Str;
{
get { return this.str;}
set
{
this.str = value;
this.PropertyChanged(this, "Str");
}
}
publicMyNestedDat NestedObj
{
get;
set;
}
}
class MyNestedDat : INotifyPropertyChanged
{
private int num;
public int Num
{
get{ return this.num;}
set
{
this.num = value;
this.PropertyChanged(this, "Num");
}
}
}
How do i get this to work? I am really clueless where to start.
MyNestedDat class throws PropertyChanged, MyDat class throws propertychanged and i want to catch them all inside my viewmodel. How can i do that?
In my opinion there are a few conceptual things wrong with what you are asking. Just imagine you get a solution that works for your scenario (that you are happy with) and consider the following:
What happens if another layer is added? do you still expect it to work the same?
Should property changes be propagated (viewModel1.propA notifies viewModel2.PropA)?
Should property changes be transformed (viewModel1.SomeProp notifies ViewModel2.AnotherProp)?
Is performance a concern? how will this perform if you need to propagate the property changed events through many levels?
This should be raising alarm bells that the current approach is not the right path to tread.
What you need is a way to provide communication between your viewModels in a loosely coupled way so that you viewModels do not even need to know about each others existence. The beauty of this is that this will also work in other situations not just for property changes.
For your case of property changed events, one viewModel wants to know when something happens (it could be something other than a property changed event, remember). This means the other viewModel needs some way of saying "Hey, a property has changed" (or "My state has changed", "That database call has finished" etc).
Now in C# you can provide events which provide this feature....except, now your objects know about each other which leaves you with the same problem you had before.
To overcome this problem you need another object, a mediator (lets call it Messenger in this example), whose sole purpose is to handle the message passing between the objects so that they can live in ignorance of each other.
The general idea is this. In the viewModel that provides notifications you might do something like this:
public string MyProp
{
get { return _myProp; }
set
{
_mProp = value;
OnPropertyChanged("MyProp");
Messenger.PostMessage(new VMChangedMessage { ViewModel = this, PropertyName = "MyProp" });
}
}
And in the viewModel that is interested in the event you might do something like this:
public class ViewModel2
{
public ViewModel2()
{
Messenger.Subscribe<VMChangedMessage>(handleMessage);
}
private void handleMessage(VMChangedMessage msg)
{
// Do something with the information here...
}
}
Notice that the two viewModels never reference each other. They are now loosely-coupled.
There are a number of pre-existing implementations already available and it isn't difficult to create your own (the messenger basically keeps a list of objects that are interested in a certain message and iterates the list when it needs to notify the interested parties). There are a few things that can be implemented differently (some implementations just pass string messages around rather than encapsulating the information in objects, and some handle the clean-up of observers automatically).
I would recommend using Josh Smiths (excellent) MVVM Foundation which includes a messenger class. It's also open source so you can see how it works.
There is no clear constraint about what PropertyName should contains in PropertyChangedEventArgs.
See Subscribe to INotifyPropertyChanged for nested (child) objects.
Here is an example :
class A : BaseObjectImplementingINotifyPropertyChanged {
private string m_name;
public string Name {
get { return m_name; }
set {
if(m_name != value) {
m_name = value;
RaisePropertyChanged("Name");
}
}
}
}
class B : BaseObjectImplementingINotifyPropertyChanged {
private A m_a;
public A A {
get { return m_a; }
set {
if(m_a != value) {
if(m_a != null) m_a.PropertyChanged -= OnAPropertyChanged;
m_a = value;
if(m_a != null) m_a.PropertyChanged += OnAPropertyChanged;
RaisePropertyChanged("A");
}
}
}
private void OnAPropertyChanged(object sender, PropertyChangedEventArgs e) {
RaisePropertyChanged("A." + e.PropertyName);
}
}
B b = new B();
b.PropertyChanged += (s, e) => { Console.WriteLine(e.PropertyName); };
b.A.Name = "Blah"; // Will print "A.Name"
The best thing to do here is to separate the idea of a Model and a ViewModel.
By having a ViewModel object that is flatter than the Model you can avoid this scenario. Using an automatic mapping tool like Automapper then allows you to map the Model to the ViewModel and vice versa.
https://github.com/AutoMapper/AutoMapper/wiki/Flattening
class MyDatViewModel : INotifyPropertyChanged
{
public string Str
{
// ... Get Set
}
public int NestedObjNum
{
// ... Get set
}
}
// Configure AutoMapper
Mapper.CreateMap<MyDat, MyDatViewModel>();
// Perform mapping
MyDatViewModel viewModel = Mapper.Map<MyDat, MyDatViewModel>(someData);
Let's just say that I have:
public Boolean booleanValue;
public bool someMethod(string value)
{
// Do some work in here.
return booleanValue = true;
}
How can I create an event handler that fires up when the booleanValue has changed? Is it possible?
Avoid using public fields as a rule in general. Try to keep them private as much as you can. Then, you can use a wrapper property firing your event. See the example:
class Foo
{
Boolean _booleanValue;
public bool BooleanValue
{
get { return _booleanValue; }
set
{
_booleanValue = value;
if (ValueChanged != null) ValueChanged(value);
}
}
public event ValueChangedEventHandler ValueChanged;
}
delegate void ValueChangedEventHandler(bool value);
That is one simple, "native" way to achieve what you need. There are other ways, even offered by the .NET Framework, but the above approach is just an example.
INotifyPropertyChanged is already defined to notify if property is changed.
Wrap your variable in property and use INotifyPropertyChanged interface.
Change the access of the BooleanValue to private and only allow changing it through one method for consistency.
Fire your custom event in that method
.
private bool _boolValue;
public void ChangeValue(bool value)
{
_boolValue = value;
// Fire your event here
}
Option 2: Make it a property and fire the event in the setter
public bool BoolValue { get { ... } set { _boolValue = value; //Fire Event } }
Edit: As others have said INotifyPropertyChanged is the .NET standard way to do this.
Perhaps take a look at the INotifyPropertyChanged interface. You're bound to come across it's use again in future:
MSDN: http://msdn.microsoft.com/en-us/library/system.componentmodel.inotifypropertychanged.aspx
CallingClass.BoolChangeEvent += new Action<bool>(AddressOfFunction);
In your class with the bool property procedure:
public event Action<bool> BoolChangeEvent;
public Boolean booleanValue;
public bool someMethod(string value)
{
// Raise event to signify the bool value has been set.
BoolChangeEvent(value);
// Do some work in here.
booleanValue = true;
return booleanValue;
}
No it is not possible* to get notified about for changes in value of a variable.
You can achieve almost what you want by making the value to be a property of some class and fire events on change as you wish.
*) if your code is debugger for a process you can make CPU to notify you about changes - see data chage breakpoints in Visual Studio. This will require at least some amount of native code and harder to implement correctly for manged code due to hance of objects to be moved in memory by GC.