WPF Databinding - Getter/Setter for intermediary object not getting hit - c#

My WPF TextBox uses a class instance (called "SelectedDocument") as its datacontext. This class implements INOtifyPropertyChanged.
The SelectedDocument instance owns another object of type "CellContent" (named "Description"), exposed via a property.
CellContent also implements INotifyPropertyChanged.
The CellContent class has a string property ("TextValue") that can be bound to.
I'm binding the TextBox's Text property to that TextValue property. Like so:
<TextBox DataContext="{Binding SelectedDocument}" Text="{Binding Path=Description.TextValue" />
No issues with the databinding - it works both ways. But for that binding to work, WPF presumably has to hit the Description property in SelectedDocument every time to retreive the CellContent object:
public CellContent Description
{ get; set; }
(This property is more complicated in my code.) Then WPF can reach inside the actual CellContent object and get/set TextValue.
The problem: the Description property is never hit. It looks like WPF is bypassing it, and has created a direct connection to the TextValue property inside of the Description object. I want to hit the Description getter and setters every time so that I can execute some extra code there.
How do I do that?

If you want the Document to be marked dirty whenever a child's property is changed, you could subscribe to the child's PropertyChanged event.
I'm assuming your CurrentDocument looks something like this.
public class Doc
{
public Doc()
{
_description = new CellContent();
// subscribe to changes in child
_description.PropertyChanged += DescriptionChanged;
}
private void DescriptionChanged(object sender, PropertyChangedEventArgs e)
{
Debug.Write($"I'm a dirty dirty document. Property {e.PropertyName} has changed");
}
private CellContent _description;
public CellContent Description
{
get
{
Debug.Write("I assure you this is called every time a getter of the child properties is called");
return _description;
}
// If you have a setter, don't forget to -= unsubscribe and resubscribe += after changing
}
}

Try to to raise PropertyChanged event for Description every time when you change TextValue property.

Related

how to provide change notification for a property when a subproperty changes?

This is such a basic question, but I don't think I've done this before despite having bound so many properties. I originally was planning to bind a class called TimeScale to various objects.
In class A we have a dependency property that I want to call change notification on. However, change notification is not done manually through this class.
public TimeScale AxisTimeScale
{
get { return (TimeScale)GetValue(AxisTimeScaleProperty); }
set { SetValue(AxisTimeScaleProperty, value); }
}
public static readonly DependencyProperty AxisTimeScaleProperty =
DependencyProperty.Register("AxisTimeScale",
typeof(TimeScale), typeof(SignalPanel),
new FrameworkPropertyMetadata(new TimeScale()));
this binds to source class B
private class B
{
private TimeScale _GraphTimeScale;
public TimeScale GraphTimeScale
{
get { return _GraphTimeScale; }
set
{
if (value != _GraphTimeScale)
{
_GraphTimeScale = value;
OnPropertyChanged("GraphTimeScale");
}
}
}
}
Looking at it again I guess all I really want is to call propertychanged on a dependency property, but since I didn't implement Inotifypropertychanged, I am wondering how i do that.
I think DependencyObject already implements Inotifypropertychanged, so I have access to this:
OnPropertyChanged(new DependencyPropertyChangedEventArgs(property, old value, new value));
However, inserting the same object into both the old value and new value slots results in the PropertyChanged event not firing (I assume the implementation checks whether the two values are the same before firing the event). I want to avoid creating a new object if possible. I guess one option is to override OnPropertyChanged. Nope that also requires me to have a dependency propertychanged event args.
Update
OnPropertyChanged("TimeScale");
to
OnPropertyChanged("GraphTimeScale");
Or,
you can wrap the TimeScale class with an ObservableObject so that you can subscribe to object change events and raise them from there.
More info: http://msdn.microsoft.com/en-us/library/ff653818.aspx
Subscribe to the PropertyChanged notification of NumberOfUnits, and then raise OnPropertyChanged("GraphTimeScale") in the property changed event handler.
Would be interested if there is a better way though.

Change notification without INotifyPropertyChanged? (excerpt from Pro WPF in C# 2010)

While reading Pro WPF in C# 2010 the author writes:
"You can raise an event for each property. In this case, the event must have the name PropertyNameChanged (for example, UnitCostChanged). It’s up to you to fire the event when the property is changed."
Could someone confirm this feature works? I was experimenting and not able to reproduce this behavior (I want to see if this works so then I can do some experimenting with System.Reflection.Emit to create dynamic types)
EDIT: I should clarify the emphasis here is to implement change notification WITHOUT implementing INotifyPropertyChanged, as this is what the book is claiming
Here's the POCO I am testing with:
public class Employee
{
private string _FirstName;
public string FirstName
{
get
{
return _FirstName;
}
set
{
if (_FirstName != value)
{
_FirstName = value;
if (FirstNameChanged != null)
{
FirstNameChanged(this, new PropertyChangedEventArgs("FirstName"));
}
}
}
}
}
I bound it to a DataGrid and have a timer in the background update the FirstName property randomly every few seconds but the DataGrid never fires
<DataGrid x:Name="dgEmployees" ItemsSource="{Binding ElementName=mainWindow, Path=MyEmployees}">
<DataGrid.Columns>
<DataGridTextColumn Header="FirstName" Binding="{Binding Path=FirstName}" />
</DataGrid.Columns>
</DataGrid>
The FirstNameChanged event is always null (I thought the binding engine might automatically subscribe to it if it detected it according to the naming convention). MyEmployees is just an ObservableCollection
Can someone confirm if this feature the author mentions, does indeed work and if I'm making a mistake?
EDIT: for the benefit of anyone who thinks I'm misinterpreting the text:
"You can use three approaches to solve this problem:
You can make each property in the Product class a dependency property using the
syntax you learned about in Chapter 4. (In this case, your class must derive from
DependencyObject.) Although this approach gets WPF to do the work for you
(which is nice), it makes the most sense in elements—classes that have a visual
appearance in a window. It’s not the most natural approach for data classes like
Product.
You can raise an event for each property. In this case, the event must have the
name PropertyNameChanged (for example, UnitCostChanged). It’s up to you to
fire the event when the property is changed.
You can implement the System.ComponentModel.INotifyPropertyChanged
interface, which requires a single event named PropertyChanged. You must then
raise the PropertyChanged event whenever a property changes and indicate which
property has changed by supplying the property name as a string. It’s still up to
you to raise this event when a property changes, but you don’t need to define a
separate event for each property."
I would say it's very possible to implement change notifications in your POCO's without using either INotifyPropertyChanged or dependency properties, like it's being claimed in the book, unless i'm completely missing the point of the question.
If you raise an event called {Propertyname}Changed from your POCO when the value of a property has changed, the WPF binding system will pick that up, and update the relevant bindings.
See this small program for a demonstration - it's the simplest thing I could think of, but I guess it should work in your case as well.
XAML:
<StackPanel>
<TextBlock Text="{Binding Name}" />
<Button Content="Change name" Click="changeNameClick" />
</StackPanel>
Code-behind:
public partial class Window1 : Window
{
private SimpleObject so = new SimpleObject();
public Window1()
{
InitializeComponent();
this.so = new SimpleObject();
so.Name = "Initial value";
this.DataContext = so;
var t = new DispatcherTimer(
TimeSpan.FromSeconds(1),
DispatcherPriority.Normal,
(s, e) => { this.so.Name = "Name changed at: " + DateTime.Now.ToString(); },
Dispatcher);
}
private void changeNameClick(object sender, RoutedEventArgs e)
{
this.so.Name = "New value!!";
}
}
public class SimpleObject
{
private string mName = null;
public string Name
{
get { return mName; }
set
{
if (mName != value)
{
mName = value;
if (NameChanged != null)
NameChanged(this, EventArgs.Empty);
}
}
}
public event EventHandler NameChanged;
}
The pattern you mention applies to WPF as well as to Windows Forms (WinForms).
WPF, see: "Providing Change Notifications" section in Binding Sources Overview on MSDN.
WinForms, see: How to: Apply the PropertyNameChanged Pattern on MSDN.
No, with WPF, you need to use DependencyProperties or INotifyPropertyChanged. For collections, INotifyCollectionChanged will do the trick too.

How to have bindable properties of a UserControl which work with OnPropertyChanged

I have a simple usercontrol (WinForms) with some public properties. When I use this control, I want to databind to those properties with the DataSourceUpdateMode set to OnPropertyChanged. The datasource is a class which implements INotifyPropertyChanged.
I'm aware of the need to create bindings against the properties and I'm doing that.
I assumed that my usercontrol would have to implement an interface, or the properties would need to be decorated with some attribute, or something along those lines.But my research has come up blank.
How should this be accomplished? At the moment I'm doing it by calling OnValidating() in my usercontrol whenever a property changes, but that doesn't seem right.
I can get validation to happen if I set the CausesValidation to true on the usercontrol, but that's not very useful to me. I need to validate each child property as it changes.
Note this is a WinForms situation.
EDIT: Evidently I have no talent for explanation so hopefully this will clarify what I'm doing. This is an abbreviated example:
// I have a user control
public class MyControl : UserControl
{
// I'm binding to this property
public string ControlProperty { get; set; }
public void DoSomething()
{
// when the property value changes, the change should immediately be applied
// to the bound datasource
ControlProperty = "new value";
// This is how I make it work, but it seems wrong
OnValidating();
}
}
// the class being bound to the usercontrol
public class MyDataSource : INotifyPropertyChanged
{
private string sourceProperty;
public string SourceProperty
{
get { return sourceProperty; }
set
{
if (value != sourceProperty)
{
sourceProperty = value;
NotifyPropertyChanged("SourceProperty");
}
}
}
// boilerplate stuff
public event PropertyChangedEventHandler PropertyChanged;
protected void NotifyPropertyChanged(string info)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(info));
}
}
public class MyForm : Form
{
private MyControl myControl;
public MyForm()
{
// create the datasource
var dataSource = new MyDataSource() { SourceProperty = "test" };
// bind a property of the datasource to a property of the usercontrol
myControl.DataBindings.Add("ControlProperty", dataSource, "SourceProperty",
false, DataSourceUpdateMode.OnPropertyChanged); // note the update mode
}
}
(I have tried this using a BindingSource, but the result was the same.)
Now what I want to happen is that when the value of MyControl.ControlProperty changes, the change is immediately propagated to the datasource (the MyDataSource instance). To achieve this I call OnValidating() in the usercontrol after changing the property. If I don't do that, I have to wait until validation gets triggered by a focus change, which is the equivalent of the "OnValidation" update mode, rather than the desired "OnPropertyUpdate" validation mode. I just don't feel like calling OnValidating() after altering a property value is the right thing to do, even if it (kind of) works.
Am I right in assuming the calling OnValidating() is not the right way to do this? If so, how do I notify the datasource of the ControlProperty change?
I think I've got this figured out. I didn't understand how change notifications were sent from control to bound datasource.
Yes, calling OnValidating() is the wrong way.
From what I've pieced together, there are two ways a control can notify the datasource that a property has changed.
One way is for the control to implement INotifyPropertyChanged. I had never done this from the control side before, and I thought only the datasource side of the binding had to implement it.
When I implemented INotifyPropertyChanged on my user control, and raised the PropertyChanged event at the appropriate time, it worked.
The second way is for the control to raise a specific change event for each property. The event must follow the naming convention: <propertyname>Changed
e.g. for my example it would be
public event EventHandler ControlPropertyChanged
If my property was called Foo, it would be FooChanged.
I failed to notice the relavent part of the MSDN documentation, where it says:
For change notification to occur in a
binding between a bound client and a
data source, your bound type should
either:
Implement the INotifyPropertyChanged
interface (preferred).
Provide a change event for each
property of the bound type.
This second way is how all existing WinForms controls work, so this is how I'm doing it now. I use INotifyPropertyChanged on my datasource, but I raise the Changed events on my control. This seems to be the conventional way.
Implementing the INotifyPropertyChanged interface is very simple. Here is a sample that shows an object with a single public field...
public class Demo : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(String info)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(info));
}
private string _demoField;
public string DemoField
{
get {return demoField; }
set
{
if (value != demoField)
{
demoField = value;
NotifyPropertyChanged("DemoField");
}
}
}
}
Then you would create a Binding instance to bind a control property to a property (DemoField) on your source instance (instance of Demo).

When do I need to use automatic poperties and when properties with propertychanged event?

I am using wpf and its C sharp!
I have this in my Animal.cs clas
private string _animalName;
public string AnimalName
{
get { return _animalName; }
set
{
if(_animalName!= value)
{
_animalName= value;
this.NotifyPropertyChanged("AnimalName");
}
}
}
I could also write:
public string AnimalName {get;set;}
There is no difference in binding and validation. Everythings works as before when I exchange the code.
Is this due to the fact that I only create new animals but I do not allow to update the animals name in my application ?
So I need to call the propertyChanged("AnimalName"); only when I want to change its property value?
I am a c# beginner ;)
If your object has an updateable property (setter) that will be bound to a control then you need to ensure to let the bound control know of any changes to that property via INotifyPropertyChanged. However, if you have a readonly property and/or a property that's not going to be used in a data-binding scenario then you don't care about implementing or calling NotifyPropertyChanged method from within that property's setter in which case you can use automatic properties.

WPF - Binding in XAML to an object created in the code behind

Can an object created in the code (ie C#) be used for binding in the XAML?
For example:
public class MyForm
{
private MyComplexObject complexObject;
public MyForm()
{
InitializeComponent();
}
public OnButtonClick(object sender, RoutedEventArgs e)
{
complexObject = new MyComplexObject();
}
}
complexObject is not created till a button is clicked. But once that button is clicked I would like to have a text box that is bound to complexObject.ID start showing the Id.
I would like to do that in the XAML if that is possible.
Can this be done? If so, how?
Yes, this can be done, binding to a property that you update with the desired value. I'd suggest you look into the MVVM pattern (Model-View-ViewModel), which is really useful for structuring this nicely working with WPF. Check out this video for a nice overview:
MVVM video
Using MMVM you would create a class which would be the ViewModel class. This one would typically be set to the DataContext of the View. Having done so you could add dynamic references to the other properties of the class, e.g. binding your text field to some property holding the Id og the ComplexObject. If your ViewModel class had a property ComplexObject, which again had a property ID you'd simply bind to the object like this:
<TextBlock Text="{Binding ComplexObject.ID}" />
Having this you could trigger creation of your ComplexObject from mouse click, which you should ideally set up as a command binding. Also note that the ViewModel class (or whoever is holding the ComplexObject needs to notify the View when the object has been set. This can either be done by making the ComplexObject a DependencyProperty or by making the class holding the property implement the INotifyPropertyChanged interface - giving it the PropertyChanged function to trigger the changed event. I prefer the latter.
One possibility would be to have your XAML bind to a property on your code behind. The getter for that property would return complexObject.ID, if complexObject != null. Otherwise, it returns something "default", whether that's null or 0 or default(ID's type). Similarly, the setter for that property would assign value to complexObject.ID if complexObject is, again, not null.
public int ID
{
get
{
if (complexObject != null)
return complexObject.ID;
return 0; // or null or some appropriate default
}
set
{
if (complexObject != null)
complexObject.ID = value;
}
}

Categories

Resources