如何简单地将其绑定到转换器参数

本文关键字:绑定 转换器 参数 何简单 简单 | 更新日期: 2023-09-27 17:55:08

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

我有带有设置和主 XAML 的主项目。

我有绑定转换器和 XAML 文件的依赖项项目,如下所示:

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

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

我必须将一个属性的值从 Set 传递给 ConverterParameter,这个参数可以在运行时更改,那么这必须是Binding的,Binding在这种情况下我只能对DependencyProperty执行。

我必须为此设置属性做DependencyProperty包装器才能解决此问题?

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

无法在 类型的"转换器参数"属性 "绑定"。只能设置"绑定" 在依赖项属性上 依赖对象。

如何简单地将其绑定到转换器参数

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

  1. 使该属性成为依赖项属性。
  2. 在保存属性的类型上实现INotifyPropertyChanged,并在属性更改时引发PropertyChanged事件。

编辑:

正如在对问题的编辑中指出的那样,无法绑定到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. 可绑定参数(使用普通绑定 + 附加属性和标记扩展)

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>

属性"目标属性":

TargetProperty=TextBox.Text

的 BindableParamerter 必须设置为原始绑定属性(在本例中为"TextBox.Text")。

采样转换器:

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

该参数可用于"转换"和"转换回来"方法(对于绑定到视图模型很有用)。

类 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. 对象参考:

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>

剪断:

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

在静态字典中创建引用,并创建以下部分:

ConverterParameter={local:ObjectReference txtParam}}" 

从字典中获取此对象引用 -->此处没有绑定,字典在解析时填充。

采样转换器:

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. 可绑定转换器参数(使用自定义绑定语法):

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