Reputation: 4107
I have a UserControl that uses a binding converter. I've made the converter an inner class of
public partial class MyPanel : UserControl
{
public class CornerRadiusConverter : IValueConverter
{
How do I reference the Converter class from the XAML? The following does not work:
<controls:MyPanel.CornerRadiusConverter x:Key="CornerRadiusConverter" />
It gives this error:
The tag 'LensPanel.CornerRadiusConverter' does not exist in XML namespace 'clr-namespace:MyApp.Windows.Controls'
Upvotes: 9
Views: 5187
Reputation: 20561
It could be possible. A few months ago I wrote a markup extension to create the converter for you inline. It keeps a dictionary of weak references so that you don't create multiple instances of the same converter. Handles creating converters with different arguments too.
In XAML:
<TextBox Text="{Binding Converter={NamespaceForMarkupExt:InlineConverter {x:Type NamespaceForConverter:ConverterType}}}"/>
C#:
[MarkupExtensionReturnType(typeof(IValueConverter))]
public class InlineConverterExtension : MarkupExtension
{
static Dictionary<string, WeakReference> s_WeakReferenceLookup;
Type m_ConverterType;
object[] m_Arguments;
static InlineConverterExtension()
{
s_WeakReferenceLookup = new Dictionary<string, WeakReference>();
}
public InlineConverterExtension()
{
}
public InlineConverterExtension(Type converterType)
{
m_ConverterType = converterType;
}
/// <summary>
/// The type of the converter to create
/// </summary>
/// <value>The type of the converter.</value>
public Type ConverterType
{
get { return m_ConverterType; }
set { m_ConverterType = value; }
}
/// <summary>
/// The optional arguments for the converter's constructor.
/// </summary>
/// <value>The argumments.</value>
public object[] Arguments
{
get { return m_Arguments; }
set { m_Arguments = value; }
}
public override object ProvideValue(IServiceProvider serviceProvider)
{
IProvideValueTarget target = (IProvideValueTarget)serviceProvider.GetService(typeof(IProvideValueTarget));
PropertyInfo propertyInfo = target.TargetProperty as PropertyInfo;
if (!propertyInfo.PropertyType.IsAssignableFrom(typeof(IValueConverter)))
throw new NotSupportedException("Property '" + propertyInfo.Name + "' is not assignable from IValueConverter.");
System.Diagnostics.Debug.Assert(m_ConverterType != null, "ConverterType is has not been set, ConverterType{x:Type converterType}");
try
{
string key = m_ConverterType.ToString();
if (m_Arguments != null)
{
List<string> args = new List<string>();
foreach (object obj in m_Arguments)
args.Add(obj.ToString());
key = String.Concat(key, "_", String.Join("|", args.ToArray()));
}
WeakReference wr = null;
if (s_WeakReferenceLookup.TryGetValue(key, out wr))
{
if (wr.IsAlive)
return wr.Target;
else
s_WeakReferenceLookup.Remove(key);
}
object converter = (m_Arguments == null) ? Activator.CreateInstance(m_ConverterType) : Activator.CreateInstance(m_ConverterType, m_Arguments);
s_WeakReferenceLookup.Add(key, new WeakReference(converter));
return converter;
}
catch(MissingMethodException)
{
// constructor for the converter does not exist!
throw;
}
}
}
Upvotes: 2
Reputation: 292375
I was thinking about this problem again, and I came up with something similar to Dennis's solution : create a "proxy" converter class, with a Type property, which will create the instance of the actual converter and delegate the conversion to it.
public class Converter : IValueConverter
{
private Type _type = null;
public Type Type
{
get { return _type; }
set
{
if (value != _type)
{
if (value.GetInterface("IValueConverter") != null)
{
_type = value;
_converter = null;
}
else
{
throw new ArgumentException(
string.Format("Type {0} doesn't implement IValueConverter", value.FullName),
"value");
}
}
}
}
private IValueConverter _converter = null;
private void CreateConverter()
{
if (_converter == null)
{
if (_type != null)
{
_converter = Activator.CreateInstance(_type) as IValueConverter;
}
else
{
throw new InvalidOperationException("Converter type is not defined");
}
}
}
#region IValueConverter Members
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
CreateConverter();
return _converter.Convert(value, targetType, parameter, culture);
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
CreateConverter();
return _converter.ConvertBack(value, targetType, parameter, culture);
}
#endregion
}
You use it like that :
<Window.Resources>
<my:Converter x:Key="CornerRadiusConverter" Type="{x:Type controls:MyPanel+CornerRadiusConverter}"/>
</Window.Resources>
Upvotes: 3
Reputation: 25959
What I do is:
<Window.Resources>
<ResourceDictionary>
<Converters:BooleanNotConverter x:Key="BooleanNotConverter"/>
</ResourceDictionary>
</Window.Resources>
And then in the control
<CheckBox IsChecked="{Binding Path=BoolProperty, Converter={StaticResource BooleanNotConverter} />
Upvotes: -3