Подтвердить что ты не робот

Как просто привязать это к ConverterParameter?

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

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

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

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

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

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

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

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

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

4b9b3361

Ответ 1

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

  • Сделать свойство зависимым.
  • Внесите 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, и вы, вероятно, поместили бы его в ресурсы несколько уровней иерархии в реальном приложении.

Ответ 2

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

  • 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" ).

Образец-конвертер:

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

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

Образец-конвертер:

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

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