Can anyone tell me why PropertyChanged value comes null in INotifyPropertyChanged. I have a property and I change its value, but when it enters in NotifyPropertyChanged method the value of PropertyChanged is null. Following is my code :
public class WorkOrder : INotifyPropertyChanged
{
public string _orderDays="0 Days";
public string OrderDays
{
get { return _orderDays; }
set
{
if (_orderDays == value)
return;
_orderDays = value;
NotifyPropertyChanged("OrderDays");
}
}
public event PropertyChangedEventHandler PropertyChanged;
// This method is called by the Set accessor of each property.
// The CallerMemberName attribute that is applied to the optional propertyName
// parameter causes the property name of the caller to be substituted as an argument.
public void NotifyPropertyChanged([CallerMemberName]String propertyName =null )
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
this is the xaml where I bind the value
<Label x:Name="lblTicketDate" Text="{Binding OrderDays}"
Grid.Row="1" HorizontalOptions="CenterAndExpand"
VerticalOptions="EndAndExpand" />
but when it enters in NotifyPropertyChangd method the value of PropertyChanged is null
That just means that nothing has subscribed to the event yet. Beyond events, a delegate instances refers to one or more actions to execute when the delegate is invoked - a null reference is used to represent "0 actions".
If you use:
var order = new WorkOrder();
// Or whatever's useful instead of Console.WriteLine
order.PropertyChanged += (sender, args) => Console.WriteLine(e.PropertyName);
order.OrderDays = "Different";
then you'll see the event being raised.
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?
It is really weird, but UI is not updated when I call BindableBase.SetProperty():
private string person;
public string Person
{
get { return person; }
set
{
person = value;
SetProperty(ref this.person, value);//Not updating UI
//OnPropertyChanged("Person");//It works really nice
}
}
I am using Prism.Core.6.1.0\lib\net45\Prism.dll and its Version=6.1.0.0.
However, OnPropertyChanged(string propertyName) perfectly works:
public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChanged(string propertyName)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
What I am missing? Any help would be greatly appreciated!:)
That's because of the person = value; instruction. BindableBase.SetProperty checks for equality between the two parameters, and only raises the PropertyChanged event if they're not equal. So removing this line should help.
The way SetProperty works is by doing all the required update logic for you. It will check if the value has changed, then either return immediately or update the value (which it can do, as it's passed by ref) and then raise the property changed event.
You're doing half its job in advance, so it will always return immediately as it will find no difference between the person field and value.
Just change your property to:
public string Person
{
get { return person; }
set { SetProperty(ref person, value); }
}
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
XAML code:
<DatePicker Date="{Binding DateTimeOffsetTest}"></DatePicker>
DataContext is set to App.ViewModel (static instance of ViewModel - just for a test!)
ViewModel:
public class ViewModel : INotifyPropertyChanged
{
private DateTimeOffset _dateTimeOffsetTest = new DateTimeOffset(new DateTime(1980, 10, 10));
public DateTimeOffset DateTimeOffsetTest
{
get { return _dateTimeOffsetTest; }
set
{
_dateTimeOffsetTest = value;
//Was OnPropertyChanged(""); (Thanks to Will for fix)
OnPropertyChanged();
}
}
public event PropertyChangedEventHandler PropertyChanged;
[NotifyPropertyChangedInvocator]
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
var handler = PropertyChanged;
if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
}
}
For a test, i simply initialized ViewModel as static instance in App.
I added this code to the button's click event handler:
App.ViewModel.DateTimeOffsetTest = new DateTimeOffset(new DateTime(1988, 09, 11));;
Problem is that DatePicker behaves like it's OneTime binding. If I update the property in ViewModel, DatePicker won't update. I've tried setting the mode to OneWay and change the UpdateSourceTrigger but it doesn't work.
Does anyone have a solution?
(note, this answer applies to the original code of the question)
OnPropertyChanged("");
Yeah, nope. You're providing a name (a meaningless one--an empty string) to a property that doesn't exist.
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
In this case, propertyName will be supplied only if you OMIT the argument in the method call.
According to the reference docs at MSDN:
The Caller Info attributes don't make a parameter optional. Instead, they affect the default value that's passed in when the argument is omitted.
If you pass any value into the method, it will override the CallerMemberNameAttribute-assigned value.
In your property setters, do the following:
public DateTimeOffset DateTimeOffsetTest
{
get { return _dateTimeOffsetTest; }
set
{
_dateTimeOffsetTest = value;
OnPropertyChanged(); // LOOK HERE
}
}
The framework will provide the name of the caller to the method, as you have not provided one. Your bindings will now work.
Apparently, DatePicker's OneWay binding mode is bugged and changes are not reflected. You can "solve" the issue by setting the binding mode to TwoWay.
<DatePicker Date="{Binding DateTimeOffsetTest, Mode=TwoWay}">
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