TextBox leave causes PropertyChanged get fired twice - c#

When the Text property of a TextBox is bound to an object property which that object implements INotifyPropertyChanged, the event PropertyChanged may fire two times while having the same value:
1) when the text is changed inside the TextBox 2) when the control is leaving from it.
Consider these methods of a form:
private void Form1_Load(object sender, EventArgs e)
{
TextBox textBox = new TextBox();
TextBox secondTextBox = new TextBox();
secondTextBox.Location = new Point(0, 100);
this.Controls.Add(textBox);
this.Controls.Add(secondTextBox);
MyClass instance = new MyClass();
instance.PropertyChanged += instance_PropertyChanged;
textBox.DataBindings.Add("Text", instance, "Id", true, DataSourceUpdateMode.OnPropertyChanged);
}
private void instance_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
Console.WriteLine(e.PropertyName + " changed");
}
and the back-end class:
private class MyClass : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
int _id;
public int Id
{
get
{
return _id;
}
set
{
_id = value;
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs("Id"));
}
}
}
To reproduce the problem, type something in the upper textbox, check the console, and then enter the lower textbox and check again the console. Upon leaving, a property change is reported. Why?

The default value of Binding.DataSourceUpdateMode property is OnValidation. In this configuration the data source is being only updated when Validating event occurs. In your example you use OnPropertyChanged mode, so you additionally request update of the data source whenever the text is changed inside the TextBox.
It is the default behaviour i.e. the Binding class was implemented in this way. If you want more details, you can examine Binding.Target_PropertyChanged and Binding.Target_Validate methods with a reflector.
From my perspective this behaviour isn't a problem but you need to change the implementation of the setter in the following way:
set
{
if(_id != value)
{
_id = value;
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs("Id"));
}
}
Even if we assume that the implementation of Binding class is wrong, I think that it is a good practise to check whether a value has changed before generating PropertyChanged event.

Based on Michal's answer, I found the solution in switching off the CausesValidation property of TextBox as:
textBox.CausesValidation = false;

Related

C# WPF MVVM Changes in TextBox is not reflected in ListBox [duplicate]

What is the purpose of INotifyPropertyChanged. I know this event is fired whenever a property is changed but how can the View/UI knows that this event is fired:
Here is my Customer class that implements the INotifyPropertyChanged event:
public class Customer : INotifyPropertyChanged
{
private string _firstName;
public string LastName { get; set; }
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string propertyName)
{
if(PropertyChanged != null)
PropertyChanged(this,new PropertyChangedEventArgs(propertyName));
}
public string FirstName
{
get { return _firstName; }
set
{
_firstName = value;
OnPropertyChanged("FirstName");
}
}
}
But now how to notify the UI that property has changed. Like when the user assigns null or empty to the first name how can I display a MessageBox on the UI.
INotifyPropertyChanged allows WPF UI elements (via the standard data binding mechanisms) to subscribe to the PropertyChanged event and automatically update themselves. For example, if you had a TextBlock displaying your FirstName property, by using INotifyPropertyChanged, you can display it on a form, and it will automatically stay up to date when the FirstName property is changed in code.
The View just subscribes to the event - which tells it everything necessary. The event includes the name of the changed property, so if a UI element is bound to that property, it updates.
WPF can know because it can inspect whether an object implements this interface, then cast the object to said interface and register for the event. It can then trigger the Binding Infrastructure to update the display. If you want to react as well, you can register yourself for the same event.
EDIT: I reread your question and some of your comments. Here is a possible solution utilizing the DataContextChanged event and the INotifyPropertyChanged interface on your Customer object. You should also look into Data Binding Validation in WPF and .Net 3.5.
<TextBox Text="{Binding FirstName}" />
// assuming:
// myWindow.DataContext = new Customer();
myWindow.DataContextChanged += MyWindow_DataContextChanged;
private void MyWindow_DataContextChanged(object sender,
DependencyPropertyChangedEventArgs e)
{
var oldCustomer = e.OldValue as Customer;
if (oldCustomer != null)
{
oldCustomer.PropertyChanged -= Customer_CheckProps;
}
var newCustomer = e.NewValue as Customer;
if (newCustomer != null)
{
newCustomer.PropertyChanged += Customer_CheckProps;
}
}
private void Customer_CheckProps(object sender, PropertyChangedEventArgs e)
{
var customer = sender as Customer;
if (customer != null)
{
if (e.PropertyName == "FirstName"
&& String.IsNullOrEmpty(customer.FirstName))
{
// Display Message Box
}
}
}

INotifyPropertyChanged - Changing PropertyName Causes No Effect

I'm trying to bind my Winforms UI to my ViewModel. I was able to successfully update my ViewModel on UI changes and vice versa. However, I can't seem to understand what is the use of "PropertyName" used in PropertyChangedEventHandler since whatever I put there, it will always work. I don't know if I've already mixed things up since I've read a lot of articles about architectural patterns (MVP,MVC,MVVM,and MVP-VM (which is the one I was trying to do now) ).
Here is the part of the concerned code:
ViewModel
public class AdditionViewModel:INotifyPropertyChanged
{
private string augend;
public string Augend
{
get { return augend; }
set {
if(augend != value)
{
augend = value;
OnPropertyChanged(new PropertyChangedEventArgs("ugend"));
}
}
}
private string addend;
public string Addend
{
get { return addend; }
set {
if (addend != value)
{
addend = value;
OnPropertyChanged(new PropertyChangedEventArgs("ddend"));
}
}
}
private string answer;
public string Answer
{
get { return answer; }
set {
if(answer != value)
{
answer = value;
OnPropertyChanged(new PropertyChangedEventArgs("nswer"));
}
}
}
public AdditionClass additionClass;
public AdditionViewModel(AdditionClass _additionClass)
{
additionClass = _additionClass;
}
public void Add()
{
//Some verifications first before inserting the value to the model class
this.Augend = "1";//Testing for from code to UI binding
additionClass.Augend = Double.Parse(Augend);
additionClass.Addend = Double.Parse(Addend);
//Somewhere here should implement the compute but since addition is a very simple task, no methods were called;
Answer = additionClass.Answer.ToString();
}
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(PropertyChangedEventArgs e)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
MessageBox.Show(e.PropertyName);
handler(this, new PropertyChangedEventArgs(e.PropertyName));
}
}
}
Form:
private void Form1_Load(object sender, EventArgs e)
{
additionPresenter = new Presenter.AdditionPresenter(new ViewModel.AdditionViewModel(new Model.AdditionClass()));
additionViewModelBindingSource.DataSource = additionPresenter.additionViewModel;
}
private void button1_Click(object sender, EventArgs e)
{
additionPresenter.AddButtonClick();
}
Presenter:
public AdditionPresenter(AdditionViewModel _additionViewModel)
{
additionViewModel = _additionViewModel;
}
public void AddButtonClick()
{
additionViewModel.Add();
}
One of the auto-generated code from Designer (Binding on UI):
//
// textBox1
//
this.textBox1.DataBindings.Add(new System.Windows.Forms.Binding("Text", this.additionViewModelBindingSource, "Addend", true));
this.textBox1.Location = new System.Drawing.Point(24, 41);
this.textBox1.Name = "textBox1";
this.textBox1.Size = new System.Drawing.Size(100, 20);
this.textBox1.TabIndex = 0;
As can be seen on the ViewModel, I've omitted all the "A"s at the start of each PropertyName in the setters but the application is still working.
Sorry for the long code pastes. I can't seem to find a better explanation than just to show you the implementation
INotifyPropertyChanged is not necessary for data binding, but it enables two-way data binding.
In fact as mentioned in documentations: The INotifyPropertyChanged interface is used to notify clients, typically binding clients, that a property value has changed.
In simple (one-way) data binding, when you change the bound property of control, value push into the bound property of your object and it doesn't need INotifyPropertyChanges.
But without INotifyPropertyChanged, if you change the value of bound property of your object using code, new value doesn't push into your control's bound property.
Having wrong property names in PropertyChanged event why do I still have two-way data-binding?
In fact it's because of using BindingSource as source of data-boinding, as mentioned by Fabio in comments.
When using BindingSource as data source of your data-bindings, it's enough for your objects to implement INotifyPropertyChanged and raise PropertyChaned event (even with empty or wrong property name) and then the BindingSource (actually its inner BindingList<T>) subscribes for PropertyChaned event and when received the event it checkes if you didn't passed a correct property name or if you passed empty property name it the will call ResetBindings() that consequencly causes a control bound to the BindingSource to reread all the items in the list and refresh their displayed values.
Correct names in PropertyChanged causes the normal behavior of two-way data-binding and also causes raising ListChanged event with correct property in e.PropertyDescriptor.

Changing data when itemsource is specified for a datagrid in wpf

I have a datagrid that I am binding to an ObservableCollection of a custom type. The type has a boolean property, "IsCopying". The "binding" is done by specifying the ItemSource property of the grid to the ObservableCollection.
I want to change data before it is bound to the grid. The grid just needs to be a read-only view of the data. I have changed the column header by using the AutoGeneratingColumn event:
if (e.Column.header.ToString() == "IsCopying")
{
DataGridTextColumn t = new DataGridTextColumn();
t.header = "Status";
e.Column = t;
}
...which works fine. I want to do the same thing during the binding of the individual property to the cell, of each row. I am thing it would work something like:
//NOT REAL CODE!!!!!!!
private void dgItem_CellBinding(object sender, DataGridCellBindingEventArgs e)
{
MyCustomType theItem = e.(MyCustomType)ObjectGettingBound;
if (theItem.IsCopying == true)
{
e.TypeGettingBound= DataGridCellType.Text //or however this works;
e.DataToBind = "Working...";
}else{
e.TypeGettingBound = DataGridCellType.Text;
e.DataToBind = "Waiting for command...";
}
}
Hopefully the above makes sense. I can not find the event for when the individual cell is binding and how to intercept it. I am sure this is very common but I'm new to this and can't find anything on SO that addresses this particular issue. Maybe I'm going about it the wrong way?
does your custom type implement INotifyPropertyChange?
can you add a public property to your custom type that raises PropertyChange event and exposes the text hat you need?
public class MyCustomType: INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string name)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(name));
}
}
private bool _IsCopying;
public bool IsCopying
{
get { return _IsCopying; }
set
{
_IsCopying = value;
OnPropertyChanged("IsCopyingText");
}
}
public string IsCopyingText
{
get
{
if(IsCopying) return "Working...";
else return "Waiting for command...";
}
}
}
then you bind the column to IsCopyingText property, and if you don't want IsCopying column to show, set its Cancel property to True in AutoGeneratingColumn event (http://msdn.microsoft.com/en-us/library/cc903950(v=vs.95).aspx)
to make grid readonly: http://msdn.microsoft.com/en-us/library/system.windows.controls.datagrid.isreadonly.aspx
Another way is to use a IValueConverter, like here:
WPF Datagrid - Column Binding related to other columns

PropertyGrid does not raise PropertyValueChanged event

When user changes text or bool value in PropertyGrid I set flagModified=true; in event handler:
private void propertyGrid1_PropertyValueChanged(object s, PropertyValueChangedEventArgs e)
{
propertyGrid1.Refresh();
PropertyChanged(true);
}
and then Save button is enabled.
I use my Editor and form (see class below) to edit one of values in Properrtygrid. It is object of my class.
After object is changed in editor and editor closed I re-assign value of object to the new value (value = frm.m_DS;). All works fine but one moment: in this case PropertyValueChanged is not raised.
I use PropertyValueChanged event to enable my button "Save" which saves all properties to file.
How I can catch event that value is changed and enable Save button?.
public class DataProviderEditor : UITypeEditor
{
public override Object EditValue(
ITypeDescriptorContext context,
IServiceProvider provider,
Object value)
{
if ((context != null) && (provider != null))
{
IWindowsFormsEditorService svc =
(IWindowsFormsEditorService)
provider.GetService(typeof(IWindowsFormsEditorService));
if (svc != null)
{
using (DatasourceForm frm =
new DatasourceForm((MyDatasource)value))
{
if (svc.ShowDialog(frm) == DialogResult.OK)
{
value = frm.m_DS;
}
}
}
}
return base.EditValue(context, provider, value);
}
I've noticed the documentation about the event PropertyGrid.PropertyValueChanged is flawed. It doesn't mention that the event is raised only if the user changes the value. You can find this out if you check the PropertyValueChangedEventArgs documentation, which says:
The PropertyValueChanged event occurs when the user changes the value of a property, which is specified as a GridItem, in a PropertyGrid.
What you can do is to add PropertyValueChanged event to the object that is browsed by PropertyGrid. You can code it similar to this:
public class BrowsedObject
{
public event EventHandler PropertyValueChanged;
private void OnPropertyValueChanged(object sender, EventArgs e)
{
EventHandler eh = PropertyValueChanged;
if (eh != null)
eh(sender, e);
}
private string someProperty;
public new string SomeProperty
{
get { return someProperty; }
set
{
someProperty = value;
OnPropertyValueChanged(this, EventArgs.Empty);
}
}
}
Each time a new value is assigned to SomeProperty the object will raise PropertyValueChanged. You can hook up to this event logic that is to enable Save button etc.
Or
Why don't you just call PropertyChanged(true); each time you change the object property from the code.
For a very simple "autoupdate" staff you can do this:
propertyGrid1.SelectedObject = propertyGrid1.SelectedObject;
It's seems it worked, change is visible.

.NET WinForms INotifyPropertyChanged updates all bindings when one is changed. Better way?

In a windows forms application, a property change that triggers INotifyPropertyChanged, will result in the form reading EVERY property from my bound object, not just the property changed. (See example code below)
This seems absurdly wasteful since the interface requires the name of the changing property. It is causing a lot of clocking in my app because some of the property getters require calculations to be performed.
I'll likely need to implement some sort of logic in my getters to discard the unnecessary reads if there is no better way to do this.
Am I missing something? Is there a better way? Don't say to use a different presentation technology please -- I am doing this on Windows Mobile (although the behavior happens on the full framework as well).
Here's some toy code to demonstrate the problem. Clicking the button will result in BOTH textboxes being populated even though one property has changed.
using System;
using System.ComponentModel;
using System.Drawing;
using System.Windows.Forms;
namespace Example
{
public class ExView : Form
{
private Presenter _presenter = new Presenter();
public ExView()
{
this.MinimizeBox = false;
TextBox txt1 = new TextBox();
txt1.Parent = this;
txt1.Location = new Point(1, 1);
txt1.Width = this.ClientSize.Width - 10;
txt1.DataBindings.Add("Text", _presenter, "SomeText1");
TextBox txt2 = new TextBox();
txt2.Parent = this;
txt2.Location = new Point(1, 40);
txt2.Width = this.ClientSize.Width - 10;
txt2.DataBindings.Add("Text", _presenter, "SomeText2");
Button but = new Button();
but.Parent = this;
but.Location = new Point(1, 80);
but.Click +=new EventHandler(but_Click);
}
void but_Click(object sender, EventArgs e)
{
_presenter.SomeText1 = "some text 1";
}
}
public class Presenter : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private string _SomeText1 = string.Empty;
public string SomeText1
{
get
{
return _SomeText1;
}
set
{
_SomeText1 = value;
_SomeText2 = value; // <-- To demonstrate that both properties are read
OnPropertyChanged("SomeText1");
}
}
private string _SomeText2 = string.Empty;
public string SomeText2
{
get
{
return _SomeText2;
}
set
{
_SomeText2 = value;
OnPropertyChanged("SomeText2");
}
}
private void OnPropertyChanged(string PropertyName)
{
PropertyChangedEventHandler temp = PropertyChanged;
if (temp != null)
{
temp(this, new PropertyChangedEventArgs(PropertyName));
}
}
}
}
The reason why all properties are being read when the event gets fired rests in the PushData method called on the binding object when the ProperyChanged event is fired. If you look at the stacktrace, you will notice that the PropValueChanged method of the internal object BindToObject is called, that in turn calls the Oncurrentchanged event on the BindingManager. The binding mechanism keeps track of the current item changes, but it doesn't do a more granular distinction. The "culprit" PushData method calls the getter on your properties (take a look at the code using reflector). So there is no way around it. That being said, as a rule of thumb, in the get and set accessors it is not recommended to do heavy processing, use separate get and set methods for that (if possible)
Also take a look at this article, and this comment in particular (http://www.codeproject.com/Messages/2514032/How-Binding-watches-control-properties-i-e-how-doe.aspx), that explains exactly how the propertychanged event gets fired, though it will not address your getter problem: http://www.codeproject.com/KB/database/databinding_tutorial.aspx?msg=2514032
An idea to explore is to delay the getter being called. You can achieve this by playing around with the ControlUpdateMode property of the binding. When this value is set to Never, the corresponding control will not update when there is a change. However, when you switch the value back to OnPropertyChanged, PushData method will be called, so the getters will be accessed. So considering your example this code will temporary prevent the textbox 2 to update:
void but_Click(object sender, EventArgs e)
{
txt2.DataBindings[0].ControlUpdateMode = ControlUpdateMode.Never;
_presenter.SomeText1 = "some text 1";
}
I'm testing subclassing binding like this and managing OnPropertyChanged, maybe helps you.
public class apBinding : Binding
{
public apBinding(string propertyName, INotifyPropertyChanged dataSource, string dataMember)
: base(propertyName, dataSource, dataMember)
{
this.ControlUpdateMode = ControlUpdateMode.Never;
dataSource.PropertyChanged += new PropertyChangedEventHandler(OnPropertyChanged);
}
private void OnPropertyChanged(object sender, PropertyChangedEventArgs e)
{
if (e.PropertyName == this.BindingMemberInfo.BindingField)
{
this.ReadValue();
}
}
}
Now the problem that i find is that the control overwrites the value of the linked object,
so i modified to
public class apBinding : Binding
{
public apBinding(string propertyName, INotifyPropertyChanged dataSource, string dataMember)
: base(propertyName, dataSource, dataMember)
{
dataSource.PropertyChanged += new PropertyChangedEventHandler(OnPropertyChanged);
}
private void OnPropertyChanged(object sender, PropertyChangedEventArgs e)
{
this.ControlUpdateMode = ControlUpdateMode.Never;
if (e.PropertyName == this.BindingMemberInfo.BindingField)
{
this.ReadValue();
}
}
}
then the first time propertychanges is called i disable controlupdate. and the control is correctly updated at the first run.

Categories