2011-05-07 19 views
19

Tengo un problema y no sé cómo resolver esto simple, tengo muchos puntos como este, entonces la solución no debería ser complicada.¿Cómo simplemente atar esto a ConverterParameter?

Tengo el proyecto principal con Configuración y XAML principal.

tengo la dependencia proyecto con convertidor de encuadernación y XAML del archivo se ve como:

<TextBlock Text="{Binding X.Y.Z, Converter={StaticResource ProbabilityConverter}, ConverterParameter=??????????????, Mode=OneWay}"/> 

archivo XAML Esto se está cargando por el archivo principal XAML del proyecto principal.

debo pasar el valor de una propiedad a través de Ajustes de a ConverterParameter, este parámetro puede ser cambiado en tiempo de ejecución, entonces esto es debe ser Binding, Binding que puedo hacer sólo para DependencyProperty en este caso.

Debo hacer DependencyProperty envoltura para este ajuste de la propiedad para resolver este problema?

cuando intento configurar Binding en ConverterParameter voy a tener esta excepción en tiempo de ejecución:

A 'Encuadernación' no se puede establecer en la propiedad de la 'ConverterParameter' de tipo 'Encuadernación'. Un 'Enlace' solo se puede establecer en en una Propiedad de dependencia de un DependencyObject.

Respuesta

21

Puede enlazar a cualquier propiedad, no tiene por qué ser una propiedad de la dependencia. Pero si usted quiere que su interfaz de usuario para reflejar los cambios en la propiedad inmediatamente cuando ocurren, tiene dos opciones:

  1. hacer que la propiedad en una propiedad de dependencia.
  2. Implemente INotifyPropertyChanged en el tipo que contiene la propiedad y plantee el evento PropertyChanged cuando la propiedad cambie.

EDIT:

Como se señaló en la edición en cuestión, no es posible obligar a ConverterParameter. Pero puede usar MultiBinding. Por ejemplo, supongamos que desea enlazar a una fecha y dar la especificación de cultura del convertidor como un parámetro y actualizar el enlace cuando la cultura cambie (no estoy seguro de que sea una buena idea, pero sirve como ejemplo). Usted puede hacerlo de esta manera:

<TextBlock> 
    <TextBlock.Resources> 
     <local:DateCultureConverter x:Key="converter" /> 
    </TextBlock.Resources> 
    <TextBlock.Text> 
     <MultiBinding Converter="{StaticResource converter}"> 
      <Binding Path="Date" /> 
      <Binding Path="Settings.Culture" /> 
     </MultiBinding> 
    </TextBlock.Text> 
</TextBlock> 

Aquí, tanto Date y Settings son propiedades de la corriente DataContext. DateCultureConverter implementa IMultiValueConverter y probablemente lo pondría en recursos pocos niveles en la jerarquía en la aplicación real.

+0

si yo le puedo obligar a la propiedad normal, entonces no voy a tener problema porque tengo clase con aplicación inotitypropertychanged ;-) pero compilador dice, encuadernaciones entonces he anidados entonces deben unirse a dependencyproperty solamente ;-(trato de hacer esto de nuevo y actualizar este tema – Svisstack

+0

actualizado, cadena de error añadido! – Svisstack

+0

@Svisstack, ver respuesta actualizada – svick

4

Puede utilizar una de las siguientes soluciones:

  1. BindableParameter (utilizando la unión normal + una propiedad adjunta y MarkupExtension)

https://marlongrech.wordpress.com/2008/08/03/my-wish-came-true-i-can-now-use-databinding-in-a-converterparameter/

Debe integrar la clase BindableParameter y BindableParameterExtension (ver a continuación) y luego puede usarlo de la siguiente manera:

En XAML:

xmlns:local="clr-namespace:BindableParameterExtension" 

    <local:SampleConverter x:Key="sampleConverter" /> 

    <StackPanel Orientation="Vertical"> 
     <TextBox Name="txtContent" Text="Text from txtContent" /> 
     <TextBox Name="txtParameter" Text="Text from txtParameter" /> 
     <TextBox Name="txtBindingSample" 
       Text="{Binding ElementName=txtContent, Path=Text, Converter={StaticResource sampleConverter}}" 
       local:BindableParameter.BindParameter="{local:BindableParameter TargetProperty=TextBox.Text, 
           Binding={Binding ElementName=txtParameter, Path=Text} }" /> 
    </StackPanel> 

la propiedad "TargetProperty":

TargetProperty=TextBox.Text 

del BindableParamerter se debe establecer en la propiedad original encuadernado (en este caso "TextBox.Text").

la muestra-Converter:

using System; 
using System.Windows.Data; 

namespace BindableParameterExtension 
{ 
    public class SampleConverter : IValueConverter 
    { 
     public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) 
     { 
      if (value != null && parameter != null) 
      { 
       return value.ToString() + ", " + parameter.ToString(); 
      } 
      return null; 
     } 

     public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) 
     { 
      if (value is string && parameter is string) 
      { 
       string text1 = value as string; 
       string textParamter = parameter as string; 

       return text1.Replace(textParamter, ""); 
      } 
      return value; 
     } 
    } 
}  

El parámetro podría ser utilizado en el "Convertir" y el método "ConvertBack" (útil para unirse a un modelo de vista).

Clase BindableParameter y BindableParameterExtension (URL ver más arriba (no es mi código))

/* 
* Copyright - Everyone can use this code for any reason yet if you find a bug, I do not hold myself responsable :D 
*/ 

using System.Windows.Data; 
using System.Windows.Markup; 

namespace BindableParameterExtension 
{ 
    /// <summary> 
    /// BindableParameter is the class that changes the ConverterParameter Value 
    /// This must inherit from freezable so that it can be in the inheritance context and thus be able to use the DataContext and to specify ElementName binding as a ConverterParameter 
    /// http://www.drwpf.com/Blog/Default.aspx?tabid=36&EntryID=36 
    /// </summary> 
    public class BindableParameter : Freezable 
    { 
     #region fields 
     //this is a hack to trick the WPF platform in thining that the binding is not sealed yet and then change the value of the converter parameter 
     private static FieldInfo isSealedFieldInfo; 
     #endregion 

     #region Properties 
     #region Parameter 

     /// <summary> 
     /// Parameter Dependency Property 
     /// </summary> 
     public static readonly DependencyProperty ParameterProperty = 
      DependencyProperty.Register("Parameter", typeof(object), typeof(BindableParameter), 
       new FrameworkPropertyMetadata((object)null, 
        (d, e) => 
        { 
         BindableParameter param = (BindableParameter)d; 
         //set the ConverterParameterValue before calling invalidate because the invalidate uses that value to sett the converter paramter 
         param.ConverterParameterValue = e.NewValue; 
         //update the converter parameter 
         InvalidateBinding(param); 
        } 
        )); 

     /// <summary> 
     /// Gets or sets the Parameter property. This dependency property 
     /// indicates .... 
     /// </summary> 
     public object Parameter 
     { 
      get { return (object)GetValue(ParameterProperty); } 
      set { SetValue(ParameterProperty, value); } 
     } 

     #endregion 

     #region BindParameter 

     /// <summary> 
     /// BindParameter Attached Dependency Property 
     /// </summary> 
     public static readonly DependencyProperty BindParameterProperty = 
      DependencyProperty.RegisterAttached("BindParameter", typeof(BindableParameter), typeof(BindableParameter), 
       new FrameworkPropertyMetadata((BindableParameter)null, 
        new PropertyChangedCallback(OnBindParameterChanged))); 

     /// <summary> 
     /// Gets the BindParameter property. This dependency property 
     /// indicates .... 
     /// </summary> 
     public static BindableParameter GetBindParameter(DependencyObject d) 
     { 
      return (BindableParameter)d.GetValue(BindParameterProperty); 
     } 

     /// <summary> 
     /// Sets the BindParameter property. This dependency property 
     /// indicates .... 
     /// </summary> 
     public static void SetBindParameter(DependencyObject d, BindableParameter value) 
     { 
      d.SetValue(BindParameterProperty, value); 
     } 

     /// <summary> 
     /// Handles changes to the BindParameter property. 
     /// </summary> 
     private static void OnBindParameterChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) 
     { 
      FrameworkElement element = d as FrameworkElement; 
      if (element == null) 
       throw new InvalidOperationException("BindableParameter can be applied to a FrameworkElement only"); 

      BindableParameter parameter = (BindableParameter)e.NewValue; 
      element.Initialized += delegate 
      { 
       parameter.TargetExpression = BindingOperations.GetBindingExpression(element, parameter.TargetProperty); 
       parameter.TargetBinding = BindingOperations.GetBinding(element, parameter.TargetProperty); 

       //update the converter parameter 
       InvalidateBinding(parameter); 
      }; 
     } 

     #endregion 

     public object ConverterParameterValue { get; set; } 

     public BindingExpression TargetExpression { get; set; } 

     public Binding TargetBinding { get; private set; } 

     /// <summary> 
     /// Gets the object being bound 
     /// </summary> 
     public DependencyObject TargetObject { get; private set; } 

     /// <summary> 
     /// Gets the dependency property being bound 
     /// </summary> 
     public DependencyProperty TargetProperty { get; internal set; } 
     #endregion 

     /// <summary> 
     /// Static constructor to get the FieldInfo meta data for the _isSealed field of the BindingBase class 
     /// </summary> 
     static BindableParameter() 
     { 
      //initialize the field info once 
      isSealedFieldInfo = 
       typeof(BindingBase).GetField("_isSealed", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance); 

      if (isSealedFieldInfo == null) 
       throw new InvalidOperationException("Oops, we have a problem, it seems like the WPF team decided to change the name of the _isSealed field of the BindingBase class."); 

     } 

     private static void InvalidateBinding(BindableParameter param) 
     { 
      if (param.TargetBinding != null && param.TargetExpression != null) 
      { 
       //this is a hack to trick the WPF platform in thining that the binding is not sealed yet and then change the value of the converter parameter 
       bool isSealed = (bool)isSealedFieldInfo.GetValue(param.TargetBinding); 

       if (isSealed)//change the is sealed value 
        isSealedFieldInfo.SetValue(param.TargetBinding, false); 

       param.TargetBinding.ConverterParameter = param.ConverterParameterValue; 

       if (isSealed)//put the is sealed value back as it was... 
        isSealedFieldInfo.SetValue(param.TargetBinding, true); 

       //force an update to the binding 
       param.TargetExpression.UpdateTarget(); 
      } 
     } 

     #region Freezable Stuff 
     protected override Freezable CreateInstanceCore() 
     { 
      //throw new NotImplementedException(); 
      //return _bindableParam; 

      return this; 
     } 
     #endregion 

    } 

    /// <summary> 
    /// Markup extension so that it is easier to create an instance of the BindableParameter from XAML 
    /// </summary> 
    [MarkupExtensionReturnType(typeof(BindableParameter))] 
    public class BindableParameterExtension : MarkupExtension 
    { 
     /// <summary> 
     /// Gets or sets the Dependency property you want to change the binding's ConverterParameter 
     /// </summary> 
     public DependencyProperty TargetProperty { get; set; } 

     /// <summary> 
     /// Gets or sets the Binding that you want to use for the converter parameter 
     /// </summary> 
     public Binding Binding { get; set; } 

     /// <summary> 
     /// constructor that accepts a Dependency Property so that you do not need to specify TargetProperty 
     /// </summary> 
     /// <param name="property">The Dependency property you want to change the binding's ConverterParameter</param> 
     public BindableParameterExtension(DependencyProperty property) 
     { 
      TargetProperty = property; 
     } 

     public BindableParameterExtension() 
     { } 

     public override object ProvideValue(IServiceProvider serviceProvider) 
     { 
      _bindableParam = new BindableParameter(); 
      //set the binding of the parameter 
      BindingOperations.SetBinding(_bindableParam, BindableParameter.ParameterProperty, Binding); 
      _bindableParam.TargetProperty = TargetProperty; 
      return _bindableParam; 
     } 

     private BindableParameter _bindableParam; 

    } 
} 
  1. ObjectReference:

http://drwpf.com/blog/2007/09/02/supplying-an-object-reference-in-the-constructorparameters-collection-of-an-objectdataprovider/

Usted debe integrar la clase ObjectReference:

http://www.drwpf.com/blog/Portals/0/Code/ObjectReference.cs.txt

En XAML:

xmlns:local="clr-namespace:WpfMarkupExtension" 

<local:SampleConverter x:Key="sampleConverter" /> 

<StackPanel Orientation="Vertical"> 
    <TextBox Name="txtContent" Text="Text from txtContent" /> 
    <TextBox Name="txtParameter" Text="Text from txtParameter" local:ObjectReference.Declaration="{local:ObjectReference txtParam}" /> 
    <TextBox Name="txtBindingSample" 
      Text="{Binding ElementName=txtContent, Path=Text, 
          Converter={StaticResource sampleConverter}, 
          ConverterParameter={local:ObjectReference txtParam}}" /> 
</StackPanel> 

El cortó:

local:ObjectReference.Declaration="{local:ObjectReference txtParam}" 

crea la referencia en un diccionario estático y la parte:

ConverterParameter={local:ObjectReference txtParam}}" 

toma esta referencia de objeto de el Diccionario -> no vinculante aquí, el diccionario es fi Lleno en el tiempo de análisis.

la muestra-Converter:

using System; 
using System.Windows.Controls; 
using System.Windows.Data; 

namespace WpfMarkupExtension 
{ 
    public class SampleConverter : IValueConverter 
    { 
     public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) 
     { 
      if (value != null && parameter is TextBox) 
      { 
       return value.ToString() + ", " + ((TextBox)parameter).Text; 
      } 
      return null; 
     } 

     public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) 
     { 
      if (value is string && parameter is TextBox) 
      { 
       string text1 = value as string; 
       string textParamter = ((TextBox)parameter).Text; 

       return text1.Replace(textParamter, ""); 

      } 

      return value; 
     } 
    } 
} 
  1. Bindable convertidor de parámetros (utilizando la sintaxis de encargo de unión):

http://www.codeproject.com/Articles/456589/Bindable-Converter-Parameter

En XAML :

xmlns:local="clr-namespace:BcpBindingExtension" 

    <local:SampleConverter x:Key="sampleConverter" /> 

    <StackPanel Orientation="Vertical"> 
     <TextBox Name="txtContent" Text="Text from txtContent" /> 
     <TextBox Name="txtParameter" Text="Text from txtParameter" /> 
     <TextBox Name="txtBindingSample"> 
       <TextBox.Text> 
        <local:BcpBinding Path="Text" ElementName="txtContent" 
             Converter="{StaticResource sampleConverter}" 
             ConverterParameters="Binding Path=Text ElementName=txtParameter" 
             Mode="TwoWay"/> 
       </TextBox.Text> 
     </TextBox> 
    </StackPanel> 

la muestra-Converter:

using System; 
using System.Windows.Data; 

namespace BcpBindingExtension 
{ 
    public class SampleConverter : IValueConverter 
    { 
     public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) 
     { 
      if (value != null && parameter is object[] && ((object[])parameter).Length > 0) 
      { 
       return value.ToString() + ", " + ((object[])parameter)[0].ToString(); 
      } 
      return null; 
     } 

     public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) 
     { 
      if (value is string && parameter is object[] && ((object[])parameter).Length > 0) 
      { 
       string text1 = value as string; 
       string textParamter = ((object[])parameter)[0] as string; 

       return text1.Replace(textParamter, ""); 

      } 

      return value; 
     } 
    } 
} 
Cuestiones relacionadas