2

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

这个想法是,当用户通过滑块或文本框输入新值时,这些值应该通过转换器“ConvertedBack”,并更新源。不过,我似乎没有看到这一点,我相信是因为正在写入 InternalRep 的属性,但没有通知 InternalRepProperty 的绑定表达式。

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

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

当用户更改一个值时,我需要转换器仅 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>

文本分割器 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); }
        }
    }

主窗口 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>

主窗口代码

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

    }
}
4

1 回答 1

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根据需要进行。

于 2012-11-27T12:59:08.397 回答