2009-05-14 12 views
8

Tengo un UserControl que usa un convertidor de encuadernación. He convertido el convertidor en una clase interna deConvertidor de unión como clase interna?

public partial class MyPanel : UserControl 
{ 
    public class CornerRadiusConverter : IValueConverter 
    { 

¿Cómo hago referencia a la clase Convertidor desde el XAML? El siguiente no funciona:

<controls:MyPanel.CornerRadiusConverter x:Key="CornerRadiusConverter" /> 

Se da este error:

The tag 'LensPanel.CornerRadiusConverter' does not exist in XML namespace 'clr-namespace:MyApp.Windows.Controls'

+0

sólo tiene que utilizar una clase normal! –

+2

@SergeyAldoukhov: ¿Por qué? Esa sugerencia no es útil. –

Respuesta

2

Estaba pensando nuevamente en este problema, y ​​se me ocurrió algo similar a la solución de Dennis: crear una clase de convertidor "proxy", con una propiedad Type, que creará la instancia del convertidor real y delegará la conversión en él.

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 
} 

que lo utilice de esa manera:

<Window.Resources> 
    <my:Converter x:Key="CornerRadiusConverter" Type="{x:Type controls:MyPanel+CornerRadiusConverter}"/> 
</Window.Resources> 
2

Podría ser posible. Hace unos meses, escribí una extensión de marcado para crear el convertidor en línea. Mantiene un diccionario de referencias débiles para que no cree múltiples instancias del mismo convertidor. Maneja la creación de convertidores con diferentes argumentos también.

En 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; 
    } 
    } 

} 
+0

¡Idea genial! Soy un gran admirador de las extensiones de marcado personalizadas;) –

-3

Lo que hago es:

<Window.Resources> 
    <ResourceDictionary> 
    <Converters:BooleanNotConverter x:Key="BooleanNotConverter"/> 
    </ResourceDictionary> 
</Window.Resources> 

Y luego en el control

<CheckBox IsChecked="{Binding Path=BoolProperty, Converter={StaticResource BooleanNotConverter} /> 
+0

No resuelve el problema de Robert, que es que su clase de convertidor es una clase anidada ... –