2012-11-27 35 views
2

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

这个想法是,当用户通过滑块或文本框输入新值时,这些值应该通过转换器进行“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); } 
     } 
    } 

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

    } 
} 
+0

你的'OnSliderChnaged'方法是做什么的?因为你有'TwoWay'绑定,'ConvertBack'将会被调用一次,所以在你的'ValueChanged'事件中第二次调用它将使它运行两次。另外,我仍然试图在后面的代码中找出你的'MultiBinding',但它可能会让它运行两次。 – Rachel

+0

OnSliderChnaged方法没有实现,它只是从一些实验中遗留下来,整个应用程序就像我上面发布的那样。问题是,当用户编辑滑块/文本框时,从不会调用convertback,这正是我试图解决/补救的问题 – wforl

+1

我开始认为存在一个体系结构问题。你试图通过让setter被调用来达到什么目的?为什么当一个滑块绑定到一个变量时会调用所有3个setter?你在计算吗? –

回答

0

您需要在ViewModel中实现INotifyPropertyChanged。
这里,视图模型是窗口,所以你要做的:

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需要。

+0

我不明白这是如何帮助我的,问题在于这些值并没有被写回源头。通过将源代码转换为目标,转换工作是一种方式。..但是当用户敲击目标时,源不会被更新 – wforl

+0

@wforl如果您不提升PropertyChanged,如何得到通知?无论何时写'Binding',Path都必须指向一个PropertyChanged-ready属性,否则该值仅在初始化时被检索。 –

+0

@狒狒。问题不在于被检索的值,..我实际上只想在初始化时读取它。问题是TextSplitterControl不会写回到MainWindow.A | MainWindow.B | MainWindow.C – wforl