Dependency Property and ViewModel initialization problem - wpf

I am trying to add my own ItemsSource to provide a list of GraphViewModels to a chart. I dont think I have it quite right though, as when I create my first GraphViewModel and add it to the Graphs, my DP is updated, but OnGraphsCollectionChanged is not called.
How is this supposed to work? If I add graphs to my VM property via a button tied to a command then all is good.
Here is the DP code, can anyone explain how this is supposed to work or what Im doing wrong to display my data during initialization?
public static readonly DependencyProperty GraphsProperty =
DependencyProperty.Register("ItemsSource",
typeof(ObservableCollection<GraphViewModel>),
typeof(DynamicPlotter),
new FrameworkPropertyMetadata(new PropertyChangedCallback(ChangeGraphs)));
public ObservableCollection<GraphViewModel> ItemsSource
{
get { return (ObservableCollection<GraphViewModel>)GetValue(GraphsProperty); }
set
{
SetValue(GraphsProperty, value);
ItemsSource.CollectionChanged += new NotifyCollectionChangedEventHandler(OnGraphsCollectionChanged);
}
}
public static void ChangeGraphs(DependencyObject source, DependencyPropertyChangedEventArgs eventArgs)
{
(source as DynamicPlotter).UpdateGraphs((ObservableCollection<GraphViewModel>)eventArgs.NewValue);
}
private void UpdateLineGraphs(ObservableCollection<GraphViewModel> grphs)
{
this.ItemsSource = grphs;
}
private void OnGraphsCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
// This never gets called when you set the ItemsSource, but is where all the work is done
}

CollectionChanged only gets called when the collection changes, if the collection is already populated prior to being set you will never get your notification, until something gets added/removed.
Secondly, if you are setting the dependency property from xaml the getter/setter is not used, the dependency mechanism uses its own internal setter routines. You should attach your collectionChanged event in your ChangeGraphs property callback function as this is called whenever the property is set/changed. You can use this to unhook the old collectionChanged event as well, the event arguments will give you an old and new value.
But really, it is an observable collection you should not have to know when the collection changes as you should be binding to the collection and when it changes the binding mechanism will update your ui.
I would change my code to look like this
public ObservableCollection<GraphViewModel> ItemsSource {
get { return (ObservableCollection<GraphViewModel>)GetValue(ItemsSourceProperty); }
set { SetValue(ItemsSourceProperty, value); }
}
// Using a DependencyProperty as the backing store for ItemsSource. This enables animation, styling, binding, etc...
public static readonly DependencyProperty ItemsSourceProperty =
DependencyProperty.Register("ItemsSource", typeof(ObservableCollection<GraphViewModel>), typeof(DynamicPlotter), new UIPropertyMetadata(null, (o, e) => { ((DynamicPlotter)o).ItemsSourceChanged(); }));
private void ItemsSourceChanged() {
if (this.ItemsSource != null){
//attach the collection changed listener, this will listen to all FUTURE collection changes, items that are added and removed
this.ItemsSource.CollectionChanged +=new NotifyCollectionChangedEventHandler(ItemsSource_CollectionChanged);
//do some inital processing with the items that are in the collection already when it is set
this.UpdateGraphs(this.ItemsSource);
}
private void ItemsSource_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e){
//this will get called if an item gets added or removed from the collection
}

Related

How can I bind to a non ObservableCollection?

Is there a way to bind directly to a Collection in the model and manually tell WPF that the binding needs refreshing without having to create an ObservableCollection for it in the viewmodel?
<ListBox ItemsSource="{Binding Position.PossibleMoves}">
...
</ListBox>
Position is my model, part of a chess library, and PossibleMoves is a Collection within it. I do not want to implement INotifyProperty changed or put ObservableCollections in a stand alone optimized library.
I want to avoid copying PossibleMoves into an ObservableCollection every time the position is updated. The data binding works on initialization but it would be handy if I could also refresh the binding at will inside the viewmodel.
Calling OnNotifyPropertyChanged("Position.PossibleMoves") from the viewmodel doesn't work because the reference to the collection itself does not change.
You can do this by using an attached behavior to bind a handler to an event that gets triggered in the view model. You can't bind directly to events though so you have to wrap them in a class like so:
public class Refresher
{
public delegate void RefreshDelegate();
public event RefreshDelegate Refresh;
public void DoRefresh()
{
if (this.Refresh != null)
this.Refresh();
}
}
Now add an instance of that to your view model:
public class MyViewModel
{
public IList<string> Items { get; set; }
private Refresher _Refresher = new Refresher();
public Refresher Refresher {get {return this._Refresher;}}
}
Next create an attached behavior that registers a delegate instance with that event and forces the listbox to refresh its binding:
public static class RefreshBehavior
{
public static readonly DependencyProperty RefresherProperty = DependencyProperty.RegisterAttached(
"Refresher",
typeof(Refresher),
typeof(RefreshBehavior),
new PropertyMetadata(null, OnRefresherChange));
public static void SetRefresher(DependencyObject source, Refresher value)
{
source.SetValue(RefresherProperty, value);
}
public static Refresher GetRefresher(DependencyObject source)
{
return (Refresher)source.GetValue(RefresherProperty);
}
private static void OnRefresherChange(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
Refresher.RefreshDelegate handler = () =>
{
var listBox = d as ListBox;
listBox.Items.Refresh();
};
if (e.NewValue != null)
(e.NewValue as Refresher).Refresh += handler;
if (e.OldValue != null)
(e.OldValue as Refresher).Refresh -= handler;
}
}
And finally attach it to your listbox in the xaml:
<ListBox ItemsSource="{Binding Items}"
local:RefreshBehavior.Refresher="{Binding Refresher}"/>
That's it. Call Refresher.DoRefresh() in your view model and it will force a listbox update.
This works but it's really hammering a square peg into a round hole. If I were you I'd do everything I could to try and do proper collection changed notification in your view model. I understand you wanting to keep ObservableCollection out of your model but there are ways to proxy change notification automatically (e.g. Castle DynamicProxy).
You need to NotifyPropertyChange for the PossibleMoves from inside the Position class or make a property that delegates to the Position.PossibleMoves and notify that one.

DependencyProperty getter/setter not being called

I am trying to create a Custom control derived from a standard Grid.
I added a ObservableCollection as a DependencyProperty of the Custom control. However, the get/set of it is never reached. Can I have some guidelines in creating a DependencyProperty that works correctly with and ObservableCollection?
public class MyGrid : Grid
{
public ObservableCollection<string> Items
{
get
{
return (ObservableCollection<string>)GetValue(ItemsProperty);
}
set
{
SetValue(ItemsProperty, value);
}
}
public static DependencyProperty ItemsProperty =
DependencyProperty.Register("Items", typeof(ObservableCollection<string>),
typeof(MyGrid), new UIPropertyMetadata(null, OnItemsChanged));
}
I would suggest not to use ObservableCollection as the type of an Items dependency property.
The reason for having an ObservableCollection here (I guess) is to enable the UserControl to attach a CollectionChanged handler when the property value is assigned. But ObservableCollection is too specific.
The approach in WPF (e.g. in ItemsControl.ItemsSource) is to define a very basic interface type (like IEnumerable) and when the property is assigned a value, find out if the value collection implements certain more specific interfaces. This would at least be INotifyCollectionChanged here, but the collection might also implement ICollectionView and INotifyPropertyChanged. All these interfaces wouldn't be mandatory and that would enable your dependency property to bind to all sorts of collections, starting with a plain array up to a complex ItemCollection.
Your OnItemsChanged property change callback would then look like this:
private static void OnItemsChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e)
{
MyGrid grid = obj as MyGrid;
if (grid != null)
{
var oldCollectionChanged = e.OldValue as INotifyCollectionChanged;
var newCollectionChanged = e.NewValue as INotifyCollectionChanged;
if (oldCollectionChanged != null)
{
oldCollectionChanged.CollectionChanged -= grid.OnItemsCollectionChanged;
}
if (newCollectionChanged != null)
{
newCollectionChanged.CollectionChanged += grid.OnItemsCollectionChanged;
// in addition to adding a CollectionChanged handler
// any already existing collection elements should be processed here
}
}
}
private void OnItemsCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
// handle collection changes here
}
The WPF binding mechanism may bypass your standard CLR property and go directly to the dependency property accessors (GetValue and SetValue).
That is why logic should not be placed inside of the CLR property, but instead inside a changed handler.
Also the ObservableCollection<string> will never be set because when you use collection properties from XAML, like the following:
<local:MyGrid>
<local:MyGrid.Items>
<sys:String>First Item</sys:String>
<sys:String>Second Item</sys:String>
</local:MyGrid.Items>
</local:MyGrid>
It is actually calling a get on Items and then calling Add for each of the elements.

How to databind two dependency properties belonging to two different controls?

Alternatively: How to subscribe to the PropertyChanged event defined by INotifyPropertyChanged thru the databinding of two dependency properties?
I have two separate user controls inside my main window. One control contains the parameters that affect the other control, let’s call it the display control. I want the parameter control to act as the datasource of the display control so that when I change a parameter in the parameter control, the display control be listening and reacts accordingly.
For this I created a class that implements INotifyPropertyChanged that stores these parameters and created dependencies properties of this class type in both controls. I was expecting that if I binded one control property to the other I would get the desired behaviour, but unfortunately I am missing something important because the display control is not reacting.
On a closer inspection with the debugger, I notice that my event PropertyChangedEventHandler PropertyChanged was always null when a property had changed, and everything I have read indicates, that no one is listening.
Because the display control is created in real time, I have to create the binding programmatically like this:
var DispayControlValuesBinding = new Binding();
DispayControlValuesBinding.Source = DisplayControlsControl;
DispayControlValuesBinding.Path = new PropertyPath("DisplayControlValues");
DispayControlValuesBinding.Mode = BindingMode.OneWay;
DispayControlValuesBinding.UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged;
DispayControlValuesBinding.NotifyOnSourceUpdated = true;
//
graph.SetBinding(Graph.DisplayControlValuesProperty, DisplayControlValuesBinding);
Both controls have a dependency property called DispayControlValues. I try to bind the DisplayControlControl's DisplayControlValues property to the graph control's DisplayControlValues property.
When the application runs, it initializes the parameter control, then with a user request a display control is created programmatically and the binding is made. Then I change a value in the parameter control, this is catch by the parameters class that implements the INotifyPropertyChanged interface but because no one is listening, the event handler is null and here is where I am stuck.
Your help is greatly appreciated!
Here are more details as requested:
I have one user control that exposes the parameters that changes the behaviour of another control. This control has a dependency property that contains parameter details and implements the INotifyPropertyChanged interface.
Here is the class:
public class ZoomGraphControlValues : INotifyPropertyChanged
{
private bool _displayRaw;
public bool DisplayRaw
{
get { return _displayRaw; }
set
{
_displayRaw = value;
OnPropertyChanged(new PropertyChangedEventArgs("DisplayRaw"));
}
}
private bool _enableFit;
public bool EnableFit
{
get { return _enableFit; }
set
{
_enableFit = value;
OnPropertyChanged(new PropertyChangedEventArgs("EnableFit"));
}
}
public ZoomGraphControlValues()
{}
public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChanged(PropertyChangedEventArgs e)
{
if (PropertyChanged != null)
PropertyChanged(this, e);
}
}
Here is the dependency property:
public ZoomGraphControlValues ControlValues
{
get { return (ZoomGraphControlValues)GetValue(ControlValuesProperty); }
set { SetValue(ControlValuesProperty, value); }
}
public static readonly DependencyProperty ControlValuesProperty =
DependencyProperty.Register("ControlValues", typeof(ZoomGraphControlValues), typeof(ZoomGraphControls), new PropertyMetadata(null, OnControlValuesPropertyChanged));
private static void OnControlValuesPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var myObj = d as ZoomGraphControls;
myObj.OnControlValuesPropertyChanged(e);
}
private void OnControlValuesPropertyChanged(DependencyPropertyChangedEventArgs e)
{
if (ControlValues != null)
{
IniValues();
}
}
Then I have the display user control. This control also implements a dependency property of the same type as the other control and I want this control to be the target of the binding, so that when I change values in the parameter control, this control reflect the changes.
Here is the dependency property of this control:
public ZoomGraphControlValues ZoomGraphControlValues
{
get { return (ZoomGraphControlValues)GetValue(ZoomGraphControlValuesProperty); }
set { SetValue(ZoomGraphControlValuesProperty, value); }
}
public static readonly DependencyProperty ZoomGraphControlValuesProperty =
DependencyProperty.Register("ZoomGraphControlValues", typeof(ZoomGraphControlValues), typeof(zoomGraph), new PropertyMetadata(null, OnZoomGraphControlValuesPropertyChanged));
private static void OnZoomGraphControlValuesPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var myObj = d as zoomGraph;
myObj.OnZoomGraphControlValuesPropertyChanged(e);
}
private void OnZoomGraphControlValuesPropertyChanged(DependencyPropertyChangedEventArgs e)
{
if (ZoomGraphControlValues != null)
{
// update the control with the new parameters
ShowRawData(ZoomGraphControlValues.DisplayRaw);
SetChartBehabiour();
}
}
The Parameters control is initialized since the beginning of the application cycle. The display control gets created as per user request into a tab, so I have to create the control programmatically and thereby the binding as well:
//create the tab and wire tab events
//…
//create a display control
var graph = new zoomGraph();
// initialize the parameters class
var zgcv = new ZoomGraphControlValues
{
DisplayRaw = true,
ChartBehaviour = ChartBehaviour.Zoom
};
//assign the parameters class to the parameters user control dependency property
ZoomGraphControlsControl.ControlValues = zgcv;
//create the binding of the parameter control to the display control by linking their respective dependency properties
var zoomGraphControlValuesBinding = new Binding();
zoomGraphControlValuesBinding.Source = ZoomGraphControlsControl;
zoomGraphControlValuesBinding.Path = new PropertyPath("ControlValues");
zoomGraphControlValuesBinding.Mode = BindingMode.TwoWay;
zoomGraphControlValuesBinding.UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged;
zoomGraphControlValuesBinding.NotifyOnSourceUpdated = true;
zoomGraphControlValuesBinding.NotifyOnTargetUpdated = true;
graph.SetBinding(zoomGraph.ZoomGraphControlValuesProperty, zoomGraphControlValuesBinding);
//…
// add the user control to a tab
When I change a parameter in the parameter control I can see that it tries to fire the OnPropertyChanged event but it is always null. Because of this I think I am lacking something.
You are setting the binding mode to "OneWay" which means the view model will never get updated when the value changes in the view. Change the Binding mode to "TwoWay" and try again.
Also, check if you are changing the complete instance of "DisplayControlValues" or just properties on that class, because your binding is only set to fire when the entire instance changes, not its properties.
In addition to that, keep in mind that you can bind properties of two different controls using the Binding.ElementName property, which would make it unnecessary for you to create a view model, unless there is anything in the code behind you need to do when these values change.
If you post more code and XAML it will be easier to find the most appropriate way to solve your issue.

Co-opting Binding to listen to PropertyChanged events without a FrameworkElement

I have some nested view models that implement INotifyPropertyChanged. I'd like to bind an event listener to a nested property path (e.g. "Parent.Child.Name"), much like FrameworkElement dependency properties can be bound to arbitrary nested properties.
However, I just want something like a PropertyChanged event listener -- I don't actually have any UI element I'd like to bind. Is there any way to use the existing framework to set up such an event source? Ideally, I shouldn't need to modify my view model classes (as this is not required for regular data binding in Silverlight).
You can certainly co-opt the binding/dependency-property infrastructure to listen for changes to a nested property. The code below is WPF but I believe you can do something similar in Silverlight:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
this.DataContext = new Parent { Child = new Child { Name = "Bob" } };
this.SetBinding(ChildNameProperty, new Binding("Child.Name"));
}
public string ChildName
{
get { return (string)GetValue(ChildNameProperty); }
set { SetValue(ChildNameProperty, value); }
}
// Using a DependencyProperty as the backing store for ChildName. This enables animation, styling, binding, etc...
public static readonly DependencyProperty ChildNameProperty =
DependencyProperty.Register("ChildName", typeof(string), typeof(MainWindow), new UIPropertyMetadata(ChildNameChanged));
static void ChildNameChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
MessageBox.Show("Child name is now " + e.NewValue);
}
}
So I've defined my own DependencyProperty, not part of any UI per se (just the MainWindow class), and bound "Child.Name" to it directly. I'm then able to be notified when Child.Name changes.
Will that work for you?

wpf how to tell when databinding has finished?

I've got a custom control which has a DependencyProperty MyAnimal - I'm binding an Animal Property on my ViewModel to the MyAnimal DependencyProperty.
I've stuck a TextBox on the Control so I can trigger an Event - whenever I trigger the event the MyAnimal property has been set - however if I put a break point on the Setter of the MyAnimal property it never gets fired!
I guess I'm missing something fundamental about WPF Dependency Properties/Binding?!
And so my question is, if I can't use the Setter how can I find out when its been set? If I put if I put a break point after InitializeComponent() its null and I had a look to see if theres an Event a can hook up to - DatabindingFinished or similar? but can't see what it would be ...
Can anyone assist please?
Thanks,
Andy
public partial class ControlStrip
{
public ControlStrip()
{
InitializeComponent();
}
public Animal MyAnimal
{
get
{
return (Animal)GetValue(MyAnimalProperty);
}
set
{
SetValue(MyAnimalProperty, value);
}
}
public static readonly DependencyProperty MyAnimalProperty =
DependencyProperty.RegisterAttached("MyAnimal", typeof (Animal), typeof (ControlStrip));
private void TextBox_TextChanged(object sender, System.Windows.Controls.TextChangedEventArgs e)
{
var myAnimal = MyAnimal;
MessageBox.Show(myAnimal.Name);
}
}
The setter methods are never called by the runtime. They go directly to the DependencyProperty. You will need to add an additional argument to your call to RegisterAttached(). There you can add a PropertyChangedCallback.
Here is some sample code:
public static readonly DependencyProperty MyAnimalProperty =
DependencyProperty.RegisterAttached("MyAnimal", typeof (Animal), typeof (ControlStrip), new PropertyMetadata(AnimalChanged));
private static void AnimalChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e)
{
// Do work here
}
The setter is only there for your use - you actually can leave the property off entirely, since DataBinding uses the actual DependencyProperty itself, not the CLR property.
If you need to see when the property changes, you will need to specify PropertyMetadata on your dependency property, and provide a PropertyChangedCallback.
For details, I recommend reading Dependency Property Metadata.

Resources