WPF Per binding instance of a value converter?
How do I ensure that a new instan开发者_运维百科ce of a value converter is created for every binding that it is used in?
You need to specify x:Shared="False"
on the converter resource. Here is an example:
<BooleanToVisibilityConverter x:Key="MyConverter" x:Shared="False"/>
You can make a separate resource in each control:
<TextBox>
<TextBox.Resources>
<Converters:VisibilityConverter x:Key="conv"/>
</TextBox.Resources>
<TextBox.Text>
<Binding Converter="{StaticResource conv}"/>
</TextBox.Text>
</TextBox>
Why? It should be deterministic, and unaware of any state except that which is passed in to it via its parameters. I have used value converters to keep a static list of images (a value is convertered to an image), and this works fine - even when the same converter is used across several columns in a datagrid with thousands of rows. (Note that the converter is still unaware of any external state).
In any case, see if this answers your question: Are value converters instantiated per-binding in WPF?
Inherit from MarkupExtension
and then create your converter in the constructor.
public override object ProvideValue(IServiceProvider serviceProvider)
{
_converter = new FlagsToBoolConverter();
return _converter;
}
Here's a full example. It's a flags converter that needed to be instanced to keep the source value for ConvertBack.
using System;
using System.Globalization;
using System.Runtime.CompilerServices;
using System.Windows;
using System.Windows.Data;
using System.Windows.Markup;
namespace Something
{
public class FlagsToBoolConverter : MarkupExtension, IValueConverter
{
private FlagsToBoolConverter _converter;
public override object ProvideValue(IServiceProvider serviceProvider)
{
_converter = new FlagsToBoolConverter();
return _converter;
}
private ulong _sourceValue;
public virtual object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
try
{
var type = value.GetType();
if (type.IsEnum)
{
ulong mask = (ulong)System.Convert.ChangeType(Enum.Parse(type, (string)parameter), typeof(ulong));
_sourceValue = (ulong)System.Convert.ChangeType(Enum.Parse(type, value.ToString()), typeof(ulong));
return ((mask & _sourceValue) != 0);
}
return value;
}
catch (Exception ex)
{
Console.WriteLine("FlagsEnumValueConverter: Invalid Cast(to) Value={0} Type={1} Param={2} Exception{3}", value, targetType, parameter, ex);
}
return value;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
try
{
if (targetType.IsEnum)
{
var original = this._sourceValue;
var originalEnum = Enum.Parse(targetType, original.ToString());
var maskEnum = Enum.Parse(targetType, (string)parameter);
var mask = (ulong)System.Convert.ChangeType(maskEnum, typeof(ulong));
_sourceValue ^= mask;
var sourceEnum = Enum.Parse(targetType, _sourceValue.ToString());
Console.WriteLine($"Modified Value: {original} ({originalEnum}) by Mask {mask} ({maskEnum}) Result = {_sourceValue} ({sourceEnum})");
return sourceEnum;
}
return value;
}
catch (Exception ex)
{
Console.WriteLine("FlagsEnumValueConverter: Invalid Cast(from) Value={0} Type={1} Param={2} Exception{3}", value, targetType, parameter, ex);
}
return value;
}
}
}
Your prefix needs to be declared xmlns:src="clr-namespace:Something"
. And then in your binding replace the normal converter with Converter={src:FlagsToBoolConverter}
<CheckBox IsChecked="{Binding SomeFlagsEnum, ConverterParameter=FlagA, Converter={src:FlagsToBoolConverter}}">FlagA</CheckBox>
<CheckBox IsChecked="{Binding SomeFlagsEnum, ConverterParameter=FlagB, Converter={src:FlagsToBoolConverter}}">FlagB</CheckBox>
<CheckBox IsChecked="{Binding SomeFlagsEnum, ConverterParameter=FlagC, Converter={src:FlagsToBoolConverter}}">FlagC</CheckBox>
If you put your converters into resources and reference them through a {StaticResource ConverterName}
lookup, then they are instanciated only once per instance of resource dictionary.
But a better approach is to inherit your converters from MarkupExtension
and use them directly instead of adding them to the resources and reference them.
Here is an example of such a base class:
http://www.snippetsource.net/Snippet/18/base-class-for-valueconverters-in-wpf
Greetings Christian
精彩评论