Yet again, I produced too much text than I intended for a short response on StackOverflow.. If somebody is interested in, the question came from StackOverflow: Dynamic Conditional Formatting in WPF.
Let's start with WPF support for "Attached properties". You can literally attach them to anything you like. A Grid.Row
is example of such attached property.
Once you create a couple of your att.props, you observe and bind them, and also bind to them as normal:
<Foo x:Name="foo" myns:MyExtension.MyAttachedProperty1="SomeSource.AProperty" />
<Bar myns:MyExtension.MyAttachedProperty2="{Binding Path=(myns:MyExtension.MyAttachedProperty1).Width, ElementName=foo }" />
note that when binding to an attached property, you must use parenthesis in the property path, or else the MyExtension
class name will be treated as a source instead of as a classname-prefix. You may also need namespace prefixes (xmlns:my=...
+ my:
everywhere)
Once you learn/master attached properties, things start to be fun! Since you can attach them and bind on almost anything, you can introduce smart extensions like:
<TextBlock m:MyEx.UnitSystem="{Binding ..}" m:MyEx.SourceValue="{Binding ..}" />
Note that TextBlock.Text
is not bound. The idea behind it is that SourceValue
attached property gets the raw value to be displayed, and its change handlers observe the changes to both DisplayedValue and UnitSystem, and they translate the values and set the Text on the component. Not much suprising.
But the suprising fact that is easily omitted is, that your code will be totally decoupled. You will have a source-of-value, source-of-unitsystem, both just pulled from datasource. Your calculations will just emit the value. And yet another class/file will define the plugin that handles the conversions and updates the TextBlock.
But, ok, so we have the source-value bindings attached like above. Where to put the actual code that handles the changes? Of course, you can put that right into these attached dependency properties, just attach uimetadata with a change-handler and done. Since every change-handler receives the originating DependencyObject=TextBlock, those change handlers will be able to update the Text.
But it'll get really messy once you need to observe two, three or more source properties, because every one of those will need to be really carefully tracked.
So, here's what I like to do in such cases:
<TextBlock my:MyEx.UnitSystem="{Binding ..}"
my:MyEx.SourceValue="{Binding ..}"
Text="{Binding (my:MyEx.DisplayedValue), RelativeSource={RelativeSource Self}}">
<my:AutoUnitConversion />
</TextBlock>
The AutoUnitConversion is an AttachedBehavior that after attaching to TextBox observes changes to UnitSystem and SourceValue and calculates the displayed value. The behavior could directly set the Text of the TextBlock, but then, it would be usable only with TextBlocks. So, instead, I'd make it emit the calculated value as a third property, DisplayedValue. Note the usage of RelativeSource=self, since the output attached property is set right on the parent component. In this setup, you may then directly reuse the AttachedBehavior in other places - just the final Text/Value/etc binding will change.
I think you see now how powerful it can be. It's more complex than styles, bindings and triggers, but on the other hand it allows you to literally attach any logic and any behavior. While bindings and conditions give you some sort of a language to set the rules, it's sometimes simply not enough, or sometimes it actually gets overcomplicated in XAML and writing the same logic by C# or VB is much simpler than via multibindings, converters and conditions. In such case, Behaviors are great!
..but this hint would not be complete without mentioning that Behaviors are really more complex, as you often need to set up or observe some bindings from the Code, not XAML, and also you need to remember about Attach/Detach lifecycle, and also they put some additional overhead to whole system as they introduce more properties/values to be tracked by WPF. It's really hard to tell if it overhead is higher or lower than MultiBindings and lots of Converters. I'd guess it's actually lower, but I have not measured that.
By the way, I almost forgot to add the link: here's a very quick overview about AttachedBehaviors. Please note that this term is used for two things: 'quick&dirty' behaviors registerd through setting a AttachedDependencyProperty, and 'fully-fledged' behaviors registered by XAML tags ("Blend Behaviors", named after MS ExpressionBlend). They look a bit different, but the idea of operation is mostly the same. While creating and using the latter ones is a bit more involving, they tend to be structured better, easier&more reusable than the former, and they come with handy Attach/Detach methods. It's good to familiarize yourself with both types!