I have a ComboBox bound to a ViewModel string Quality_SelectedItem.
And I have a Method named Quality, which inside accesses the value of the SelectedItem in an if statement.
I have two ways of accessing the value, by passing the ViewModel through the Method, or by passing the string Quality_SelectedItem.
Which way should I be using it and which performs faster?
XAML
<ComboBox x:Name="cboQuality"
ItemsSource="{Binding Quality_Items}"
SelectedItem="{Binding Quality_SelectedItem, Mode=TwoWay}"
HorizontalAlignment="Left"
Margin="0,2,0,0"
VerticalAlignment="Top"
Width="105"
Height="22"/>
ViewModel Class
public class ViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged = delegate { };
private void OnPropertyChanged(string prop)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(prop));
}
}
// Quality Selected Item
private string _Quality_SelectedItem { get; set; }
public string Quality_SelectedItem
{
get { return _Quality_SelectedItem; }
set
{
if (_Quality_SelectedItem == value)
{
return;
}
_Quality_SelectedItem = value;
OnPropertyChanged("Quality_SelectedItem");
}
}
...
Example 1 - Passing ViewModel
In the Quality Method, I access vm.Quality_SelectedItem directly from the if statement.
public ViewModel vm = new ViewModel();
// MainWindow
public MainWindow()
{
InitializeComponent();
DataContext = vm;
// Quality Method
Quality(vm); // <---
}
// Quality Method
public static void Quality(ViewModel vm)
{
if (vm.Quality_SelectedItem == "High")
{
// do something
}
else if (vm.Quality_SelectedItem == "Low")
{
// do something
}
}
Example 2 - Passing String SelectedItem
I pass vm.Quality_SelectedItem through the Quality Method and give it the string name quality.
public ViewModel vm = new ViewModel();
// MainWindow
public MainWindow()
{
InitializeComponent();
DataContext = vm;
// Quality Method
Quality(vm.Quality_SelectedItem); // <---
}
// Quality Method
public static void Quality(string quality)
{
if (quality == "High")
{
// do something
}
else if (quality == "Low")
{
// do something
}
}
As a general rule, you should make your code as simple as possible. Remember the KISS principle. This also plays well with SOLID ("simple" is a good way to achieve Single responsibility and Interface segregation).
Avoid reaching into one object to get another.
If you need only a string value in the method, only pass that string value. Don't force your method to dig into object hierarchies and dependencies to get that value.
If the method needs to modify a string property value, then pass the object where to modify the property.
From the performance point of view, you will not notice any change. Accessing an object by-reference is a very cheap operation. (Unless you're implementing loops with billions of iterations.)
From the design point of view, keeping the things simple makes your code SOLID and easily allows re-usage.
It depends on what is //do something.
If you have to handle/interact your viewmodel-object, then pass
viewmodel as parameter
otherwise use a string for less dependencies and possibility of common use.
If you have your viewmodel as singleton, then it doesn't matter which way you go.
Related
I know, title is a little confusing so let me explain. I have a user control that has a dependency property. I access this dependency property with a regular property called Input. In my view model I also have a property called Input. I have these two properties bound together in XAML using two-way binding as shown below:
<uc:rdtDisplay x:Name="rdtDisplay" Input="{Binding Input, Mode=TwoWay}" Line1="{Binding myRdt.Line1}" Line2="{Binding myRdt.Line2}" Height="175" Width="99" Canvas.Left="627" Canvas.Top="10"/>
Okay in my view model, I call a method whenever the value of Input is changed as shown in my property:
public string Input
{
get
{
return input;
}
set
{
input = value;
InputChanged();
}
}
The problem with this is that when I set the value of Input in my view model it only updates the value of the variable input as per my setter in my property. How can I get this to update back to the dependency property in the user control? If I leave the code input = value; out then I get a compilation error.
I need something like this:
public string Input
{
get
{
return UserControl.Input;
}
set
{
UserControl.Input = value;
InputChanged();
}
}
If I make the Input property in my view model look like this:
public string Input
{
get; set;
}
then it works, however, I am unable to call the InputChanged() method that I need to call when the Property is changed. All suggestions are appreciated.
Implement INotifyPropertyChanged in your ViewModel
public class Sample : INotifyPropertyChanged
{
private string input = string.Empty;
public string Input
{
get
{
return input;
}
set
{
input = value;
NotifyPropertyChanged("Input");
InputChanged();
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(String info)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(info));
}
}
}
In your case, you can do it in the code behind of your usercontrol
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'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.
I am creating an application that uses several threads as a result I want to try to use UIControls in my code behind as few as possible. The way I do it is by binding the controls to a property in my code behind that way I will be able to update the control by changing that property it does not matter if that property is updated on a different thread. Anyways I am creating the following code in order for the class to create the bindings form me.
public static class MyExtensionMethods
{
public static TextBoxBind<T> BindTextBox<T>(this TextBox textbox, string property=null)
{
return new TextBoxBind<T>(textbox,property);
}
}
public class TextBoxBind<T> : INotifyPropertyChanged
{
string property;
protected T _Value;
public T Value
{
get { return _Value; }
set { _Value = value; OnPropertyChanged(property); }
}
public event PropertyChangedEventHandler PropertyChanged = delegate { };
protected void OnPropertyChanged(string propertyName){
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
public TextBoxBind(TextBox textbox, string property)
{
if (property == null)
{
property = "Value";
}
this.property = property;
Binding b = new Binding(property)
{
Source = this
};
b.UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged;
textbox.SetBinding(TextBox.TextProperty, b);
}
}
And on my XAML I have:
<TextBox Name="textBox2" />
Therefore I will be able to use the first code that I posted as:
var newTextBox2 = textBox2.BindTextBox<int>();
newTextBox2.Value = 50; // this will update the textBox2.Text = "2"
// also every time I update the value of textBox2 newTextBox2.Value will update as well
The problem is when I try to bind it to a custom object. Take this code for example:
public class Person
{
public string Name { get; set; }
public string Age { get; set; }
public override string ToString()
{
return Age.ToString();
}
}
void LogIn_Loaded(object sender, RoutedEventArgs e)
{
txtUsuario.Focus();
var newTextBox2 = textBox2.BindTextBox<Person>("Age");
// here newTextBox2 never updates....
}
When it comes to data binding one should update an object (doesn't matter CLR property or DependencyObject) from the same thread, as the UI is running at. If you have a UI element bound to something in code, updating that from a separate thread will lead to exception. However, you can always retrieve your UI thread and perform property update there.
Here's a piece of code, that I am using in a similar situation as you have:
ThreadStart updateLogs = delegate()
{
ObservableCollection<LogMessage> newLogs = this._parcer.Parce();
foreach (LogMessage log in newLogs)
LogMessages.Add(log);
};
App.Current.Dispatcher.BeginInvoke(updateLogs, null);
This block of code is running in a thread different to one UI is running at. So I extract the code, that actually updates the binding source (which is LogMessages) into a delegate updateLogs and then run this delegate in a UI thread, passing it to the application dispatcher.
Nevertheless, WPF application can have more than one Dispather if, for example, you create separate windows in separate threads, although this approach is rare. But just in case, DependencyObject class has a Dispatcher property, which references the Dispather that owns this object.
OnPropertyChanged(property); should be pointing to Value, since that's the Name of your Property.
This should not be pointing to the type T.
So this code is not right:
if (property == null)
{
property = "Value";
}
because property should always be "Value"
public T Value
{
get { return _Value; }
set { _Value = value; OnPropertyChanged("Value"); }
}