MultiBinding ConvertBack问题

时间:2012-11-27 12:53:37

标签: wpf multibinding imultivalueconverter

在尝试解决我在另一个项目中遇到的问题时 - 我创建了以下示例来复制该问题。

这个想法是,当用户通过滑块或文本框输入新值时,这些值应该是" ConvertedBack"通过转换器,并更新源。我似乎没有看到这一点,我相信由于正在写入InternalRep的属性,但没有告知InternalRepProperty的bindexpression。

解决此问题的最佳方法是什么?

我尝试过的一种方法是处理滑块ValueChanged事件,但是这导致转换器转换为... ConvertBack 然后转换然后 ConvertBack 然后转换,不知道为什么。

当用户更改某个值时,我需要将转换器仅转换为ConvertBack以更新源,而不需要其他任何内容,这是可能的吗?

TextSplitter XAML

<ContentControl x:Class="WpfApplication23.TextSplitter"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:local="clr-namespace:WpfApplication23"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">

    <UniformGrid Columns="3" Rows="2">
        <TextBox Text="{Binding RelativeSource={RelativeSource AncestorType={x:Type local:TextSplitter}}, 
                 Path=InternalRep.First, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}" />

        <TextBox Text="{Binding RelativeSource={RelativeSource AncestorType={x:Type local:TextSplitter}}, 
                 Path=InternalRep.Second, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}" />

        <TextBox Text="{Binding RelativeSource={RelativeSource AncestorType={x:Type local:TextSplitter}}, 
                 Path=InternalRep.Third, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}" />

        <Slider  Value="{Binding RelativeSource={RelativeSource AncestorType={x:Type local:TextSplitter}}, 
                 Path=InternalRep.First, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}" Maximum="255" />

        <Slider  Value="{Binding RelativeSource={RelativeSource AncestorType={x:Type local:TextSplitter}}, 
                 Path=InternalRep.Second, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}" Maximum="255" />

        <Slider  Value="{Binding RelativeSource={RelativeSource AncestorType={x:Type local:TextSplitter}}, 
                 Path=InternalRep.Third, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}" Maximum="255" ValueChanged="OnSliderChnaged" />
    </UniformGrid>

</ContentControl>

TextSplitter C#

public class InternalRep
    {
        public int First { get; set; }
        public int Second { get; set; }
        public int Third { get; set; }
    };

    public class LettersToInternalRepMultiConvertor : IMultiValueConverter
    {
        public object Convert(object[] values, Type targetType,
               object parameter, System.Globalization.CultureInfo culture)
        {
            InternalRep ir = new InternalRep()
            {
                First = (int)(char)values[0],
                Second = (int)(char)values[1],
                Third = (int)(char)values[2],
            };

            return ir;
        }

        public object[] ConvertBack(object value, Type[] targetTypes,
               object parameter, System.Globalization.CultureInfo culture)
        {
            InternalRep ir = (InternalRep)value;
            if (ir != null)
            {
                return new object[] 
                { 
                    (char)ir.First, 
                    (char)ir.Second, 
                    (char)ir.Third 
                };
            }
            else
            {
                throw new Exception();
            }
        }
    }

    public partial class TextSplitter : ContentControl
    {
        public static readonly DependencyProperty FirstProperty = DependencyProperty.Register(
        "First", typeof(char), typeof(TextSplitter));

        public static readonly DependencyProperty SecondProperty = DependencyProperty.Register(
        "Second", typeof(char), typeof(TextSplitter));

        public static readonly DependencyProperty ThirdProperty = DependencyProperty.Register(
        "Third", typeof(char), typeof(TextSplitter));

        public static readonly DependencyProperty InternalRepProperty = DependencyProperty.Register(
        "InternalRep", typeof(InternalRep), typeof(TextSplitter));

        BindingExpressionBase beb = null;

        public TextSplitter()
        {
            InitializeComponent();

            MultiBinding mb = new MultiBinding();
            mb.Mode = BindingMode.TwoWay;
            mb.Bindings.Add(SetBinding("First"));
            mb.Bindings.Add(SetBinding("Second"));
            mb.Bindings.Add(SetBinding("Third"));
            mb.Converter = new LettersToInternalRepMultiConvertor();

            beb = SetBinding(InternalRepProperty, mb);
        }

        Binding SetBinding(string _property)
        {
            Binding b = new Binding(_property);
            b.Mode = BindingMode.TwoWay;
            b.Source = this;
            return b;
        }

        public char First
        {
            get { return (char)GetValue(FirstProperty); }
            set { SetValue(FirstProperty, value); }
        }

        public char Second
        {
            get { return (char)GetValue(SecondProperty); }
            set { SetValue(SecondProperty, value); }
        }

        public char Third
        {
            get { return (char)GetValue(ThirdProperty); }
            set { SetValue(ThirdProperty, value); }
        }
    }

MainWindow XAML

<Window x:Class="WpfApplication23.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="clr-namespace:WpfApplication23"
        Title="MainWindow" Height="640" Width="800" WindowStartupLocation="CenterScreen">
    <StackPanel>
        <local:TextSplitter First="{Binding A, Mode=TwoWay}"
                            Second="{Binding B, Mode=TwoWay}"
                            Third="{Binding C, Mode=TwoWay}"/>
    </StackPanel>
</Window>

MainWindow代码

namespace WpfApplication23
{
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
            DataContext = this;
        }

        char m_a = 'a';
        public char A
        {
            get { return m_a; }
            set { m_a = value; }
        }

        char m_B = 'b';
        public char B
        {
            get { return m_B; }
            set{ m_B = value; }
        }

        char m_c = 'c';
        public char C
        {
            get { return m_c; }
            set { m_c = value; }
        }

    }
}

1 个答案:

答案 0 :(得分:0)

您需要在ViewModel中实现INotifyPropertyChanged 这里,ViewModel是Window,所以你必须这样做:

public partial class MainWindow : Window, INotifyPropertyChanged
{
    public MainWindow()
    {
        this.DataContext = this;
        InitializeComponent();
    }

    char m_a = 'a';
    public char A
    {
        get { return m_a; }
        set
        {
            if (value != m_a)
            {
                m_c = value;
                RaisePropertyChanged("A");
            }
        }
    }

    char m_B = 'b';
    public char B
    {
        get { return m_B; }
        set
        {
            if (value != m_B)
            {
                m_c = value;
                RaisePropertyChanged("B");
            }
        }
    }

    char m_c = 'c';
    public char C
    {
        get { return m_c; }
        set
        {
            if (value != m_c)
            {
                m_c = value;
                RaisePropertyChanged("C");
            }
        }
    }

    public event PropertyChangedEventHandler PropertyChanged;
    private void RaisePropertyChanged(string _Prop)
    {
        if (PropertyChanged != null)
        {
            PropertyChanged(this, new PropertyChangedEventArgs(_Prop));
        }
    }

    DelegateCommand _RecomputeCommand;
    public DelegateCommand RecomputeCommand
    {
        get { return _RecomputeCommand ?? (_RecomputeCommand = new DelegateCommand(Recompute)); }
    }

    public void Recompute()
    {
        //Do something with A, B and C.
    }
}

编辑:你应该简单地将3个滑块绑定到A,B,C(你需要上面的实现)然后按照这样的方式对RecomputeCommand进行操作:

<StackPanel>
    <Slider Value="{Binding A}" Maximum="255">
        <i:Interaction.Triggers>
            <i:EventTrigger EventName="ValueChanged">
                <i:InvokeCommandAction Command="{Binding RecomputeCommand}" />
            </i:EventTrigger>
        </i:Interaction.Triggers>
    </Slider>
    <Slider Value="{Binding B}" Maximum="255">
        <i:Interaction.Triggers>
            <i:EventTrigger EventName="ValueChanged">
                <i:InvokeCommandAction Command="{Binding RecomputeCommand}" />
            </i:EventTrigger>
        </i:Interaction.Triggers>
    </Slider>
    <Slider Value="{Binding C}" Maximum="255">
        <i:Interaction.Triggers>
            <i:EventTrigger EventName="ValueChanged">
                <i:InvokeCommandAction Command="{Binding RecomputeCommand}" />
            </i:EventTrigger>
        </i:Interaction.Triggers>
    </Slider>
</StackPanel>

当然,根据需要,这可以是ContentControl