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);
......
Related
Currently I have three properties in class MyClassOne. The number of properties may increase in future. Whenever any of the property changes, I need to call a method called SavePropertyToFile() which is responsible to set the corresponding property (AnotherPropertyOne, AnotherPropertyTwo or AnotherPropertyThree) of another class.
My sample code (which is of course not working) is below:
Class MyClassOne
{
public bool PropertyOne
{
get => _propertyOne;
set
{
if (_propertyOne == value)
return;
_propertyOne = value;
SavePropertyToFile(value, MyAnotherClass.AnotherPropertyOne); //<------------------
NotifyOfPropertyChange(() => _propertyOne);
}
}
public bool PropertyTwo
{
get => _propertyTwo;
set
{
if (_propertyTwo == value)
return;
_propertyTwo = value;
SavePropertyToFile(value, MyAnotherClass.AnotherPropertyTwo); //<------------------
NotifyOfPropertyChange(() => _propertyTwo);
}
}
public bool PropertyThree
{
get => _propertyThree;
set
{
if (_propertyThree == value)
return;
_propertyThree = value;
SavePropertyToFile(value, MyAnotherClass.AnotherPropertyThree); //<------------------
NotifyOfPropertyChange(() => _propertyThree);
}
}
SavePropertyToFile(bool value, TProperty myPropertyOfAnotherClass)
{
myPropertyOfAnotherClass = value;
// Some more lines of Code
RefreshProgram();
}
}
In the code above, I need to find a way so that I can parse the correct property of AnotherClass. I already had a look at this another SO question, but I am not able to apply any of the answer for my case.
How can I parse different properties to a method?
Update: As per the suggestions in the comments, I have tried to use "Nathan Baulch" answer's option-2 but then I getting warning/error wiggly line as shown in the screenshot below:
If the idea is to make several classes use the same property, my approach is to wrap the values in a class:
public class Changeable<T> : INotifyPropertyChanged
{
private T currentValue;
public Changeable(T initialValue) => currentValue = initialValue;
public T Value
{
get => currentValue;
set
{
if (!EqualityComparer<T>.Default.Equals(currentValue, value))
{
this.currentValue = value;
Changed?.Invoke(this, value);
OnPropertyChanged(nameof(Value));
}
}
}
public event EventHandler<T> Changed;
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
=> PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
You can then just let both classes use the same actual object. This approach can also be used to cut down on boilerplate for wpf, instead of declaring getters and setters you can just write:
public Changeable<bool> MyProperty {get;} => new (false);
Just remember to update the binding: {Binding MyProperty.Value}
You can also write various extensions. For example if you want to update one property whenever some other property updates:
MyProperty.BindTo(MyIntProperty, intValue => intValue > 0);
So whenever MyIntProperty changes sign, the MyProperty would be updated and raise its changed event. I'm not going to include all the code behind this, since it is fairly large, but I hope you get the idea. This approach makes it possible to link various UI values in a convenient way, but it can also make the code more difficult to understand and debug, so some caution is advised.
I want to set a dirty flag for any of the required properties in my view model. I initialize IsDirty to false in the constructor. Unfortunately all of the setters in my properties are called after the constructor. Is there a way I can set IsDirty to false after all of the setters? The setters all have a line IsDirty=true;
I'm using the Prism framework with Xamarin 4.0, but Prism documentation doesn't have anything on the ViewModel life cycle.
My redacted constructor looks like this:
public SomeDetailsViewModel(INavigationService navigationService) : base(navigationService)
{
Sample = new SampleDTO();
InitializeLookupValues();
_samplesService = new SampleService(BaseUrl);
TextChangedCommand = new Command(() => OnTextChanged());
AddSampleCommand = new Command(() => AddCurrentSample());
CancelCommand = new Command(() => Cancel());
IsDirty = false;
}
Edit 3:
The constructor calls InitializeLookupValues(). These appear to be the culprit.
private async Task InitializeLookupValues()
{
App app = Prism.PrismApplicationBase.Current as App;
string baseUrl = app.Properties["ApiBaseAddress"] as string;
_lookupService = new LookupDataService(baseUrl);
int TbId = app.CurrentProtocol.TbId;
int accessionId = CollectionModel.Instance.Accession.AccessionId;
Parts = await _lookupService.GetParts(accessionId);//HACK
Containers = await _lookupService.GetSampleContainers(TbId);
Additives = await _lookupService.GetAdditives(TbId);
UnitsOfMeasure = await _lookupService.GetUnitsOfMeasure();
// with a few more awaits not included.
}
After exiting the constructor each of the properties are set. They look like this one.
public ObservableCollection<PartDTO> Parts
{
get
{
return parts;
}
set
{
SetProperty(ref parts, value);
}
}
private PartDTO part;
public PartDTO SelectedPart
{
get
{
return part;
}
set
{
SetProperty(ref part, value);
IsDirty = true;
}
}
Where IsDirty is defined thus:
private bool isDirty;
public bool IsDirty
{
get
{
return isDirty;
}
set
{
SetProperty(ref isDirty, value);
Sample.DirtyFlag = value;
}
}
I haven't explicitly set any of the properties. I would like to avoid their being initialized automatically, or call something after them.
Edit
Just a note to everyone I have been debugging to find out what I could. I found that in each data-bound property the getter is called twice, then the setter is called. I looked at what generated code I could find, and there is no obvious place where data binding is explicitly calling the setter.
Edit 2
What I hadn't shown before, and now looks likes it's a critical piece of information, was that I populate the ObservableCollection with an async call to a service. As far as I can tell, because of XAML data binding, the SelectedPart property setter is called. If I debug slowly this start to show in some places. I've added the async call above.
Is there a way I can set IsDirty to false after all of the setters?
The setters aren't called by themselves, there has to be someone calling them. You should identify who's doing that and either stop him from setting stuff without good reason (preferred) or make him reset the dirty flag after he's done.
As suggested in the comments, adding a breakpoint in the setter and having a look at the stacktrace is a good starting point for finding the source of the setting... if I had to guess, I'd suspect some navigation related callback.
But you should try to make sure that the view model is initialized after the constructor and that IsDirty actually means "has been changed through the view" and not "maybe changed by the user, might also be just part of a delayed initialization".
After your multiple edits, an edit from me:
You should modify your architecture to account for the asynchronous initialization of your view model. Just running everything in parallel and hoping for the best rarely works.
You could make the properties read-only until initialization is complete, for instance, and set IsDirty to false at the end of InitializeLookupValues.
Pseudo-Code:
Constructor()
{
Task.Run( async () => await InitializeAsync() );
}
string Property
{
get => _backingField;
set
{
if (_isInitialized && SetProperty( ref _backingField, value ))
_isDirty = true;
}
}
private async Task InitializeAsync()
{
await SomeAsynchronousStuff();
_isInitialized = true;
}
private bool _isInitialized;
private bool _isDirty;
Probably, you want to expose _isInitialized as a property to the view to show some hourglass, and use a ManualResetEvent instead of a simple bool... but you get the idea.
Since the SetProperty methods are overridable you can inject some custom logic. This could be very useful for when you have objects that you need to validate if they have been altered.
public class StatefulObject : Prism.Mvvm.BindableBase
{
private bool _isDirty;
public bool IsDirty
{
get => _isDirty;
private set => SetProperty(ref _isDirty, value);
}
protected override bool SetProperty<T>(ref T storage, T value, Action onChanged, [CallerMemberName] string propertyName = null)
{
var isDirty = base.SetProperty(ref storage, value, onChanged, propertyName);
if(isDirty && propertyName != nameof(isDirty))
{
IsDirty = true;
}
return isDirty;
}
public void Reset() => IsDirty = false;
}
Keep in mind that when you initialize fields in this IsDirty would be true, so before binding you would want to call the Reset method to set IsDirty back to false that way you can reliably know when a field has been changed.
Note that how you handle this is somewhat up to you. For instance you might do this with Linq...
var fooDTOs = someService.GetDTOs().Select(x => { x.Reset(); return x; });
You might also enforce a pattern like:
public class FooDTO : StatefulObject
{
public FooDTO(string prop1, string prop2)
{
// Set the properties...
Prop1 = prop1;
// Ensure IsDirty is false;
Reset();
}
}
So I have seen some responses to similar questions as this, but I was wondering if a certain paradigm that I am thinking of is even possible in C#. First, I'll lay out the issue:
I have a MVVM application that I am developing in C#. The model has properties that change, and when a single property changes in the model, it often times affects multiple properties in the view-model. So the view-model listens for changes on the model. And the view listens for changes on the view-model.
In my view-model, I end up getting some code that looks like this:
private void OnModelPropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
{
string prop_name = e.PropertyName;
if (prop_name.Equals("some_property_on_the_model"))
{
NotifyPropertyChanged("some_property_on_the_view_model");
NotifyPropertyChanged("some_property_on_the_view_model");
NotifyPropertyChanged("some_property_on_the_view_model");
NotifyPropertyChanged("some_property_on_the_view_model");
NotifyPropertyChanged("some_property_on_the_view_model");
}
else if (...)
{
... etc ...
}
}
This gets annoying because it just seems messy. And if I forget to edit this function after adding a new property to the view-model then it can easily lead to bugs.
So here is what I would like to do, but I don't know if this is possible. So I would like one of you to help me understand if it is possible or not.
It would be really cool if I could use C#'s "attributes" feature to take care of the property changed propagation.
So maybe something like this:
[ListenToModelProperty("some_property_on_the_model")]
[OnPropertyChanged("MyButtonVisibility")]
public Visibility MyButtonVisibility
{
get
{
if (model.some_property_on_the_model == true)
{
return Visibility.Visible;
}
else
{
return Visibility.Hidden;
}
}
}
private void OnModelPropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
{
prop_name = e.PropertyName;
foreach (var property in view_model)
{
var attributes = property.GetCustomAttributes(typeof(ListenToModelPropertyAttribute));
var descriptions = attributes.Select(x => x.Description);
if (descriptions.Contains(prop_name))
{
notification_to_make = property.GetCustomAttributes(typeof(OnPropertyChangedAttribute));
string notification_string = notification_to_make[0].Description;
NotifyPropertyChanged(notification_string);
}
}
}
Please note that the above code is not meant to be real code. It will definitely not compile and will not work. But I would like to see if something like the above is possible in C#. Is it possible to do something like this using attributes? Or is there a library out there that makes something like this possible?
I have figured out how to do it! It is fairly simple. I will post the relevant code here, and those who are interested can find all the code at this github repository that I just made: https://github.com/davepruitt/model-subscribe
First, I created a custom attribute class. It is a simple class that takes an array of strings as a parameter to its constructor. This allows you to listen to multiple properties on the model for changes. It looks like this:
using System;
using System.Collections.Generic;
namespace TestPropagationOfPropertyChanges
{
[AttributeUsage(AttributeTargets.All)]
public class ListenForModelPropertyChangedAttribute : System.Attribute
{
public List<string> ModelPropertyNames = new List<string>();
public ListenForModelPropertyChangedAttribute (string [] propertyNames)
{
ModelPropertyNames.AddRange (propertyNames);
}
}
}
I then created my model. For simplicity's sake, it only contains two properties. They are strings that store a "first name" and a "last name":
using System;
using System.ComponentModel;
namespace TestPropagationOfPropertyChanges
{
public class Model : NotifyPropertyChangedObject
{
#region Constructors
public Model ()
{
}
#endregion
#region Private data members
private string _first = string.Empty;
private string _last = string.Empty;
#endregion
#region Public properties
public string FirstName
{
get
{
return _first;
}
set
{
_first = value;
NotifyPropertyChanged ("FirstName");
}
}
public string LastName
{
get
{
return _last;
}
set
{
_last = value;
NotifyPropertyChanged ("LastName");
}
}
#endregion
}
}
The view-model, in this case, has a "full name" property. So it wants to listen to any changes that happen to the first or last name on the model, and then react to changes on either of those. I realize this isn't the best "real world" scenario in which this kind of system would be used, but it does help illustrate the concept. The first part of my view-model is below:
using System;
namespace TestPropagationOfPropertyChanges
{
public class ViewModel : NotifyPropertyChangedObject
{
#region Private data members
//This is public for testing purposes
public Model _model = new Model();
#endregion
#region Constructors
public ViewModel ()
{
_model.PropertyChanged += ReactToModelPropertyChanged;
}
#endregion
#region Properties
[ListenForModelPropertyChangedAttribute(new string [] {"FirstName", "LastName"})]
public string FullName
{
get
{
return _model.FirstName + _model.LastName;
}
}
Finally, the view-model finishes with the method that reacts to changes on the model. Normally, in a large and complex application, this method could contain a large if-else statement with lots of calls to NotifyPropertyChanged. Instead, we now just iterate through the properties of the view-model and see which ones subscribe to the model's property that was changed. See below:
void ReactToModelPropertyChanged (object sender, System.ComponentModel.PropertyChangedEventArgs e)
{
//Get the name of the property that was changed on the model
string model_property_changed = e.PropertyName;
//Get a System.Type object representing the current view-model object
System.Type t = typeof(ViewModel);
//Retrieve all property info for the view-model
var property_info = t.GetProperties();
//Iterate through each property
foreach (var property in property_info)
{
//Get the custom attributes defined for this property
var attributes = property.GetCustomAttributes (false);
foreach (var attribute in attributes)
{
//If the property is listening for changes on the model
var a = attribute as ListenForModelPropertyChangedAttribute;
if (a != null)
{
//If the property that was changed on the model matches the name
//that this view-model property is listening for...
if (a.ModelPropertyNames.Contains(model_property_changed))
{
//Notify the UI that the view-model property has been changed
NotifyPropertyChanged (property.Name);
}
}
}
}
}
Overall, it works excellently, and is exactly what I needed. This code can easily be expanded upon to be even more functional for those interested.
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);
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?