2

別のプロジェクトで発生している問題を解決しようとすると、問題を再現するために次の例を作成しました。

ユーザーがスライダーまたはテキストボックスを介して新しい値を入力すると、それらの値はコンバーターを介して「ConvertedBack」になり、ソースが更新されるという考え方です。InternalRepのプロパティが書き込まれているが、InternalRepPropertyのbindexpressionに通知していないため、これは表示されていないようです。

この問題を解決するための最善の方法は何ですか?

私が試した1つの方法は、スライダーの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>

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>

メインウィンドウコード

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 に答える