So i have something along the lines of
private ObservableCollection<ViewModel> _internal;
public ObservableCollection<ViewModel> BoundInternal{get;set}; //this is Binded in the Itemssource like ItemSource={Binding BoundInternal}
Now In my code i do something like
BoundInternal=_internal, However the problem is the BoundInternal isn't trigger any collectionChanged event. I have to use the Add method. So I am wondering if there is a solution to this.
Here is what I suspect your code ought to look like like (although its not quite a match for what you currently doing):-
public class YourClassHoldingThisStuff : INotifyProperyChanged
{
private ObservableCollection<ViewModel> _internal;
public ObservableCollection<ViewModel> BoundInternal
{
get { return _internal; }
set
{
_internal = value;
NotifyPropertyChanged("BoundInternal");
};
}
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(string name)
{
if (PropertyChanged != null)
PropertyChanged(this, new ProperytChangedEventArgs(name));
}
}
In this case the _internal field becomes the source of the value of BoundInternal directly and you should only assign it via BoundInternal, (don't assign a value directly to _internal). When that occurs anything currently bound to it will be informed of the change.
If for some reason you really do need to maintain _internal as a separate reference from the backing field of BoundInternal then:-
public class YourClassHoldingThisStuff : INotifyProperyChanged
{
private ObservableCollection<ViewModel> _internal;
private ObservableCollection<ViewModel> _boundInternal;
public ObservableCollection<ViewModel> BoundInternal
{
get { return _boundInternal; }
set
{
_boundInternal = value;
NotifyPropertyChanged("BoundInternal");
};
}
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(string name)
{
if (PropertyChanged != null)
PropertyChanged(this, new ProperytChangedEventArgs(name));
}
}
Now at some point in your code when you do BoundInternal = _internal, anything bound to it will be informed of the change.
Every ItemsControl has a, Items property which has a Refresh() method that you can call, which will update your list.
MyList.Items.Refresh()
Related
I am relatively new to WPF and having a problem with data binding. I am binding a dependency property of a user control to a class property in my code behind. During intantiation of the class entity in my code behind the UI is sucessfully updated through INotifyPropertyChanged. However when subsequently changing the value in my code behind the OnPropertyChangedEventHandler fires, but the OnPropertyChanged method does no longer answer to this. Below the details. It would be great if someone could give me some hints what I am doing wrong.
I implemented a user control that I am binding to a property CurrentAccProp.DiscountRate of my partial class in code behind:
<local:doubleUEdit x:Name="InterestRate" LabelField="Interest rate" MinimumValue="0" MaximumValue="1" FormatStringForNumbers="P2" IncrementSize="0.01" UncertainValue="{Binding ElementName=RibbonWindow, Path=CurrentAccProp.DiscountRate, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
The class of which CurrentAccProp is an instance implements INotifyPropertyChanged to inform the UI about value changes
//Event to inform data grid about changes
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
OnPropertyChanged is called in the setter for the DiscountRate property:
doubleU discountingrate;
public doubleU DiscountRate
{
get {return discountingrate;}
set
{
discountingrate = value;
OnPropertyChanged("DiscountingRate");
}
}
The property of my user control that I am binding to is implemented as a dependency property:
//Property for data binding to doubleU
[Description("The formatstring for the double boxes"), Category("Default")]
public doubleU UncertainValue
{
get { return new doubleU(0, 0, (double)doubleUSupremum.Value, (double)doubleUSupremum.Value); }
set { doubleURangeSlider.LowerValue = value.Interval.Infimum; doubleURangeSlider.HigherValue = value.Interval.Supremum; doubleUInfimum.Value = value.Interval.Infimum; doubleUSupremum.Value = value.Interval.Supremum; }
}
public static readonly DependencyProperty UncertainValueProperty =
DependencyProperty.Register(
"UncertainValue",
typeof(doubleU),
typeof(doubleUEdit),
new PropertyMetadata(default(doubleU), OnItemsPropertyChanged));
private static void OnItemsPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
doubleUEdit MydblUEdt = d as doubleUEdit;
MydblUEdt.UncertainValue = e.NewValue as doubleU;
}
When I am instantiating CurrentAccProp in my code behind the OnPropertyChanged informs the UI and the value is updated.
AccountingProperties currentaccprop = new AccountingProperties(new doubleU(0.0));
public AccountingProperties CurrentAccProp { get { return currentaccprop; } set { currentaccprop = value; } }
However, when I later update the value of DiscountRate
CurrentAccProp.DiscountRate = new doubleU(1.0);
OnPropertyChanged gets executed, but the UI is no longer updated. Does anyone have a clue what I am doing wrong here?
The typo pointed out by HighCore and zaknotzach was indeed the problem. Thanks for your help! I implemented the approach in the thread referenced by HighCore to avoid this and it works like a charm. Below the changed AccountingProperties class from which CurrentAccProp is instantiated for reference:
public class AccountingProperties : INotifyPropertyChanged
{
doubleU discountrate;
public doubleU DiscountRate
{
get {return discountrate;}
set { SetField(ref discountrate, value, () => DiscountRate); }
}
//------------------------------------------------
//constructors
public AccountingProperties(doubleU discountrate)
{
DiscountRate = discountrate;
}
//Event to inform data grid about changes
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
}
protected virtual void OnPropertyChanged<T>(Expression<Func<T>> selectorExpression)
{
if (selectorExpression == null)
throw new ArgumentNullException("selectorExpression");
MemberExpression body = selectorExpression.Body as MemberExpression;
if (body == null)
throw new ArgumentException("The body must be a member expression");
OnPropertyChanged(body.Member.Name);
}
protected bool SetField<T>(ref T field, T value, Expression<Func<T>> selectorExpression)
{
if (EqualityComparer<T>.Default.Equals(field, value)) return false;
field = value;
OnPropertyChanged(selectorExpression);
return true;
}
}
You need to first change the string in
OnPropertyChanged("DiscountingRate");
to "DiscountRate". The string you are giving your OnPropertyChanged function must match the property name. That is most likely the issue you are having.
As already answered, the problem is OnPropertyChanged("DiscountingRate"); providing the event with an incorrect property name.
In order to prevent errors like this, you can avoid using string literals all together. In your OnPropertyChanged parameter, use CallerMemberName. You can modify your OnPropertyChanged signature to
public void OnPropertyChanged([CallerMemberName] string propertyName = "")
{
// Do your stuff
}
Then in your setters, you just call this.OnPropertyChanged();. The method will be given the property name that was changed.
public doubleU DiscountRate
{
get {return discountingrate;}
set
{
discountingrate = value;
OnPropertyChanged();
}
}
The benefit to this is that you can refactor your code and not worry about breaking your property changed events.
)
Please have a look at the code below:
public class MyClass : INotifyPropertyChanged
{
private string _myName;
public string MyName
{
get { return _myName; }
set
{
if (_myName != value)
{
_myName = value;
OnPropertyChanged("MyName");
}
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
Object of this class is used as a DataContext for textBox control on WPF form.
Idea is simple: changes done to MyName property of this object should be automatically displayed in textBox. It works but I do not understand why.
Please follow my way of thinking and point out where I get it wrong...
We set a new value for MyName. Program goes to 'setter' and it calls 'OnPropertyChanged'. It goes to 'OnPropertyChanged' and checks if 'PropertyChanged' is null.
And it is not. And I do not know why since I do not add any event handlers to this event.
So... how does it work?
Thank you!
The Event Handler is added automatically when you bind the object to the DataContext.
I use INotifyPropertyChanged to notify class when there is any change in a variable of a particular object within it.
Below is the class:
public class MyClass
{
public SecClass MyObj { get; set; }
//A few more variables
}
SecClass:
public class SecClass:INotifyPropertyChanged
{
private bool _noti= false;
public bool Noti
{
get { return _noti; }
set
{
_noti= value;
NotifyPropertyChanged("Noti");
}
}
//A few more variables
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(string name)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(name));
}
}
}
Here my function that makes the event registration:
public void Register()
{
MyObj.PropertyChanged += MyObj_PropertyChanged;
}
Function works and the registration is done, but when it comes to change it displays the Property Change as null (I guess that somewhere registration deleted, before happens change, how can I check this?)
I hooked this together with:
static class Program
{
static void Main()
{
var c = new MyClass();
c.MyObj = new SecClass();
c.Register();
c.MyObj.Noti = !c.MyObj.Noti;
}
}
adding (for illustration):
private void MyObj_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
Console.WriteLine(e.PropertyName);
}
to MyClass, and:
public event PropertyChangedEventHandler PropertyChanged;
to SecClass (to get them to compile), and it works fine - printing "Noti" at runtime. There is a theoretical thread-race, but it is very unlikely in any sane usage, but recommended usage is:
var handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(name));
}
Also, for info: if you add [CallerMemberName] to that, you don't need to specify the property explicitly:
private void NotifyPropertyChanged([CallerMemberName] string name = null) {...}
with:
NotifyPropertyChanged(); // the compiler adds the "Noti" itself
But fundamentally: "cannot reproduce" - it works fine. I wonder if maybe it relates to your PropertyChanged implementation, since you don't actually show that. In particular, I wonder if you actually have two events: one explicitly implemented. That would mean that it is getting treated differently by your cast.
So I have a class with 40 or so properties that are updated from communication with a micro controller. This class implements INotifyPropertyChanged.
Loose Example:
private int _Example;
public int Example
{
get
{
return _Example;
}
set
{
_Example = value;
OnPropertyChange("Example");
}
}
And the OnPropertyChange function:
protected void OnPropertyChange(string p_Property)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(p_Property));
}
}
public event PropertyChangedEventHandler PropertyChanged;
Binding (many of these)
Second_Class_Control.DataBindings.Clear();
Second_Class_Control.DataBindings.Add("My_Property", FirstClass, "Example");
In the main form I've set up binds to display and react to these values. One of those happens to land on another property in a another class. I happened to place a breakpoint in the set function of this property, and noticed it was being called any time any property from the first class changed.
Is this the correct behavior? I don't notice any performance hits but I plan on having many instances of these classes running together and wasn't expecting this.
Thanks
Hmm.. I noticed that you have the your OnPropertyChange virtual. Why is this, are you making a override somewhere?
I usually creates it like this :
public class ViewModelBase : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string propertyName)
{
if (this.PropertyChanged != null)
{
this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
then for the usage :
public class MainWindowViewModel : ViewModelBase
{
private string name;
public string Name
{
get { return name; }
set { name = value; OnPropertyChanged("Name"); }
}
}
I have created WPF MVVM application, and set WPFToolkit DataGrid binding to DataTable so I want to know how to implement DataTable property to notify changed. Currently my code is like below.
public DataTable Test
{
get { return this.testTable; }
set
{
...
...
base.OnPropertyChanged("Test");
}
}
public void X()
{
this.Test.Add(...); // I suppose this line will call to getter first (this.Test = get Test) and then it will call add letter, this mean that setter scope will never fire.
base.OnPropertyChanged("Test"); // my solution is here :) but I hope it has better ways.
}
Is it has another solution for this problem?
There are 2 ways your Table data could change: Either an element could be added/removed from the collection, or some properties from within an element could change.
The first scenario is easy to handle: make your collection an ObservableCollection<T>. Invoking .Add(T item) or .Remove(item) on your table will fire a change notification through to the View for you (and the table will update accordingly)
The second scenario is where you need your T object to implement INotifyPropertyChanged...
Ultimately your code should look something like this:
public class MyViewModel
{
public ObservableCollection<MyObject> MyData { get; set; }
}
public class MyObject : INotifyPropertyChanged
{
public MyObject()
{
}
private string _status;
public string Status
{
get { return _status; }
set
{
if (_status != value)
{
_status = value;
RaisePropertyChanged("Status"); // Pass the name of the changed Property here
}
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void RaisePropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = this.PropertyChanged;
if (handler != null)
{
var e = new PropertyChangedEventArgs(propertyName);
handler(this, e);
}
}
}
Now set the datacontext of your View to be an instance of your ViewModel, and bind to the collection, like:
<tk:DataGrid
ItemsSource="{Binding Path=MyData}"
... />
Hope this helps :)
Ian