WPF : An alternative way to specify a ValueConverter on binding - wpf

Most common way I encountered of specifying a value converter for a binding is to:
1. Create an instance of the value converter as a resource with a key.
2. Reference the instance using StaticResource markup extension:
<TextBlock Text="{Binding Converter={StaticResource myFormatter}" />
Q: Is there anything wrong with using static instance as follows:
<TextBlock Text="{Binding Path=Description, Converter={x:Static local:MyFormatter.Instance}}"/>
// where Instance is declared as:
public readonly static MyFormatter Instance = new MyFormatter();
In my case value converter is immutable.
Edit: another way is to turn the converter into an extension
so that you specify the converter using markup extension format:
<TextBlock Text="{Binding Converter={local:MyFormatter}}"/>

Technically it will be fine, but in practice I don't like it:
If you declare the converter as a resource, then you have a single point of reference. If you change the namespace or class name of the converter, then you only have a single place to update.
If you declare it as static, then you need to bring the clr-namespace in at the top of each and every xaml file that uses the converter. If you declare it as a resource, you don't.
{Binding Converter={StaticResource myFormatter} is much shorter and easier to read than the static one. In the long run, this will help you more than you might think.

As long as the formatter really has no state, this should be fine. It is not equivalent though. In the first case, you have an instance of the class for each instance of your XAML-based control. In the second, only one instance will ever be created.

Related

Difference between {x:Static} and {StaticResource} in XAML WPF

What is the difference between {x:Static} and {StaticResource} in the XAML code for WPF?
For example:
<StackPanel IsEnabled="{Binding Model.IsReadOnly, Converter={StaticResource BoolInverseConverter}}">
and
<StackPanel IsEnabled="{Binding Model.IsReadOnly, Converter={x:Static BoolInverseConverter}}">
And when should I use one or the other?
Both are markup extensions.
x:Static is used to reference a static attribute, which won't change during runtime. This applies to enumerations, constants, static properties but also fields.
This means your second code example is wrong as you can't reference a class but only a property. Given that BoolInverseConverter is a static property of the Converters class, the correct code would look like:
<StackPanel IsEnabled="{Binding Model.IsReadOnly, Converter={x:Static Converters.BoolInverseConverter}}">
StaticResource does not mean that the resource is static in terms of accessibility but rather in terms of resource lookup. It's a markup extension that instructs the XAML parser to lookup the resource tree to find a predefined instance.
Same does DynamicResource. The only difference is that StaticResource instructs the XAML parser to resolve the reference to a resource at compile time, whereas DynamicResource let's the XAML parser create an expression instead, that will be evaluated at run time (deferred). DynamicResource therefore resolves the resource at run time.
The following code snippet shows how the resource (the converter) is referenced via resource tree lookup by the XAML parser. Since the XAML markup extension StaticResource is used, the lookup is static. This means once the resource was found, it can't be replaced by another instance. And also if the instance is not defined during compile time, the application will crash, throwing a StaticResourceExtension. In case the resource behind the x:key changes during runtime, the references won't update (static):
<StackPanel IsEnabled="{Binding Model.IsReadOnly, Converter={StaticResource BoolInverseConverter}}">
The next code snippet again shows how the resource (the converter) is referenced via resource tree lookup by the XAML parser. But this time using the DynamicResource markup extension, so that the lookup becomes dynamic. This means the resource will be looked up during runtime. And everytime the resource of the specific x:Key changes e.g., a Brush, the referencing instance will update accordingly (dynamic):
<StackPanel IsEnabled="{Binding Model.IsReadOnly, Converter={DynamicResource BoolInverseConverter}}">
Another code snippet shows how to reference an instance, which is not a resource, but a static property defined in the Converters class, using the x:static markup extension:
<StackPanel IsEnabled="{Binding Model.IsReadOnly, Converter={x:Static Converters.BoolInverseConverter}}">
The last code snippet shows another use case of the x:Static markup extension. This time it's used to reference an enumeration value (enum) and sets the Visibility property to a value of the Visibility enumeration:
<StackPanel Visibility="{x:Static Visibility.Collapsed}">

Binding Source vs x:Static

in WPF one can bind to static properties. Now I know 2 ways of doing this:
Content="{x:Static stat:Statics.CurrentUser}"
Or:
Content="{Binding Source={x:Static stat:Statics.CurrentUser}}"
Are there any differences between these 2 methods?
Main difference in this case is that x:Static does not perform additional conversion
From x:Static Markup Extension
Use caution when you make x:Static references that are not directly the type of a property's value. In the XAML processing sequence, provided values from a markup extension do not invoke additional value conversion. This is true even if your x:Static reference creates a text string, and a value conversion for attribute values based on text string typically occurs either for that specific member or for any member values of the return type.
So lets say you do
<TextBlock Text="{x:Static SystemColors.ActiveBorderBrush}"/>
this will cause runtime error:
'#FFB4B4B4' is not a valid value for property 'Text'.
because SolidColorBrush is not String whilst
<TextBlock Text="{Binding Source={x:Static SystemColors.ActiveBorderBrush}}"/>
will work fine and display #FFB4B4B4 because it will perform ToString() conversion. Also without Binding you are not able to access instance properties of static object so for example you would not be able to get Color property of that brush
<TextBlock Text="{Binding Source={x:Static SystemColors.ActiveBorderBrush}, Path=Color}"/>

difficulty understanding and using xaml markup extentions

I am learning on to the concepts of WPF such as data binding, commands, resources, element bindings, styles etc, which use markup extensions extensively, and i am having problem understanding the meaning behind the Markup classes, as they are being used beyond what i understand they should be used for. So here are a few points i need to clear:
(all code snippets are from Pro WPF in C# 2010 book)
What is the meaning and use of Static extension? It can be used to
declare static resources, which can be declared in as
, but this xaml confuses me:
<Button ... Foreground="{x:Static SystemColors.ActiveCaptionBrush}" >
In {x:Static SystemColors.ActiveCaptionBrush}, what is the role
of static here, and what will change if i use x:Dynamic here? The
book says this xaml is equivalent to this codebehind:
cmdAnswer.Foreground = SystemColors.ActiveCaptionBrush;
This means that if i have a class with static properties, i should
be able to use something like this:
<Button ... Foreground="{x:Static MyClass.SomeStaticProperty}" >
But it didn't work, despite i had created a class, i tried using
local:Static (referring to the local namespace) but VisualStudio
didn't allow me to use it. What is the proper method of achieving
this?
What is the meaning of Binding (beyond obvious meaning, what is
happening when i am binding)? It is used for resource binding, or
data or element binding. I was able to understand element binding,
but binding to objects that are not elements caused problems. For
example:
<TextBlock Text="{Binding Source={x:Static SystemFonts.IconFontFamily},
Path=Source}"></TextBlock>
Here it is binding to the text to the SystemFonts.IconFontFamily
property, what is the use of x:static in this case, and how to bind
it to a property in a class that i have created? Also how to update
the text property of the textfield if the value of the binding
target changes? Due to binding, it should update by itself, is this
the case?
All the examples in the book make use of SystemFonts.IconFontFamily,
none that i have seen explains the use of such binding, and how to
do it for the classes that i create? Some help in this regard is
needed. I want to ask more about binding, but i will do so in a
separate question about binding only.
Finally, recommend a book or resource that explains what is
happening, instead of how to do this and that?
Answers....
1)
You said ...
... This means that if i have a class with static properties, i should be
able to use something like this:
<Button ... Foreground="{x:Static MyClass.SomeStaticProperty}" >
But it didn't work, despite i had created a class, i tried using
local:Static (referring to the local namespace) but VisualStudio
didn't allow me to use it. What is the proper method of achieving
this?
Well your trial attempt was correct but it was incorrect to what term you have applied that namespace token to.... local namespace token applies to the class that is declared under it so...
<Button ... Foreground="{x:Static local:MyClass.SomeStaticProperty}" >
Should work just fine provided that SomeStaticProperty is a valid Brush.
In this example, the whole markup was internally equivalent to Binding as ...
Binding.Source = {x:Type local:MyClass}
Binding.Path = SomeStaticProperty.
2)
You had an example...
<TextBlock Text="{Binding Source={x:Static SystemFonts.IconFontFamily},
Path=Source}">
</TextBlock>
So use the same equivalence from example 1 and apply it to this example...
<TextBlock Text="{Binding Source={x:Type SystemFonts},
Path=IconFontFamily.Source}">
</TextBlock>
3)
I learned this whole thing from MSDN... I dont think we can have any other legitimate source than that.

How to access property attributes on a data bound property in Silverlight?

For example, I have a simple textbox bound to a property:
<TextBox Text="{Binding FirstName, Mode=TwoWay, ValidatesOnExceptions=True, NotifyOnValidationError=True}" />
The property looks something like this:
[Display(Name="Last Name")]
public string LastName { ... }
So, given the textbox, I would like to get the Display Name property from the attribute. This will be used in a composite control that includes a fieldlabel and some other niceties.
Thanks in advance.
I am not able to attempt this at the moment so this may not be accurate or even possible. As soon as I get to a computer that I can try this I will...until then, this is just theory.
I'm guessing in your composite control you'll have something like this for each data bound field:
<TextBlock Text="{Binding FirstName, Mode=OneWay}" />
<TextBox Text="{Binding FirstName, Mode=TwoWay, ...}" />
What you'll probably need to do in order to create a converter that will look at the binding data for the Display attribute, and convert the value to the attribute value instead. This would cause the above block to look like this:
<TextBlock Text="{Binding FirstName, Mode=OneWay, Converter={StaticResource AttributeConverter}, ConverterParameter=Display}" />
<TextBox Text="{Binding FirstName, Mode=TwoWay, ...}" />
Here I passed in the Display as the parameter in case you wanted to access a different attribute.
Again this is just theory since I'm not able to currently test this and cannot recall if IValueConverter.Convert(object value, ...) passes the object in question or just the string value in this case. If it's just the string value, it probably isn't possible, though if it's the object instead, it will depend on how much access you have to the reflection namespace to evaluate the attributes.
As soon as I am able to, I'll throw the scenario together and try it out.
EDIT:
For some reason the sytax highlighter is giving me the finger when I try to paste code in this edit
Anyways, after trying this out in a little project, it don't think you can do this.
Based on my suggestion of making 2 data bound controls and using a converter for the one that consumes the attribute, I did the following:
Created the xaml for the databound control.
Create the Custom Attribute for testing
Created the Model with the decorated property for testing.
Created the converter to attempt to read the attribute from the property.
Here's where I got caught up. I wasn't able to obtain the data bound type from the IValueConverter.Convert(...) method. The value parameter came through as String as did the targetType parameter. While that was the primary hangup, the second was that I was unable to dynamically identify the property name that the control was data bound to. This could be remedied through a converter parameter possibly.
Now, I WAS able to read the attribute value if I supplied the type of my test Model with the decorated property so that much is possible but I wasn't able to dynamically identify the type on the fly.
The only other way I can think of is create some form of observer or converter prior to the data truly being bound to your custom control.
Good Luck

Is there a way to convert WPF binding markup to and from an instance of the binding class?

For example I have the following binding markup
Text="{Binding Path=FirstName}"
pretty simply but it could be much more complex, I need to be able to parse this markup and get it into some objectified form such as an instance of the Binding class.
Something that could work in reverse, an instance of the binding class to spit out the markup would be great as well.
I know such a thing must exist in the framework but I dont know where/what class.
I have looked at XamlReader but was unable to get it working because in this case I am missing context as I am only working with bits of the project and not the whole.
You can get the Binding object using GetBindingExpression, for example if you have:
<TextBlock Name="MyTextBlock" Text="{Binding Name}"/>
You can use:
BindingExpression expr = BindingExpression.GetBindingExpression(MyTextBlock, TextBlock.TextProperty);
Binding bindingObject = expr.ParentBinding;
To use XAMLReader you have to surround it with a valid root. then this shoudl work.

Resources