2009-05-14 14 views
8

Ho un UserControl che utilizza un convertitore di binding. Ho trasformato il convertitore in una classe interna diConvertitore di binding come classe interna?

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

Come faccio a fare riferimento alla classe del convertitore da XAML? Quanto segue non funziona:

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

Si dà questo errore:

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

+0

Basta usare una classe normale! –

+2

@SergeyAldoukhov: perché? Questo suggerimento non è utile. –

risposta

2

Stavo pensando di nuovo a questo problema e ho trovato una soluzione simile alla soluzione di Dennis: creare una classe di convertitore "proxy", con una proprietà Type, che creerà l'istanza del convertitore effettivo e delegherà la conversione ad esso.

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 
} 

si usa in quel modo:

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

potrebbe essere possibile. Alcuni mesi fa ho scritto un'estensione di markup per creare il convertitore per te in linea. Mantiene un dizionario di riferimenti deboli in modo da non creare più istanze dello stesso convertitore. Gestisce anche la creazione di convertitori con argomenti diversi.

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

} 
+0

Fantastica idea! Sono un grande fan delle estensioni di markup personalizzate anch'io;) –

-3

Quello che faccio è:

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

E poi nel controllo

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

Non risolve il problema di Robert, ovvero che la sua classe di convertitore è una classe nidificata ... –

Problemi correlati