Default settings for bound WPF DependencyProperty - c#

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.

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);
}

WPF Binding not updating with UpdateSourceTrigger=PropertyChanged

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.

bind CRL wrapper or bind direct dependency property on XAML element

I have simple depedency property in window.
public static readonly DependencyProperty UserLastNameProperty =
DependencyProperty.Register("UserLastName",
typeof (string),
typeof (MainWindow),
new FrameworkPropertyMetadata(default(string),
FrameworkPropertyMetadataOptions.BindsTwoWayByDefault));
public string UserLastName
{
get
{
return (string) GetValue(UserLastNameProperty);
}
set
{
SetValue(UserLastNameProperty, value);
}
}
When I bind direct depedency property on textBox binding doesn’t work.
<TextBox Margin="4" FontSize="14" x:Name="TxbLastName" MinWidth="200"
Text="{Binding UserLastNameProperty, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
But when I bind CLR prop wrapper on textBox binding works.
<TextBox Margin="4" FontSize="14" x:Name="TxbLastName" MinWidth="200"
Text="{Binding UserLastName, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
Why I can’t bind direct depedency property on textBox?
You are confusing static DependencyPropertyIdentifier with instance CLR wrapper for that property.
DependencyPropertyIdentifier is static field which is register at class level and embed in class metadata. Whereas to fetch and set the value for an instance, GetValue() and SetValue() is called on that DP identifier.
From MSDN -
Dependency property identifier: A DependencyProperty instance, which is obtained as a return value when registering a dependency
property, and then stored as a static member of a class. This
identifier is used as a parameter for many of the APIs that interact
with the WPF property system.
CLR "wrapper": The actual get and set implementations for the property. These implementations incorporate the dependency property
identifier by using it in the GetValue and SetValue calls, thus
providing the backing for the property using the WPF property system.
Dependency properties on a given type are accessible as a storage table through the property system. Instance value is stored in that storage table and WPF implementation of XAML processor uses that table to get and set value for an instance object.
I would suggest you to read more about it here and here.

Why does Run.Text bind two-way by default?

In .NET 4.0, Run.Text is bindable. So I tried to bind it:
<Run Text="{Binding DisplayText}"/>
But when I ran, I got an error: "A TwoWay or OneWayToSource binding cannot work on the read-only property 'DisplayText' of type 'SomeNamespace.SomeClass'."
My DisplayText property was indeed read-only, but so is a Run -- Runs go in in TextBlocks, which you can't edit. So why would I be getting this error? I dug into PresentationFramework with dotPeek and sure enough:
public static readonly DependencyProperty TextProperty =
DependencyProperty.Register("Text", typeof (string), typeof (Run),
(PropertyMetadata) new FrameworkPropertyMetadata((object) string.Empty,
FrameworkPropertyMetadataOptions.BindsTwoWayByDefault,
new PropertyChangedCallback(Run.OnTextPropertyChanged),
new CoerceValueCallback(Run.CoerceText)));
The fourth line, plain as day, specifies that Run.Text should bind two-way by default, which makes no sense and seems like a glaring design bug.
Of course, this is easy enough to work around:
<Run Text="{Binding DisplayText, Mode=OneWay}"/>
But why should I have to work around it? Why does Run bind two-way by default?
Just a guess here:
It might be because Run objects are also used in the RichTextBox control, and I can imagine this control might want to Bind TwoWay by default!

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

Categories