在尝试解决我在另一个项目中遇到的问题时 - 我创建了以下示例来复制该问题。
这个想法是,当用户通过滑块或文本框输入新值时,这些值应该是" 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; }
}
}
}
答案 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
。