WPF Binding not updating with UpdateSourceTrigger=PropertyChanged - c#

I have a TabControl in which I set the DataContext to an instance of the this class, It's basicly a wrapper for DependencyProperties of a static class with the same properties.
In my Markup I set the DataContext like this
<TabControl DataContext="{Binding ElementName=self, Path=Settings}">
and binding to the property within the TabControl like this
<TextBox Text="{Binding Path=Url, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
However, this does not lead to any updates of the source when the content of the TextBox is changed. I can change the content of the TextBox, let it loose focus etc. it does just not update the source.

Url is a dependency property and when set from XAML, wrapper property setter won't be called.
From MSDN:
The current WPF implementation of its XAML processor is inherently
dependency property aware. The WPF XAML processor uses property system
methods for dependency properties when loading binary XAML and
processing attributes that are dependency properties. This effectively
bypasses the property wrappers. When you implement custom dependency
properties, you must account for this behavior and should avoid
placing any other code in your property wrapper other than the
property system methods GetValue and SetValue.
In case you want to do something on its property changed you should provide PropertyChangedCallback and write code there.
You can refer to the sample here in case PropertyChangedCallback is new to you. Something like:
public static readonly DependencyProperty UrlProperty =
DependencyProperty.Register(
"Url",
typeof(string),
typeof(SettingsWrapper),
new PropertyMetadata(OnUrlChanged)
)
);
private static void OnUrlChanged(DependencyObject d,
DependencyPropertyChangedEventArgs e)
{
SettingsWrapper instance = (SettingsWrapper)d;
instance.Settings.Url = e.NewValue.ToString();
}

You said in a (now deleted) comment that your Window has x:Name="self", however the Window class does not have a property called Settings.
If this is an attached property, you need to reference it by the attached property by the full name, and wrap it in parenthesis.
For example,
<TabControl DataContext="{Binding ElementName=self, Path=(local:MyClass.Settings)}">
See WPF Attached Property Data Binding for more info.

Related

Set NotifyOnTargetUpdated for existing binding

I have a binding in xaml <TextBlock Style="{StaticResource textStyle}" Text="{Binding DisplayText}" />.
I am attempting to write an attached behavior that reacts to the bound DisplayText value changing. If I specify NotifyOnTargetUpdated=True in the xaml, I can react to the change within the behavior and everything is fine, but I'd rather not depend on binding the Text property in a specific way just to make the behavior work.
My thought was to change the NotifyOnTargetUpdated value on the existing TextBlock.TextProperty binding when the behavior is opted in. I am using the below code to do so, where tb is the TextBlock being opted in.
var textBinding = BindingOperations.GetBinding(tb, TextBlock.TextProperty);
textBinding.NotifyOnTargetUpdated = true;
tb.SetBinding(TextBlock.TextProperty, textBinding);
The behavior is opted in like so, in the style:
<Setter Property="behaviors:Text.AutoSizeText" Value="True"/>
Initially this didn't work because textBinding was null. I can get around this by binding the Text property in xaml before the behavior property, but this still leaves an external dependency that I don't like (xaml ordering). If I do go this route, I get the below exception, which seems to indicate that I can't accomplish this in this way, at all.
InvalidOperationException: Binding cannot be changed after it has been used.
So then, how can I go about automatically handling setting NotifyOnTargetUpdated for the Text binding when the behavior is opted in?
I was able to solve my problem thanks to direction provided by #canton7. I was originally (as is often the case) looking for the way to implement my imagined solution, rather than a solution that fit my need. After adjusting my outlook, my working solution is thus:
Add the AttachedProperty InternalText to the behavior class, with a property changed handler.
private static readonly DependencyProperty InternalTextProperty = DependencyProperty.RegisterAttached(
"InternalText", typeof(string), typeof(Text), new PropertyMetadata(default(string), HandleInternalTextChanged));
In the changed handler (HandleInternalTextChanged above) do the work that I would have done in a TargetUpdated handler if my original idea to set NotifyOnTargetUpdated had worked out.
On opt-in to my behavior, create a binding from the opted-in TextBlock.Text to the InternalText attached property.
var internalBinding = new Binding { Source = tb, Path = new PropertyPath(TextBlock.TextProperty) };
tb.SetBinding(InternalTextProperty, internalBinding);
The HandleInternalTextChanged callback on InternalTextProperty allows me to work around being unable to change the NotifyOnTargetUpdated value by providing an alternate means of notifying on each change.
I prefer to bind internally to DisplayText because I'd prefer to bind to the source, if possible, rather than daisy-chain through the TextBlock.Text property.
If you need a binding to a source property, then it is created in a slightly different way.
var binding = BindingOperations.GetBindingBase(tb, TextBox.TextProperty);
if (binding == null)
{
tb.ClearValue(InternalTextProperty);
}
else
{
tb.SetBinding(InternalTextProperty, binding);
}

Dependency Property Datacontext

I have a usercontrol, and there is a Datacontext set for it. This usercontrol contains also a Dependency-Property. Now, i want simply bind to this property.
I think the problem has something to do with the wrong datacontext.
The dependency-Property in my usercontrol (called TimePicker) looks like this:
public TimeSpan Time
{
get { return (TimeSpan)GetValue(TimeProperty); }
set
{
SetValue(TimeProperty, value);
OnPropertyChanged();
}
}
public static readonly DependencyProperty TimeProperty = DependencyProperty.Register("Time", typeof (TimeSpan), typeof (TimePicker));
I try to use it like this:
<upDownControlDevelopement:TimePicker Grid.Row="1" Time="{Binding Path=TimeValue}" />
When i do this i get the following binding error:
System.Windows.Data Error: 40 : BindingExpression path error: 'TimeValue' property not found on 'object' ''TimePicker' (Name='TimePickerControl')'. BindingExpression:Path=TimeValue; DataItem='TimePicker' (Name='TimePickerControl'); target element is 'TimePicker' (Name='TimePickerControl'); target property is 'Time' (type 'TimeSpan')
Any help would be highly appreciated
Greetings Michael
PS: you can download the code at here
Although this has now been solved there seems to be some, in my opinion, inappropriate use of the DataContext.
When developing a custom reusable control, you should not set DataContext at all. What the DataContext will be, that is for the user of the control to decide, not for the developer. Consider the following common pattern of code:
<Grid DataContext="{Binding Data}">
<TextBox Text="{Binding TextValue1}" />
<!-- Some more controls -->
</Grid>
Notice that here, you are using the Grid control. The developer of the control (in this case, the WPF team), didn't touch the DataContext at all - that is up to you. What does it mean for you as a control developer? Your DependencyProperty definition is fine, but you shouldn't touch the DataContext. How will you then bind something inside your control to the DependencyProperty value? A good way is using a template (namespaces omitted):
<MyTimePicker>
<MyTimePicker.Template>
<ControlTemplate TargetType="MyTimePicker">
<!-- Stuff in your control -->
<TextBlock Text="{TemplateBinding Time}" />
<TextBox Text="{Binding Time, RelativeSource={RelativeSource TemplatedParent}}" />
</ControlTemplate>
<MyTimePicker.Template>
</MyTimePicker>
Note that TemplateBinding is always one-way only, so if you need any editing at all, you need to use normal binding (as you can see on the TextBox in the example).
This only means that the TextBlock/Box inside your control will get its Time value from your custom control itself, ignoring any DataContext you might have set.
Then, when you use the control, you do it like this (added to my first example):
<Grid DataContext="{Binding Data}">
<TextBox Text="{Binding TextValue1}" />
<!-- Some more controls -->
<MyTimePicker Time="{Binding TimeValue}" />
</Grid>
What just happened here is that the MyTimePicker does not have DataContext set anywhere at all - it gets it from the parent control (the Grid). So the value goes like this: Data-->(binding)-->MyTimePicker.Time-->(template binding)-->TextBlock.Text.
And above all, avoid doing this in the constructor of your custom control:
public MyTimePicker()
{
InitializeComponent();
DataContext = this;
}
This will override any DataContext set in XAML, which will make binding a huge pain (because you'll have to always set Source manually). The previous example would not work, and this wouldn't work either:
<MyTimePicker DataContext="{Binding Data}" Time="{Binding TimeValue}" />
You would think this is OK, but the DataContext will be resolved in the InitializeComponent() call, so the value will be immediately overwritten. So the binding to TimeValue will look for it in the control instead (which will, of course, fail).
Just don't touch the DataContext when developing a control and you'll be fine.
You don't need to override the data context of user control. You can use RelativeSource to point your binding source property i.e. TimeValue to any other source you like. E.g. If you have the source property in your window's class. You could simply point your binding target to the source in window's data context as follows:
{Binding Path=DataContext.TimeValue, RelativeSource={ RelativeSource AncestorType={x:Type Window}}}
Your error states that 'TimeValue' property not found on 'object' 'TimePicker', which means that the WPF Framework is looking at the 'TimePicker' object to resolve the 'TimeValue' property value. You must have somehow set the DataContext of the Window or UserControl that contains the 'TimePicker' object to an instance of the 'TimePicker' object.
Instead, it should be set to an instance of the class that declares the 'TimeValue' property. If you're using a view model, then you should set it to an instance of that:
DataContext = new YourViewModel();
If the 'TimeValue' property is declared in the Window or UserControl then you can set the DataContext to itself (although generally not recommended):
DataContext = this;
Please note that when data binding to the 'Time' property from inside your TimePicker control, you should use a RelativeSource Binding:
<TextBlock Text="{Binding Time, RelativeSource={RelativeSource
AncestorType={x:Type YourLocalPrefix:TimePicker}}}" ... />
Normally we are not setting datacontext directly.If u want to set datacontext create an instance of your usercontrol and set datacontext individually to each one.

Dependency Property on a usercontrol that is bound to another VM

I've run into the following problem:
I'm currently creating an on screen keyboard that is a usercontrol that has its own viewmodel.
<UserControl.DataContext>
<Binding Source="{StaticResource Locator}" Path="AlphaNumericKeyboard" />
</UserControl.DataContext>
I'm attempting to add a dependency property called KeyboardAlphaMode that can be toggled by other view models that are using this usercontrol
public static readonly DependencyProperty KeyboardAlphaModeProperty = DependencyProperty.Register("KeyboardAlphaMode",
typeof(UIKeyboardAlphaMode), typeof(AlphaNumericKeyboardView),
new FrameworkPropertyMetadata(UIKeyboardAlphaMode.LowerCase, new PropertyChangedCallback(KeyboardAlphaModeCallBack)));
private static void KeyboardAlphaModeCallBack(DependencyObject d, DependencyPropertyChangedEventArgs e){ ... }
But, when I attempt to bind to this property from another view, the callback was never fired ..
<k:AlphaNumericKeyboardView x:Name="alphaNumericKeyboard" KeyboardAlphaMode="{Binding KeyboardAlphaMode, UpdateSourceTrigger=PropertyChanged}">
</k:AlphaNumericKeyboardView>
What am I missing here? a setter? trigger?
Or this is just a thought, can a usercontrol that has dependency be bound to a viewmodel? or does it have to be bound to itself?
Edit - 10/10/2014 # 1:31pm
After rethinking the entire solution i came up with the following scenario for my problem.
I binded the Dependency Property to the view's viewmodel and let the viewmodels interact with each other instead having other viewmodel talking to this specific view ...
Here's the code for that ..
Binding alphaModeBinding = new Binding("KeyboardAlphaMode")
{
Mode = BindingMode.TwoWay,
TargetNullValue = UIKeyboardAlphaMode.LowerCase,
FallbackValue = UIKeyboardAlphaMode.LowerCase
};
this.SetBinding(KeyboardAlphaModeProperty, alphaModeBinding);
I also made the dependency property protected so no one else can access it.
Unless there is a better way to track property changes, i'm sticking with this for now.
Again, not sure this is the best solution but it gets the job done.
Try Mode=TwoWay on the binding.

WPF, Setting property with single value on multiple subcontrols

I have a parent contentcontrol which displays data via a datatemplate. The datatemplate contains a stackpanel with several usercontols of the same type. I like to set the property only once on the parent control, it must set the value of the property on all the subcontrols. But if there is a way to do it on the stackpanel it's also OK. The template can be changed at runtime and the values need also to be propagated to the new template.
My current solution is to implement the property on both parent and subcontrol and use code to propagate the value from the parent to all the subcontrols. My question is: is there a better or other ways of doing this?
EDIT:
Some notes of clarification to my question. The application is currently WPF, but if it's portable to silverlight it would be a bonus. The property is a dependency of the type Style.
I want to use it to style part of the subcontrol. Currently the datatemplate is stored in a separate resource dictionary, so it can be reused. The visuals of the subcontrol are styled via controltemplate. The template contains three different controls, the first one is a label. The need (desire, foolish wish) is to set the style only once, to give the label on all the subcontrols in the datatemplate a consistent look and feel.
So the crux of the problem is to override the value of the a style dependency property on a subcontrol, stored in a resource dictionary from a container control. Both are custom user controls, so all options are open.
<Parent SubSubStyle="x" Template="template" />
<DataTemplate x:Key=template>
<StackPanel>
<Subcontrol SubSubStyle="?"/>
<Subcontrol SubSubStyle="?"/>
<Subcontrol SubSubStyle="?"/>
<Subcontrol SubSubStyle="?"/>
</StackPanel>
</DataTemplate>
Is the property that you're trying to set a DependencyProperty that you have created? If so, the ideal thing to do in WPF is to define the property such that it will be inherited by elements in the visual tree.
If it's not your own dependency property (or if you're using Silverlight which does not support this mechanism) then you should instead use implicit styles.
public class MyControl {
// be prepared for some dependency property hell below
// this defines a DependencyProperty whose value will be inherited
// by child elements in the visual tree that do not override
// the value. An example of such a property is the FontFamily
// property. You can set it on a parent element and it will be
// inherited by child elements that do not override it.
public static readonly DependencyProperty MyInheritedProperty =
DependencyProperty.Register(
"MyInherited",
typeof(string),
typeof(MyControl),
new FrameworkPropertyMetadata(
null,
FrameworkPropertyMetadataOptions.Inherits
)
);
}
Style is best option http://msdn.microsoft.com/en-us/library/ms745683.aspx#styling_basics

Default settings for bound WPF DependencyProperty

I've created a custom user control named MyCustomComboBox. Everywhere in the application I put it I do the following:
<Widgets:MyCustomComboBox
Foo="{Binding Foo,
UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}" />
MyCustomComboxBox has the dependency property Foo, I have some validation and other logic in the combobox which is the very reason why I wrapped it up in a custom control.
The custom combobox is included another user control which also has a Foo property, which the combobox's is bound to.
But I also have to set UpdateSourceTrigger and Mode, I would like to somehow specify that those are the default values when binding to that DependencyProperty. Can it be done?
The default BindingMode can be specified in the dependency property metadata:
public static readonly DependencyProperty FooProperty = DependencyProperty.Register(
"Foo",
typeof(string),
typeof(MyCustomComboBox),
new FrameworkPropertyMetadata(
null,
FrameworkPropertyMetadataOptions.BindsTwoWayByDefault);
However, to my knowledge there is no way to provide a default for the update source trigger.

Categories