如何简单地将其绑定到ConverterParameter?

时间:2011-05-07 11:17:37

标签: c# data-binding dependency-properties

我有问题,我不知道如何解决这个问题,我有很多这样的观点,那么解决方案应该不复杂。

我有主要项目设置和主要XAML。

我有Binding Converter和XAML File的依赖项目如下:

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

此XAML文件由主项目的主XAML文件加载。

我必须将一个属性的值从Setting's传递给ConverterParameter,此参数可以在运行时更改,那么这必须是BindingBinding我只能为{{ 1}}在这种情况下。

我必须为此Setting属性执行DependencyProperty包装才能解决此问题?

当我尝试在DependencyProperty中设置Binding时,我会在运行时收到此异常:

  

无法设置'绑定'   'ConverterParameter'属性的类型   '捆绑'。只能设置'绑定'   在一个DependencyProperty上   的DependencyObject。

2 个答案:

答案 0 :(得分:23)

您可以绑定到任何属性,它不必是依赖属性。但是,如果您希望UI在发生时立即反映属性的变化,您有两种选择:

  1. 将该属性设为依赖属性。
  2. 对包含该属性的类型实施INotifyPropertyChanged,并在属性更改时引发PropertyChanged事件。
  3. 修改

    正如问题编辑所指出的那样,无法绑定到ConverterParameter。但您可以使用MultiBinding。例如,假设您想要绑定到日期并将转换器文化规范作为参数,并在文化更改时刷新绑定(我不确定这是一个好主意,但它很好地作为示例)。你可以这样做:

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

    此处,DateSettings都是当前DataContext的属性。 DateCultureConverter实现了IMultiValueConverter,你可能会把它放在实际应用程序层次结构中的几层资源中。

答案 1 :(得分:7)

您可以使用以下解决方案之一:

  1. BindableParameter(使用普通绑定+附加属性和MarkupExtension)
  2. https://marlongrech.wordpress.com/2008/08/03/my-wish-came-true-i-can-now-use-databinding-in-a-converterparameter/

    您必须集成类BindableParameter和BindableParameterExtension(见下文),然后您可以按如下方式使用它:

    在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>
    

    属性&#34; TargetProperty&#34;:

    TargetProperty=TextBox.Text
    
    必须将BindableParamerter的

    设置为原始绑定属性(在本例中为&#34; TextBox.Text&#34;)。

    样品转换器:

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

    该参数可用于&#34;转换&#34;和&#34; ConvertBack&#34;方法(用于绑定到视图模型)。

    Class BindableParameter和BindableParameterExtension(URL见上文(不是我的代码))

    /*
     * 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:
    2. http://drwpf.com/blog/2007/09/02/supplying-an-object-reference-in-the-constructorparameters-collection-of-an-objectdataprovider/

      您必须集成ObjectReference类:

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

      在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>
      

      The snipped:

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

      在静态字典和部分中创建引用:

      ConverterParameter={local:ObjectReference txtParam}}" 
      

      从字典中获取此对象引用 - &gt;这里没有绑定,字典在解析时填充。

      样品转换器:

      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. 可绑定转换器参数(使用自定义绑定语法):
      2. http://www.codeproject.com/Articles/456589/Bindable-Converter-Parameter

        在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>
        

        样品转换器:

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