Как просто привязать это к ConverterParameter? - PullRequest
24 голосов
/ 07 мая 2011

У меня есть проблема, и я не знаю, как решить эту простую проблему, у меня много таких моментов, поэтому решение не должно быть сложным.

У меня есть основной проект с настройками и основной XAML.

У меня есть проект зависимости с Binding Converter, и XAML-файл выглядит так:

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

Этот XAML-файл загружается основным XAML-файлом из основного проекта.

Я должен передать значение одного свойства из Setting в ConverterParameter, этот параметр может изменяться во время выполнения, тогда это должно быть Binding, Binding я могу сделать только для DependencyProperty в этом случае.

Я должен сделать DependencyProperty обертку для этого свойства настройки, чтобы решить эту проблему?

Когда я пытаюсь установить Binding в ConverterParameter, я получу это исключение во время выполнения:

«Связывание» не может быть установлено на Свойство ConverterParameter типа 'Binding'. «Связывание» может быть установлено только на DependencyProperty DependencyObject.

Ответы [ 2 ]

23 голосов
/ 07 мая 2011

Вы можете привязать к любому свойству, оно не должно быть свойством зависимости. Но если вы хотите, чтобы ваш пользовательский интерфейс отражал изменения в свойстве сразу после их появления, у вас есть два варианта:

  1. Превратить свойство в свойство зависимости.
  2. Реализуйте INotifyPropertyChanged в типе, который содержит свойство, и вызовите событие PropertyChanged при изменении свойства.

EDIT:

Как указывалось в редактировании вопроса, невозможно связать с 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>

Здесь Date и Settings являются свойствами текущего DataContext. DateCultureConverter реализует IMultiValueConverter, и вы, вероятно, поместите его в ресурсы на несколько уровней выше иерархии в реальном приложении.

7 голосов
/ 05 августа 2015

Вы можете использовать одно из следующих решений:

  1. BindableParameter (с использованием обычной привязки + прикрепленного свойства и MarkupExtension)

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

TargetProperty=TextBox.Text

объекта BindableParamerter должен быть установлен в исходное свойство привязки (в данном случае «TextBox.Text»).

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

Параметр может использоваться в методах «Преобразование» и «ConvertBack» (полезно для привязки к модели представления).

Класс 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:

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

берет ссылку на этот объект из словаря -> здесь нет привязки, словарь заполняется во время разбора.

Sample-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. Параметр привязываемого преобразователя (с использованием пользовательского синтаксиса привязки):

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>

Sample-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;
        }
    }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...