I have following class which I use for radio button binding
public class RadioButtonSwitch : ViewModelBase
{
IDictionary<string, bool> _options;
public RadioButtonSwitch(IDictionary<string, bool> options)
{
this._options = options;
}
public bool this[string a]
{
get
{
return _options[a];
}
set
{
if (value)
{
var other = _options.Where(p => p.Key != a).Select(p => p.Key).ToArray();
foreach (string key in other)
_options[key] = false;
_options[a] = true;
RaisePropertyChanged("XXXX");
else
_options[a] = false;
}
}
}
XAML
<RadioButton Content="Day" IsChecked="{Binding RadioSwitch[radio1], Mode=TwoWay}" GroupName="Monthly" HorizontalAlignment="Left" VerticalAlignment="Center" />
ViewModel
RadioSwitch = new RadioButtonSwitch(
new Dictionary<string, bool> {{"radio1", true},{"radio2", false}}
);
I'm having problem with RaisePropertyChanged() in my Class. I'm not sure what value I should be putting in order to raise the change.
I tried putting:
Item[]
a
[a]
I keep getting following error:
This is so in case of any change I can handle it in my view accordingly. Please do not give me solutions for List for radio buttons etc.
The problem is that you are implementing an indexer, not an ordinary property. Although the binding subsystem supports indexers, MVVMLight and INotifyPropertyChanged do not.
If you want to use an indexer you need to:
Use a collection base class such as ObservableCollection<T>
Implement INotifiyCollectionChanged and raise that event instead
The first option isn't realistic because you are already deriving from ViewModelBase and have to continue to do that. Since implementing INotifiyCollectionChanged is a little bit of work, the easiest approach is to:
add a property to RadioButtonSwitch that is an observable collection of boolean values (ObservableCollection<bool>)
Then change you binding to add one more path element and you are done.
Edit:
Based on your comment and rereading your question, I think implementing INotifyCollectionChanged is the easiest. Here is the rewrite of your RadioButtonSwitch class which actually no longer needs to derive from the MVVMLight base class, although you still could if you wanted to.
The careful reader will notice that we use a sledgehammer and "reset" the whole collection when any element of the collection is modified. This is not just laziness; it is because the indexer uses a string index instead of an integer index and INotifyCollectionChanged doesn't support that. As a result, when anything changes we just throw up our hands and say the whole collection has changed.
public class RadioButtonSwitch : INotifyCollectionChanged
{
public event NotifyCollectionChangedEventHandler CollectionChanged;
protected void RaiseCollectionChanged()
{
if (CollectionChanged != null)
CollectionChanged(this, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
}
IDictionary<string, bool> _options;
public RadioButtonSwitch(IDictionary<string, bool> options)
{
this._options = options;
}
public bool this[string a]
{
get
{
return _options[a];
}
set
{
if (value)
{
var other = _options.Where(p => p.Key != a).Select(p => p.Key).ToArray();
foreach (string key in other)
_options[key] = false;
_options[a] = true;
RaiseCollectionChanged();
}
else
_options[a] = false;
}
}
}
GalaSoft.MvvmLight has the following code to check property name before raising PropertyChanged event.
public void VerifyPropertyName(string propertyName)
{
if (GetType().GetProperty(propertyName) == null)
throw new ArgumentException("Property not found", propertyName);
}
GetType().GetProperty("Item[]") obviously returns null.
This is why it is failing.
I think, the quickest workaround for you would be not to use ViewModelBase from this library, but implement your own version, that doesn't do this check:
public class ViewModelBase : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void RaisePropertyChanged(string propertyName)
{
var handler = PropertyChanged;
if (handler != null)
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
If you implement this class you will be able to run RaisePropertyChanged("Item[]").
Related
I am trying to figure out how to update my bool properties inside a ViewModel using
INotifyPropertyChanged?
Basically in my ViewModel I pass in a List of string. Each boolean properties check the list to see if a
string value exists.
Now in my software lifecycle the list will get updated and inturn I would like to update each properties
using INotifyPropertyChanged.
My question is how do I invoke the INotifyPropertyChanged from a AddToList method? Is using a method for this the
correct direction?
public class ViewModel : INotifyPropertyChanged
{
private List<string> _listOfStrings;
public ViewModel(List<string> ListOfStrings)
{
_listOfStrings = ListOfStrings;
}
public bool EnableProperty1 => _listOfStrings.Any(x => x == "Test1");
public bool EnableProperty2 => _listOfStrings.Any(x => x == "Test2");
public bool EnableProperty3 => _listOfStrings.Any(x => x == "Test3");
public bool EnableProperty4 => _listOfStrings.Any(x => x == "Test4");
public void AddToList(string value)
{
_listOfStrings.Add(financialProductType);
// Should I call the OnPropertyChanged here
}
public event PropertyChangedEventHandler PropertyChanged;
[NotifyPropertyChangedInvocator]
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
The easiest thing to do here would be to manually call OnPropertyChanged in the AddString method.
public void AddToList(string value)
{
_listOfStrings.Add(financialProductType);
OnPropertyChanged("EnableProperty1");
OnPropertyChanged("EnableProperty2");
// etc
}
This is fine if you're not likely to change the class much. If you add another property that's calculated from _listOfStrings you'll need to add a OnPropertyChanged call here.
Using an ObservableCollection doesn't really help because you already know when the list changes (AddToList) and you'll still have to trigger all the OnPropertyChanged methods anyway.
As far as I can see, there are 2 things you are missing in your implementation:
You should use ObservableCollection instead of List. As the name suggest, the former one can be observed (notify about its changing) by the view.
You need to bind a control to the public ObservableCollection and call OnPropertyChanged every time you assign/change value of the collection. something like this:
private ObservableCollection<string> _myList;
// your control should bind to this property
public ObservableCollection<string> MyList
{
get => return _myList;
set
{
// assign a new value to the list
_myList = value;
// notify view about the change
OnPropertiyChanged(nameof(MyList));
}
}
// some logic in your view model
string newValue = "newValue";
_myList.Add(newValue );
OnPropertyCHanged(nameof(MyList));
Hope this helps?
I was experimenting with Data Binding in Windows Forms and found a glitch that I can't explain. I post the question here in hopes that someone in the community can come up with an answer that makes sense.
I tried to come up with a clever way of binding read-only values that depend on operations on other values, and update it automatically when the dependent values change.
I created a form with 3 textboxes, where I want the sum of the first 2 to appear in the 3rd textbox.
The following code should work, but doesn't, at least not properly:
public class Model : INotifyPropertyChanged
{
private int m_valueA;
private int m_valueB;
public int ValueA
{
get { return m_valueA; }
set { m_valueA = value; RaisePropertyChanged("ValueA"); }
}
public int ValueB
{
get { return m_valueB; }
set { m_valueB = value; RaisePropertyChanged("ValueB"); }
}
public event PropertyChangedEventHandler PropertyChanged;
private void RaisePropertyChanged(string propertyName)
{
var handler = PropertyChanged;
if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
}
}
public class DynamicBindingProperty<T> : INotifyPropertyChanged
{
private Func<T> m_function;
private HashSet<string> m_properties;
public DynamicBindingProperty(Func<T> function, INotifyPropertyChanged container, IEnumerable<string> properties)
{
m_function = function;
m_properties = new HashSet<string>(properties);
container.PropertyChanged += DynamicBindingProperty_PropertyChanged;
}
public T Property { get { return m_function(); } }
void DynamicBindingProperty_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
if (!m_properties.Contains(e.PropertyName)) return;
if (PropertyChanged == null) return;
PropertyChanged(this, new PropertyChangedEventArgs("Property"));
}
public event PropertyChangedEventHandler PropertyChanged;
}
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
InitializeDataBinding();
}
private void InitializeDataBinding()
{
Model model = new Model();
DynamicBindingProperty<int> tmp = new DynamicBindingProperty<int>(() => model.ValueA + model.ValueB, model, new[] {"ValueA", "ValueB"});
textBox1.DataBindings.Add("Text", model, "ValueA");
textBox2.DataBindings.Add("Text", model, "ValueB");
textBox3.DataBindings.Add("Text", tmp, "Property");
tmp.PropertyChanged += (sender, args) => Console.WriteLine(args.PropertyName);
}
}
After experimenting for a while, I tried renaming DynamicBindingProperty<T>.Property to something else (e.g. DynamicProperty), and everything worked as expected!. Now, I was expecting something to break by renaming Model.ValueA to Property, but it didn't, and still worked flawlessly.
What is going on here?
I did some debugging and it looks like a bug (or requirement "the property must not be named Property" I am not aware of). If you replace
PropertyChanged(this, new PropertyChangedEventArgs("Property"));
with
PropertyChanged(this, new PropertyChangedEventArgs(null));
it still does not work - null or an empty string means any property may have changed. This indicates that problem is not in the handling of the change notification but that the binding has not been correctly established.
If you add a second property Property2 to DynamicBindingProperty<T> that does the same as Property and bind it to a fourth text box, then both text boxes will get update correctly if you perform a change notification with an empty string, null or "Property2". If you perform the change notification with "Property" both text boxes will not get update correctly. This indicates that the binding to Property is not completely broken and also that the change notification is somewhat broken.
Sadly I was unable to pin down the exact location where things go wrong, but if you invest enough time stepping through optimized framework source code you can probably figure it out. The earliest difference between the case with property name Property and the case with property name Property2 I could identify when processing a change notification was in OnValueChanged() in the internal class System.ComponentModel.ReflectPropertyDescriptor. In one case the base implementation gets called while it gets skipped in the other case - at least if the debugger didn't trick me, but this is hard to tell in optimized code.
Given a standard view model implementation, when a property changes, is there any way to determine the originator of the change? In other words, in the following view model, I would like the "sender" argument of the "PropertyChanged" event to be the actual object that called the Prop1 setter:
public class ViewModel : INotifyPropertyChanged
{
public double Prop1
{
get { return _prop1; }
set
{
if (_prop1 == value)
return;
_prop1 = value;
// here, can I determine the sender?
RaisePropertyChanged(propertyName: "Prop1", sender: this);
}
}
private double _prop1;
// TODO implement INotifyPropertyChanged
}
Alternatively, is it possible to apply CallerMemberNameAttribute to a property setter?
If I understood correctly, you're asking about the caller of the setter. That means, the previous method call in the call stack before getting to the setter itself (which is a method too).
Use StackTrace.GetFrames method for this. For example (taken from http://www.csharp-examples.net/reflection-callstack/):
using System.Diagnostics;
[STAThread]
public static void Main()
{
StackTrace stackTrace = new StackTrace(); // get call stack
StackFrame[] stackFrames = stackTrace.GetFrames(); // get method calls (frames)
// write call stack method names
foreach (StackFrame stackFrame in stackFrames)
{
Console.WriteLine(stackFrame.GetMethod().Name); // write method name
}
}
The output:
Main
nExecuteAssembly
ExecuteAssembly
RunUsersAssembly
ThreadStart_Context
Run
ThreadStart
Basically, what you're asking for would be stackFrames[1].GetMethod().Name.
My first approach to your problem would be to derive from PropertyEventArgs. The new class would have a member called, for instance PropertyChangeOrigin in addition to PropertyName. When you invoke the RaisePropertyChanged, you supply an instance of the new class with the PropertyChangeOrigin set from the information gleaned from the CallerMemberName attribute. Now, when you subscribe to the event, the subscriber could try casting the eventargs to your new class and use the information if the cast is successful.
This is what I always use as a middle-ground between INotifyPropertyChanged and my View Models:
public class NotifyOnPropertyChanged : INotifyPropertyChanged
{
private IDictionary<string, PropertyChangedEventArgs> _arguments;
public event PropertyChangedEventHandler PropertyChanged = delegate { };
public void OnPropertyChanged([CallerMemberName] string property = "")
{
if(_arguments == null)
{
_arguments = new Dictionary<string, PropertyChangedEventArgs>();
}
if(!_arguments.ContainsKey(property))
{
_arguments.Add(property, new PropertyChangedEventArgs(property));
}
PropertyChanged(this, _arguments[property]);
}
}
Two things here. It uses the [CallerMemberName] attribute to set the property name. This makes the usage syntax as follows:
public string Words
{
set
{
if(value != _words)
{
_words = value;
OnPropertyChanged( );
}
}
}
Beyond that, it stores the PropertyChangedEventArgs object in a dictionary so it's not created a ton of times for properties that are frequently set. I believe this addresses your problem. Good luck!
Whenever I have had to pass in extra information down into a VM I have a great success with using commands:
Commands, RelayCommands and EventToCommand
I want to be notified when a property changes so that I can log the oldvalue and new value of the property in database.
So I decided to go with the approach of property setter and have a generic method that handles all properties.
I created below class:
public class PropertyChangedExtendedEventArgs<T> : PropertyChangedEventArgs
{
public virtual T OldValue { get; private set; }
public virtual T NewValue { get; private set; }
public PropertyChangedExtendedEventArgs(string propertyName,
T oldValue, T newValue)
: base(propertyName)
{
OldValue = oldValue;
NewValue = newValue;
//write to database the values!!!
}
}
and on my property I call it as such:
private string _surname;
public string Surname
{
get { return _surname; }
set
{
string temp = Surname;
_surname = value;
Helper.PropertyChangedExtendedEventArgs("Surname", temp, value);
}
}
but it is first time working with generics so got few concerns :
how do I call this on my property?
is this a good approach?
would I be able to call a function in public
PropertyChangedExtendedEventArgs(string propertyName, T oldValue, T newValue) and save to database?
You seem to have got a bit of confused in property change usage.
Typically, components that wish to be observable about their property changes INotifyPropertyChanged interface. So as such correct implementation would be something like
private string _surname;
public string Surname
{
get { return _surname; }
set
{
if (_surname != value) // IMP: you want to inform only if value changes
{
string temp = Surname;
_surname = value;
// raise property change event,
NotifyPropertyChanged(temp, _surname);
}
}
}
Typically, base implementation could provide helper implementation to raise the event - for example,
public abstract Component : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected void NotifyPropertyChanged<T>(T oldVal, T newVal, [CallerMemberName] String propertyName = "")
{
var e = PropertyChanged;
if (e != null)
{
e(this, new PropertyChangedExtendedEventArgs(propertyName, oldVal, newVal));
}
}
}
Now, its consumer's responsibility on how to react to property changes. This separates observable components from unrelated concern of what to do when some property changes. Typically, one will have some the common implementation that would say - save the current object state in stacked manner as to provide undo-redo functionality.
So in your case, you wish to log them to database (?), there should be code that would listen to this property change events and does the logging. There will be some controller/binding code that would iterate through all objects implementing this interface and hook up the event. Typically, the root level container does such house keeping - for example, in a designer surface, its the root element (or code that is handling root element) would hook up event whenever a new component is created and added to the design surface.
I'm building a WPF application and I'm slowly uncovering some of the joys and also the frustrations of using WPF. My latest question involves updating the UI using INotifyPropertyChanged
My app has stacked UserControls with each UserControl containing multiple controls, so overall there are hundreds of controls which update every second providing live data. In order to update all controls I'm using something similar to below which does currently work as intended.
namespace ProjectXAML
{
public partial class ProjectX : UserControl, INotifyPropertyChanged
{
#region Declare Getter/Setter with INotifyPropertyChanged groupx3
private string m_group1Text1;
public string group1Text1
{
get
{
return m_group1Text1;
}
set
{
m_group1Text1 = value;
NotifyPropertyChanged("group1Text1");
}
}
private string m_group1Text2;
public string group1Text2
{
get
{
return m_group1Text2;
}
set
{
m_group1Text2 = value;
NotifyPropertyChanged("group1Text2");
}
}
private string m_group2Text1;
public string group2Text1
{
get
{
return m_group2Text1;
}
set
{
m_group2Text1 = value;
NotifyPropertyChanged("group2Text1");
}
}
private string m_group2Text2;
public string group2Text2
{
get
{
return m_group2Text2;
}
set
{
m_group2Text2 = value;
NotifyPropertyChanged("group2Text2");
}
}
private string m_group3Text1;
public string group3Text1
{
get
{
return m_group3Text1;
}
set
{
m_group3Text1 = value;
NotifyPropertyChanged("group3Text1");
}
}
private string m_group3Text2;
public string group3Text2
{
get
{
return m_group3Text2;
}
set
{
m_group3Text2 = value;
NotifyPropertyChanged("group3Text2");
}
}
#endregion
#region INotifyPropertyChanged Members
public event PropertyChangedEventHandler PropertyChanged;
/// Notifies the property changed.
private void NotifyPropertyChanged(string property)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(property));
}
}
#endregion
}
}
My questions are:
Is there a more elegant way to raise PropertyChanged events for lots of controls rather than lots of get/set code?
Is there a way to raise 1 PropertyChanged event covering the whole UserControl containing multiple controls instead of a separate event for every control? Is there a better method than what I'm attempting?
In strict reference to this part of your question..."Is there a way to raise 1 PropertyChanged event covering the whole UserControl containing ".
Yes, you can raise a PropertyChanged notification which says all my properties on my object are updated.
Use:
NotifyPropertyChanged(null);
then this informs the listener of INotifyPropertyChanged that all properties have changed on an object.
This isn't normally used...and can be abused....and cause inefficient updates e.g. if you were only changing a few properties and used that.
But you could argue the case for using it if you have lots of properties in your object, that you were always changing anyway at the same time...and you wanted to collapse lots of individual notifications into 1 that was raised after you had modified all properties.
Example use case (i.e. presumes you are updating all your groups in some way):
void UpdateAllGroupTextProperties()
{
group1Text1 = "groupA";
group1Text2 = "groupA2";
group2Text1 = "groupB";
group2Text2 = "groupB2";
group3Text1 = "groupC";
group3Text2 = "groupC2";
NotifyPropertyChanged(null);
}
For point 1 if you are using VS 2012 you can do the below
private void SetProperty<T>(ref T field, T value, [CallerMemberName] string name = "")
{
if (!EqualityComparer<T>.Default.Equals(field, value))
{
field = value;
var handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(name));
}
}
}
and then you can use your set property method without having to hard code the name of the properties.
Note the above code is an except of the below link
http://danrigby.com/2012/03/01/inotifypropertychanged-the-net-4-5-way/
Use the design pattern model view controler. So the model will raise the changes for you. Together with MVVM the controls will see with its dependency objects the changes and view them automatically.