Silverlight Behavior: DataContext of AssociatedObject changes - silverlight

I've written an Interactivity Behavior (from Blend SDK) , which can be attached to a DataGrid, and does some magic with the DataGrid's columns based on the ViewModel in the DataContext of the DataGrid.
Since the DataContext can be set later, I have to listen for DataContext changes in the behavior. So, I've bound a DependencyProperty to the Associated DataGrid's DataContext, like this:
BindingOperations.SetBinding(this, SourceProperty, new Binding("DataContext") { Source = AssociatedObject });
This line is hit, so the binding does happen.
Now the tricky part:
if I call
datagrid.DataContext = new MyViewModel();
everything works perfectly. But, if the datagrid is contained in some UserControl (not necessarily its immediate child) and I want to call
this.DataContext = new MyViewModel();
the callback of the Source property DOESN'T fire. I debugged it, the datagrid.DataContext is set, so the DataContext is inherited through the visual tree, as it should be, if I manually call update on the behavior, it does see the DataContext, but nothing happens automatically.
I don't want to name the DataGrid instance, I don't want to name the behavior, since there can be any number of those in one UserControl, I want to set the UserControl's DataContext and let the DependencyProperty system work its magic.
What am I doing wrong?

Have you tried something simpler:-
BindingOperations.SetBinding(this, SourceProperty, new Binding());
This should give you the DataContext object. A binding without a Path returns the source object. A binding without an explicit Source returns the current DataContext.
The question is does does the DataContext of this (the behaviour) aquire its value from the DataGrid to which its attached? I think it probably does.

Related

Custom DependencyProperty not updating external view model

I have a UserControl that has a DataGrid in it filled with members. The DataGrid.ItemsSource is bound to an ObservableCollection on the model. The DataGrid.SelectedItem is bound to the SelectedMember field on the model. The SelectedMember._set calls NotifyPropertyChanged and the event calls SetValue() for the exposed DependencyProperty.
This UserControl is on a page. That page has a viewmodel too. I'm trying to bind the UserControl.CurrentMember to the viewmodel.SelectedMember but it's not changing. I can bind the CurrentMember.MemberName to a textbox and the box fills with the member name so it looks like the UserControl is exposing the DependencyProperty correctly. But if I bind to the model it doesn't update.
I can't find any cross bindings. The bind to the TextBox works fine. The field on the page model is new so there's nothing bound to it.
What could be the problem? Does the field on the page model need to be a DependencyProperty? The compiler would give me an error if that were the case.
I'll try and get a code sample but it's so ingrained I can't just post a couple of lines of code.
Tom P.
After combing the code and trying to replicate the problem in a new project I found the problem.
In the UserControl I set the DataContext to the Model. But the UserControl.DataContext gets overwritten when I put it on the page. What i needed to do was name the MainGrid and set the DataContext of the MainGrid to the UserControlModel. MainGrid, being private to the UserControl, won't get overwritten. Now it works wonderfully.

Updating source using DependencyProperty in custom usercontrol

I got a binding source not being updated when the targeted DependencyProperty of a custom UserControl changes.
The source is a ViewModel loaded with MEF into the DataContext of my custom UserControl.
My binding looks like this in the root XAML of MyUserControl
// MyUserControl.xaml
<MyUserControlBase x:Class="MyUserControl" MyDependencyProperty="{Binding ViewModelProperty}">
If i use the MyDependencyProperty with a FrameworkPropertyMetadata and use the callback function when the property changes i can do the following which works fine
// MyUserControlBase.cs
private static void MyDependencyProperty_Changed(DependencyObject depObj, DependencyPropertyChangedEventArgs args)
{
var filter = (UserControl )depObj;
var vm = (ViewModel)filter.DataContext;
vm.ViewModelProperty= (VMPropType)args.NewValue;
}
Registration of the DependencyProperty in MyUserControlBase which inherit UserControl.
// MyUserControlBase.cs
public static readonly DependencyProperty MyDependencyPropertyProperty = DependencyProperty.Register("MyDependencyProperty",
typeof(VMPropType),
typeof(MyUserControlBase),
new FrameworkPropertyMetadata(null, MyDependencyProperty_Changed));
But why can't i get the simple two way binding to work when done in the XAML?
Output window show no binding failurs. I can see the ViewModel.ViewModelProperty getter is being called. Just not the setter once MyDependencyProperty changes.
It don't appear to be a problem with the ordering of events. The ViewModel is created long before DependencyProperty is changed for the first time.
I've also looked at Setting up binding to a custom DependencyProperty inside a WPF user control
But his problem seems slightly different since i actualy inherit from my own Base class which holds the DependencyProperty.
Thank you for any help.
Edit: with link to example solution.
Solution zip here
Sorry for the scary looking link, I don't know alot of quick fileshareing sites. If its bad post comment and I will remove it asap, but it seems ok.
Answer updated / irrelevant info deleted
After looking at the code in your sample I saw what the problem is. You have this "selector" control. In the BindDependencies method you set a binding on the MyControlBase.MyDependencyProperty. But at the same time you also bind this property to your control's datacontext (in MyUserControl.xaml). Effectively this means that in the BindDependencies method you are overwriting the binding to your viewmodel thus it is not active after that method - you can see this yourself by breaking into before and after the SetBinding call and calling BindingOperations.GetBindingExpression(control, MyUserControlBase.MyPropertyDependencyProperty) - the bindings are different. This is why your code does not work. I guess you will have to find another way of transferring the value there :)
Some additional info
In WPF a property can be a target of only one binding. This is actually very logical - if a property is bound to two sources - which one should it use? The fact that the binding is marked as OneWayToSource does not really matter in this situation as it still counts as a binding that targets the control's property.
One way you can investigate and see if it works for you is the MultiBinding - it does allow to bind the control's property to several sources by implementing your own IMultiValueConverter. You can find more info on this on MSDN in the link provided.
Do you expose the ViewModelProperty as a regular property..
Usually you do the following....
public static readonly DependencyProperty ViewModelPropertyProperty = DependencyProperty.Register("ViewModelProperty",
typeof(VMPropType),
typeof(MyUserControlBase),
new FrameworkPropertyMetadata(null, ViewModelProperty_Changed));
public VMPropType ViewModelProperty {
get { return (VMPropType)GetValue(ViewModelPropertyProperty); }
set { SetValue(ViewModelPropertyProperty,value); }
}
EDIT -- After comment.
When are you checking that the property has changed? By default WPF bindings only update the values when they lose focus, you change change the UpdateSourceTrigger property of the binding to alter this behaviour.

Get the value of an AttachedProperty of a UI item not (yet) displayed

I'm trying to do the following using MVVM, Binding and AttachedProperty
Create a ViewObj (System.Windows.Controls.Control derived class)
The ViewObj has 1 AttachedProperty named "Order" (OrderProperty) declared in a class named View.
The attached property is bound on a property of the ViewModel in the xaml
Create the ViewModel
Set the ViewModel as DataContext of the ViewObj
Before the ViewObj is displayed/rendered/etc.
Get the order in code doing var order = View.GetOrder(ViewObj)
The ViewObj is displayed and is showing the bound value ...
If the AttachedProperty is a value and not a binding expression, the value returned by View.GetOrder(ViewObj) is the good one and not the default one.
Any ideas?
EDIT:
I forced the databinding expression to be evaluated using the BindingExpression class. I discovered that the BindingExpression.Status was set to Unattached which seems to explain why it is not working.
I think the binding is attached when the element is attached to the visual tree.
But ... that do not help me a lot with my problem ...
I discovered that (in my case at least), the Binding was Unattached, but the DataContext was set.
So I decided to get the DataContext (the ViewModel) and to work with it.
Any others suggestions are welcome.

DataBinding Text Property of UserControl to ObservableCollection does not update

I have created a UserControl, which is to display a converted string value based on the contents of a bound ObservableCollection. Everything works when the application loads; my IValueConverter is called and produces the correct string result, which is displayed correctly in my UserControl. However if the ObservableCollection contents change, my control is not updated.
Also, before I created this control, I had the same behaviour, but binding the Content property of a regular Button control, and this also worked correctly and updated as expected.
Any ideas what I am missing to get the same thing with my UserControl?
The control property looks like;
public static readonly DependencyProperty TextProperty =
DependencyProperty.Register("Text", typeof(string), typeof(MyUserControl));
public string Text
{
get { return GetValue(TextProperty) as string; }
set { SetValue(TextProperty, value);
}
The relevant section in the UserControl XAML (which displays the converted string value) is;
<TextBlock Text="{Binding RelativeSource={RelativeSource AncestorType={x:Type Controls:MyUserControl}}, Path=Text}" />
And the control is created in a separate Window like so;
<CoreControls:MyUserControl
Name="myControl"
Text="{Binding Path=ObservableCollectionInstance, Converter={StaticResource MyValueConverter}, Mode=OneWay}" />
I would use ElementName instead of RelativeSource in your binding, since you have named your user control. Also, you are trying to bind a collection to a <Textbox>. a <Textbox> is designed to display a single item. this is probably why its not working. ObservableCollection fires CollectionChanged events, not PropertyChanged. Even if it did respond, you are still going to have problems because ObservableCollection does not notify when an item contained in it has property changes--only when items are added/removed etc (think, the collection itself changes). If this is the behavior you want, you are going to have to write some code.
EDIT
after your comments, it sounds to me like even though you set it to OneWay binding mode, its acting like OneTime binding mode.
I would try this to help you debug it:
add this xmlns:
xmlns:diagnostics="clr-namespace:System.Diagnostics;assembly=WindowsBase"
and then, in your binding add this:
diagnostics:PresentationTraceSources.TraceLevel=High
here is an article on debugging bindings.
the other thing you could do is set breakpoints in your converter. see if its actually updating when you add/remove things to your collection. I would be willing to bet that its bc the ObservableCollection is NOT firing PropertyChanged events and that the initial update occurs because its not based on an update event.
ObservableCollection notifies only in case if items get added or removed. It is used to observe a collection. They are more suited for content controls. Read about it here. You are talking about observing a property, which needs INotifyPropertyChanged. Posting more code might help, like how are you changing the value of the collection.
Thanks for the tips guys.
I managed to work out a solution; I can handle the CollectionChanged event on the ObservableCollection and then explicitly update the target with something like;
BindingExpression exp = myControl.GetBindingExpression(MyUserControl.TextProperty);
if (null != exp) exp.UpdateTarget();
As noted, most likely, binding on the Text property is only listening to PropertyChanged events, not NotifyCollectionChanged events, but this solution does the trick.

Update all bindings in UserControl at once

I need to update all the bindings on my UserControl when its visibility changes to Visible. Pretty much all my bindings are bound to the DataContext property of the user control so I'm trying to update the target of that binding:
BindingOperations.GetBindingExpressionBase(this, UserControl.DataContextProperty).UpdateTarget();
But I get null as the result of GetBindingExpression(..) method and I'm wondering if I'm using this wrong.
Also, is there any other good way to refresh all bindings on the control (which use DataContext as the source).
Well, you could just re-assign the DataContext:
var dataContext = DataContext;
DataContext = null;
DataContext = dataContext;
FYI, resetting the property to its value (i.e.DataContext = DataContext) won't work.
You're using the BindingOperations.GetBindingExpressionBase method on the wrong property. You have to use it on the properties which are binding to the DataContext property, not the DataContext property itself.

Resources