Using INotifyPropertyChanged in WPF - c#

Hi i am trying to use the NotifyPropertyChanged to update all the places where i am binding a property. For what i have searched the INotifyPropertyChanged is indicated to this cases.
So i need help because i don't understand what i have wrong in here. And i really don't know what to do with the PropertyChange event. My question about that is, when he changes? What i have to more with him?
Datagrid:
<ListView Name="listView" ItemsSource="{Binding Categories}"/>
An example when i change my Categories property:
DataTest dtTest = new DataTest();
public MainWindow()
{
InitializeComponent();
this.DataContext = dtTest;
}
private void Button1_Click(object sender, RoutedEventArgs e)
{
//Here i pick up a string from a textBox, where i will insert in a Table of my DB,
//Then i will do a query to my Table, and i will get a DataTable for example
//Then i just put the contents into the DataView, so the value have changed.
dtTest.Categories = dtTable.DefaultView;
dtTest = dtTable.defaultView; (this only an example, i don't this for real.)
//What i have to do now, to wherever i am binding (DataGrid, ListView, ComboBox)
//the property to the new values automatically being showed in all places?
}
My Class:
public class DataTest : INotifyPropertyChanged
{
private DataView categories;
public event PropertyChangedEventHandler PropertyChanged; //What i have to do with this?
private void NotifyPropertyChanged(string str)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(str));
}
}
public DataView Categories
{
get { return categories; }
set
{
if (value != categories)
{
categorias = value;
NotifyPropertyChanged("Categories");
}
}
}
}
My Class with INotifyCollectionChanged:
public class DataTest : INotifyCollectionChanged
{
public event NotifyCollectionChangedEventHandler CollectionChanged;
private void OnCollectionChanged(NotifyCollectionChangedEventArgs e)
{
if (CollectionChanged != null)
{
CollectionChanged(this, e);
}
}
public DataView Categories
{
get { return categories; }
set
{
if (value != categories)
{
categories = value;
OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, Categories));
}
}
}
}
But why the PropertyChanged is always NULL??? I have to do something more, but i don't know what.
Thanks in advance!

the DataTest class needs to be the DataContext for the Binding. You can set this in code-behind (or a myriad of other ways, but for simplicity - just do it in code-behind)
public MainWindow() // constructor
{
this.DataContext = new DataTest();
}
Then, with the 'Source' of the Binding set, you can specify the 'Path', so your xaml looks like this:
<ListBox ItemsSource="{Binding Categories}" />
Now, if the property 'Categories' is changed in code, the NotifyPropertyChanged code you have written will alert the Binding, which in turn will access the public getter for the property and refresh the view.

Getting the handler prevents the event handler from going to null after you check for null and checking for null will prevent you from getting a null exception if there are no event handlers.
The reason that your PropertyChanged is null is that there are no event handlers attached to it. The reason there are not handlers attached to it is you have not bound your object to anything (which will take care of adding a handler) or you haven't added a handler to it (if you wanted to observe it for some other reason). Once your object is created you need to bind it somewhere.

You are doing all you have to do. An event is just a special kind of delegate. You declare it, you invoke it, clients subscribe to it. That's all there is to it.

Related

INotifyPropertyChanged in nested object

I have 2 main classes. The first class represents a Cell that can have values X, O or Empty. I have implemented INotifyPropertyChanged on this.
public class Cell : INotifyPropertyChanged
{
private Symbol state;
public Symbol State
{
get { return state; }
set
{
if (value == Symbol.X || value == Symbol.O)
state = value;
OnPropertyChanged("State");
}
}
public Cell()
{
state = Symbol.Empty;
}
public enum Symbol
{
X, O, Empty
}
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(string propName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propName));
}
}
}
The second class contains an object of this class and is also set as the datacontext for my main window.
public class Board
{
private Cell testCell;
public Cell TestCell
{
get { return testCell; }
set { testCell = value; }
}
public Board()
{
TestCell = new Cell();
}
public void Cell_Click(int cellNum)
{
TestCell.State = Cell.Symbol.O;
}
}
In my mainwindow I have set the datacontext as board, and also contains a button_click function.
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
this.DataContext = new Board();
}
private void Button_Click(object sender, RoutedEventArgs e)
{
Board board = this.DataContext as Board;
board.Cell_Click(cellNum);
}
}
In my XAML I have bound to Cell object within board using a button style like this:
<Setter Property="Background" Value="{Binding TestCell,
Converter={StaticResource BGConverter}}"/>
BGConverter is a custom converter that accepts a Cell object and converts it into a Colors object. I believe I am indeed directly binding to an object that has INotify implemented, so there's no issue of nested objects. However the binding doesn't reflect changes when I click. When debugging, I found that PropertyChanged event is always null.
The closest answer I found for this is that the event will be subscribed to only if the class Cell is my datacontext. Atleast that's what I understood. How can I correct this problem?
Also I am fresh out of college, currently learning WPF on a new job, so any general recommendations are welcome too.
Thanks
Simply bind to TestCell.State instead of TestCell
I'm pretty new at this myself, but I believe your data context needs to implement INotifyPropertyChanged as well.
That is, your Board class needs to listen to the PropertyChanged event of the cells and fire its own PropertyChanged event when this happens.

Bound UserControl is not updating data source

I have a UserControl that has a Textbox, Button, and a Tooltip controls on it. It does implement INotifyPropertyChanged I have tried overriding the Text property and adding my own property, but in all cases the control reads from the bound data source fine, but never updates the data source. My events are raised when the text is changed. Some of the code is below. All other standard controls are working fine. What do I need to get the control to update the data source when the user has entered or changed the value?
public partial class UrlControl : UserControl, INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
[Bindable(true)]
[Browsable(true)]
public string Url
{
get
{
return url.Text;
}
set
{
if (value != url.Text)
{
url.Text = value;
OnPropertyChanged("Url");
}
}
}
protected virtual void OnPropertyChanged(string propertyName)
{
OnPropertyChanged(new PropertyChangedEventArgs(propertyName));
}
protected virtual void OnPropertyChanged(PropertyChangedEventArgs e)
{
if (PropertyChanged != null)
{
PropertyChanged(this, e);
}
}
This is the binding code from the form designer.
this.urlControl1.DataBindings.Add(new System.Windows.Forms.Binding("Url", this.customerBindingSource, "First", true, System.Windows.Forms.DataSourceUpdateMode.OnPropertyChanged));
}
INotifyPropertyChanged is for datasources. It allows your datasource to notify bound controls and other listeners of property changes. However, controls themselves use a different mechanism. It's a bit strange: you create events on your control with the naming convention <PropertyName>Changed. When the value of a property changes, you raise the associated event.
Example:
public string Url
{
get { return url.Text; }
set
{
if (value != url.Text)
{
url.Text = value;
OnUrlChanged(); // raise event
}
}
}
public event EventHandler UrlChanged;
private void OnUrlChanged()
{
// raise the UrlChanged event
if (UrlChanged != null)
UrlChanged(this, new EventArgs());
}
That's all you need to do. The Databinding Fairies will see that event and hook it up when you create the binding.
Here's the topic on MSDN: How to: Apply the PropertyNameChanged Pattern
This should work well for reading values from the datasource.
However, when it comes to writing values to the datasource it looks like you're storing and getting the Url value directly from the url textbox. However, you're not raising property change notifications when the textbox's text is changed within the UI. To fix this, add a TextChanged event handler on the textbox, which can simple call:
void url_TextChanged(object sender, EventArgs e)
{
OnPropertyChanged("Url");
OnUrlChanged(); // See additional note below
}
As a side, although implementing INotifyPropertyChanged should work... When it comes to Windows Forms binding you can also create an event with the property name suffixed with "Changed" and the binding should watch that:
public event EventHandler UrlChanged;
protected virtual void OnUrlChanged()
{
var handler = UrlChanged;
if (handler != null)
{
handler(this, EventArgs.Empty);
}
}
[Bindable(true)]
[Browsable(true)]
public string Url
{
get
{
return url.Text;
}
set
{
if (value != url.Text)
{
url.Text = value;
OnPropertyChanged("Url");
OnUrlChanged();
}
}
}

Data binding in WPF on button click

I am trying to implement data binding, and to have TextBox's text to be update once I click on some button.
XAML:
<TextBox Text="{Binding Path=Output}" />
Code:
public MainWindow()
{
InitializeComponent();
DataContext = Search;
Search.Output = "111";
}
public SearchClass Search = new SearchClass();
private void button1_Click(object sender, RoutedEventArgs e)
{
Search.Output = "222";
}
public class SearchClass
{
string _output;
public string Output
{
get { return _output; }
set { _output = value; }
}
}
When I execute the program, I see "111", so the binding from MainWindow() works, but if I click a button - the text in the TextBox is not updated (but in the debugger I see that button1_Click is executed and Search.Output is now equal to "222"). What am I doing wrong?
You should implement INotifyPropertyChanged in your SearchClass and then in setter raise the event:
public event PropertyChangedEventHandler PropertyChanged = delegate { };
public string Output
{
get { return _output; }
set
{
_output = value;
PropertyChanged(this, new PropertyChangedEventArgs("Output"));
}
}
If I understood right, SearchClass is the DataContext for your TextBlock. In this case implementing as above would help.
When WPF see some class as the source of Binding - it tries to cast it to INotifyPropertyChanged and subscribe to PropertyChanged event. And when event is raised - WPF updates the binding associated with sender (first argument of PropertyChanged). It is the main mechanism that makes binding work so smoothly.
You have to implement the INotifyPropertyChanged interface on your SearchClass class. This is how binder values are notified their source values have changed. It displays the "111" value because it hasn't been laid out yet (more or less), but will won't update after that until you implement that interface.

How to implement DataTable property with INotifyPropertyChanged

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

Databinding a Custom Control

I have a Custom Control (Windows Form) that is a lookup text box. A property on the Control is Current Selection which is a Custom Object containing "Identifier", "Code" and "Description". This property is Databound using a BindingSource.
Displaying the information works great. On the other hand regardless of whether I set the Update to OnValidate or OnValueChange it never updates the BindingSource. Is there something I'm missing to get this to auto update?
private System.Windows.Forms.BindingSource buildPlanComponentDataBindingSource;
public void LoadBuildPlan(string itemNumber)
{
var buildPlanComponents = BuildPlan.LoadBuildPlanComponents(itemNumber, AutomaticPrice);
buildPlanComponentDataBindingSource.DataSource = buildPlanComponents;
AssemblyNumber = itemNumber;
}
[Bindable(true)]
[DefaultValue(null)]
public ILookupSelection CurrentSelection
{
get
{
if (currentSelection == null)
currentSelection = new LookupSelection {Code = txtLookup.Text};
return currentSelection;
}
set
{
if (value == null) return;
currentSelection = value;
SetText(currentSelection, DisplayText);
SetDescription(currentSelection, DisplayDescription);
}
}
Implementing INotifyPropertyChanged seems to be the solution!
#region IPropertyChanged
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propertyName)
{
OnPropertyChanged(new PropertyChangedEventArgs(propertyName));
}
protected virtual void OnPropertyChanged(PropertyChangedEventArgs e)
{
if (null != PropertyChanged)
{
PropertyChanged(this, e);
}
}
#endregion
Displaying the information works
great. On the other hand regardless of
whether I set the Update to OnValidate
or OnValueChange it never updates the
BindingSource.
Looking at your code I'm actually not sure of this. In your set, you test for null and abandon; if the data actually contains null (which is what you're describing) your control will be out of synch. I wonder if perhaps that check is masking the underlying problem.
Maybe you need to cause the DataBinding to write its value for each control whose value you are setting this way?
Assuming one data binding for a textbox named txtMySetValue:
txtMySetValue.DataBindings[0].WriteValue();

Categories

Resources