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?
CodePudding user response:
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.
CodePudding user response:
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);
}