So I've got the following ComboBox with the SelectedValue bound to the Property below. With the following binding, when I set value, the binding/RaisePropertyChanged combination is throwing a StackOverflow Exception.
Here's the ComboBox
<ComboBox x:Name="WireType" ItemsSource="{x:Bind ViewModel.WireTypes}" SelectedValue="{x:Bind ViewModel.WireType, Mode=TwoWay}"/>
Here's the Property
public string WireType
{
get
{
return _wireType;
}
set
{
_wireType = value;
RaisePropertyChanged();
}
}
And here's the RaisePropertyChanged method.
private void RaisePropertyChanged([CallerMemberName] string caller = "")
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(caller));
}
}
I'm pretty sure I've done this before. What am I missing?
My psychic powers suggest that the PropertyChanged event is trying to set the property value.
The setter should protect against the case where the value didn't change. ie-
set
{
if (_wireType != value) // or the appropriate comparison for your specific case
{
_wireType = value;
RaisePropertyChanged();
}
}
Of course a stack trace would confirm what's actually happening.
Try this
public event PropertyChangedEventHandler PropertyChanged;
private void RaisePropertyChanged([CallerMemberName] string caller = "")
{
if (this.PropertyChanged != null)
this.PropertyChanged(this, new PropertyChangedEventArgs(caller));
}
Related
I have a GridControl(Devexpress v13) in view(WPF). A Datatable set values in ViewModel and assigned to ItemsSource. But ItemsSource filled only initialize. Later Datatable's value changes but it doesn't refresh.
How to ItemsSource refresh?
<dxg:GridControl Name="GridControlData" DataSource="{Binding DtCriterias, Mode=TwoWay}" HorizontalAlignment="Left" VerticalAlignment="Top" AutoGenerateColumns="AddNew" Width="400" Height="100">
I hope you know what I mean.
Any help will be much appreciated.
Thanks in advance.
Edit:
Property changed using:
public DataTable DtCriterias {
get { return _dtCriterias; }
set
{
_dtCriterias = value;
Notify(() => DtCriterias);
}
}
protected void Notify(Expression<Func<object>> expression)
{
if (_propertyChangedEvent == null) return;
Notify(GetPropertyName(expression));
}
protected void Notify(string propertyName)
{
if (_propertyChangedEvent != null)
{
_propertyChangedEvent(this, new PropertyChangedEventArgs(propertyName));
}
}
public ObservableCollection<ClientB2B> Clients
{
get
{
return _clients;
}
set
{
if (_clients == value) return;
_clients = value;
OnPropertyChanged(); // This is what you need
}
}
Implement this interface - INotifyPropertyChanged
protected void OnPropertyChanged(PropertyChangedEventArgs e)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
handler(this, e);
}
protected void OnPropertyChanged(string propertyName)
{
OnPropertyChanged(new PropertyChangedEventArgs(propertyName));
}
public event PropertyChangedEventHandler PropertyChanged;
I found reason of problem.
I am using encapsulation and I was set private value(_dtCriterias). Therefore Property Changed Event wasn't work.
Definitions:
private DataTable _dtCriterias;
public DataTable DtCriterias {
get { return _dtCriterias; }
set
{
_dtCriterias = value;
Notify(() => DtCriterias);
}
}
When I have problem datatable set:
_dtCriterias = GetValue().DefaultView.ToTable("FooTable");
Solution:
DtCriterias = GetValue().DefaultView.ToTable("FooTable");
What is the best way to bind a property to a control so that when the property value is changed, the control's bound property changes with it.
So if I have a property FirstName which I want to bind to a textbox's txtFirstName text value. So if I change FirstName to value "Stack" then the property txtFirstName.Text also changes to value "Stack".
I know this may sound a stupid question but I'll appreciate the help.
You must implement INotifyPropertyChanged And add binding to textbox.
I will provide C# code snippet. Hope it helps
class Sample : INotifyPropertyChanged
{
private string firstName;
public string FirstName
{
get { return firstName; }
set
{
firstName = value;
InvokePropertyChanged(new PropertyChangedEventArgs("FirstName"));
}
}
#region Implementation of INotifyPropertyChanged
public event PropertyChangedEventHandler PropertyChanged;
public void InvokePropertyChanged(PropertyChangedEventArgs e)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null) handler(this, e);
}
#endregion
}
Usage :
Sample sourceObject = new Sample();
textbox.DataBindings.Add("Text",sourceObject,"FirstName");
sourceObject.FirstName = "Stack";
A simplified version of the accepted answer that does NOT require you to type names of properties manually in every property setter like OnPropertyChanged("some-property-name"). Instead you just call OnPropertyChanged() without parameters:
You need .NET 4.5 minimum.
CallerMemberName is in the System.Runtime.CompilerServices namespace
public class Sample : INotifyPropertyChanged
{
private string _propString;
private int _propInt;
//======================================
// Actual implementation
//======================================
public event PropertyChangedEventHandler PropertyChanged;
[NotifyPropertyChangedInvocator]
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
var handler = PropertyChanged;
if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
}
//======================================
// END: actual implementation
//======================================
public string PropString
{
get { return _propString; }
set
{
// do not trigger change event if values are the same
if (Equals(value, _propString)) return;
_propString = value;
//===================
// Usage in the Source
//===================
OnPropertyChanged();
}
}
public int PropInt
{
get { return _propInt; }
set
{
// do not allow negative numbers, but always trigger a change event
_propInt = value < 0 ? 0 : value;
OnPropertyChanged();
}
}
}
Usage stays the same:
var source = new Sample();
textbox.DataBindings.Add("Text", source, "PropString");
source.PropString = "Some new string";
Hope this helps someone.
I have a login page in my application and on the page i have a textbox that shows the error in case of wrong username/password, etc. I am updating the error from view model, but the view is not changing automatically.
The viewmodel implements INotifyPropertyChanged interface.
Error definition.
string _error;
public string Error
{
get { return _error; }
set
{
_error = value;
NotifyPropertyChanged("Error");
}
}
INotify Event Handlers
public event PropertyChangedEventHandler PropertyChanged;
protected void NotifyPropertyChanged(String info)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(info));
}
}
update
_error = "Wrong username/password!";
xaml
<TextBlock Text="{Binding Error, UpdateSourceTrigger=PropertyChanged}"/>
What else am i missing?
set your error like this, you're just changing the private member that isn't being binded to:
Error="Wrong username/password!";
not _error
How can I use a dotted path as a property name of a PropertyChangedEventHandler?
public class Person
{
private int _age;
public int Age
{
get { return _age;}
set
{
_age = value;
OnPropertyChanged();
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propertyName = null)
{
var handler = PropertyChanged;
if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
}
}
public partial class MyControl : UserControl, INotifyPropertyChanged
{
public Person Person
{
get { return (Person)GetValue(PersonProperty); }
set { SetValue(PersonProperty, value); }
}
public static DependencyProperty PersonProperty =
DependencyProperty.Register("Person", typeof (Person), typeof (MyControl), null);
private void someMethod()
{
OnPropertyChanged("Person.Age");
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propertyName = null)
{
var handler = PropertyChanged;
if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
}
}
<TextBox Text="{Binding Person.Age, Mode=TwoWay}"/>
But OnPropertyChanged("Person.Age") cannot resolve the symbol.
Is it possible to use a dotted path as a propertyName of OnPropertyChanged()?
The Age setter, you should always call OnPropertyChanged("Age").
INotifyPropertyChanged isn't meant to be used for sub-properties. You also don't need it on a UserControl, since dependency properties already provide notification. Once you fix your OnPropertyChanged call in the Person class you should be fine.
You have a couple of options to fix the Person.Age setter:
Call OnPropertyChanged("Age") (and remove the = null in the OnPropertyChanged signature.
If you are targeting .NET 4.5 or later, the preferred solution is to change the Person.OnPropertyChanged signature to be OnPropertyChanged(string [CallerMemberName] propertyName = null). Calling OnPropertyChanged() from the Age setter will then fill set propertyName to Age. See the this blog post or the MSDN documentation for more details.
My PropertyChanged event is getting set properly when I watch the variable, but somewhere in code it gets reset to null and I have no idea how to figure out where this is happening.
Here is come code:
public event PropertyChangedEventHandler PropertyChanged;
//this is in the NotifyTaskCompletion class, as used from the blog
// http://msdn.microsoft.com/en-us/magazine/dn605875.aspx
private async Task WatchTaskAsync(Task task)
{
try
{
await task; //After this task, PropertyChanged gets set to a non-null method("Void OnPropertyChanged()")
}
catch
{
}
//PropertyChanged, which was set to a value after the task was run, and still not null during IdentifyCompleted getting set, is now null here
var propertyChanged = PropertyChanged;
if (propertyChanged == null)
return;
//other code
}
//My Watch variable of PropertyChanged is still good after this setter is run.
public NotifyTaskCompletion<GraphicCollection> IdentifyCompleted
{
get { return _IdentifyCompleted; }
set
{
_IdentifyCompleted = value;
// _IdentifyCompleted.PropertyChanged+= new PropertyChangedEventHandler(this, new PropertyChangedEventArgs("IdentifyCompleted"));
// NotifyPropertyChanged();
}
}
My main issue is that I cannot use a {set;get;} on PropertyChanged to attempt to identify WHERE it is getting set to null. So my main question, unless anyone sees something that is obviously wrong, is how would I go about finding out where it is getting set to null? Thank you for any assistance.
EDIT
As per the last posters suggestion, I set my code as follows:
private PropertyChangedEventHandler _propertyChanged;
public event PropertyChangedEventHandler PropertyChanged
{
add { _propertyChanged += value; }
remove { _propertyChanged -= value; }
}
And here is the issue.
//this is in my View Model. The ViewModel CONTAINS NotifyTaskCompletion<GraphicCollection> IdentifyCompleted which in turn implements INotifyPropertyChanged and has its own PropertyChanged that is not getting set
private void NotifyPropertyChanged([CallerMemberName] String propertyName = "")
{
if (PropertyChanged != null)
{
//This line sets the PopertyChanged in the view model AND in the NotifyTaskCompletion class somehow, but I don't know how it is setting it properly in the NotifyTaskCompletion class in my other project where this code works, When I step through this line in my code, it doesn't trigger
//the add{} of the PropertyChanged in NotifyTaskCompletion, but it DOES in my other project...
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
So with all that said, I can now see what line SHOULD be working, but I have no idea WHY it isn't working. Any other ideas? Thanks for the help so far.
You can write your own event accessors:
private PropertyChangedEventHandler propertyChanged;
public event PropertyChangedEventHandler PropertyChanged {
add { propertyChanged += value; }
remove { propertyChanged -= value; }
}
You can then set breakpoints.
Note that this is not thread-safe.