2012-04-09 77 views
6

我有一个ComboBox,其属性ItemsSource和SelectedValue绑定到模型。有时,模型需要将所选项目调整为不同的模型,但是当我在模型中执行该操作时,即使SelectedValue已正确设置(通过snoop和SelectionChanged同时检查,模型值并未反映在视图中事件处理程序)。为什么WPF组合框不能正确反映绑定值?

为了说明问题,这里是一个简单的XAML:

<Window x:Class="WpfApplication1.MainWindow" 
     xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
     Title="MainWindow" Height="350" Width="525" DataContext="{Binding RelativeSource={RelativeSource Self}}"> 
    <Grid> 
     <ComboBox Height="25" Width="120" SelectedValue="{Binding SelectedValue}" SelectedValuePath="Key" ItemsSource="{Binding PossibleValues}" DisplayMemberPath="Value"/> 
    </Grid> 
</Window> 

这里是模型:

using System.Collections.Generic; 
using System.Windows; 
using System.ComponentModel; 

namespace WpfApplication1 
{ 
    public partial class MainWindow : Window, INotifyPropertyChanged 
    { 
     int m_selectedValue = 2; 
     Dictionary<int, string> m_possibleValues = new Dictionary<int, string>() { { 1, "one" }, { 2, "two" }, { 3, "three" }, {4,"four"} }; 

     public int SelectedValue 
     { 
     get { return m_selectedValue; } 
     set 
     { 
      if (value == 3) 
      { 
       m_selectedValue = 1; 
      } 
      else 
      { 
       m_selectedValue = value; 
      } 
      PropertyChanged(this, new PropertyChangedEventArgs("SelectedValue")); 
     } 
     } 
     public Dictionary<int, string> PossibleValues 
     { 
     get { return m_possibleValues; } 
     set { m_possibleValues = value; } 
     } 


     public MainWindow() 
     { 
     InitializeComponent(); 
     } 

     public event PropertyChangedEventHandler PropertyChanged; 
    } 
} 

我所期望的行为是如下:

  1. 最初,选择两个
  2. 选择“one” - >组合框显示YS “一个”
  3. 选择 “二” - >组合框显示 “二”
  4. 选择 “三化” - >组合框显示 “一个
  5. 选择 “四大” - >组合框显示 “四大”

然而,在#4中显示“三”。为什么?模型中的值更改为1(“1”),但View仍显示3(“三”)。

我已经找到了一个解决方法,通过显式更新SelectionChanged事件处理程序中的绑定目标,但这似乎是错误的。是否有另一种方法来实现这一目标?

+0

不应该ComboBox显示模型的当前状态?它可能不直观,但它是正确的显示。这实际上是一个简单的例子。实际使用情况有点复杂,但归结为同一问题 – Rado 2012-04-09 20:46:52

+0

您是否尝试过明确地使SelectedValue双向? {Binding Path = SelectedValue,Mode = TwoWay} – 2012-04-09 21:03:41

+0

@JacobProffitt模式默认为TwoWay,但我只是试图明确设置它,问题依然存在。 – Rado 2012-04-09 21:21:45

回答

5

当您选择一个项目时,绑定引擎将更新模型,并且将忽略任何PropertyChanged它在调用属性设置器期间刚刚更改的属性。如果没有,事情可能会陷入无限循环。

系统假定setter不会更改它传递的值。此处的解决方法很简单:使用Dispatcher.BeginInvoke()或在绑定上设置IsAsync=True以获取要查找的行为。不过,我个人的强烈不鼓励你这样做。不仅因为它引入了一系列与时间有关的新问题,而且主要是因为它是解决不应该首先存在的问题的解决方法。

只是不要这样做。它不仅适用于WPF:任何改变setter的东西都可以期望它刚刚设置的值被正确设置,也不会引发异常。改变setter内部的值是违反直觉的,并且可能会在稍后咬你。另外,应用程序的用户可能会看到组合框忽略了它的选择,这实在令人不安。

如果您不喜欢所传递的值,请抛出异常并使用Binding.ValidatesOnExceptions来告诉WPF它是预期的。如果您不希望用户选择此值,请不要将它放在列表中。如果该项目由于其他业务规则而不应有条件可用,请动态过滤列表或应用触发器。

+0

'IsAsync = True'有效。阅读描述,我不确定它为什么会这样做,因为它不是为了这个,而是它:)。你提到你不鼓励这种实现。任何建议,我可以做什么呢?实际使用情况是这样的:我有一个预定义数量的值配置文件,我必须尝试近似。由于用户应该选择其中一个预定义的值,因此这种映射非常适合使用组合框模式。但是,在某些情况下,当我无法达到所选值时,为了不误导用户,我喜欢将值更改为“中间值”。 – Rado 2012-04-10 12:48:44

+0

要增加上述评论,另一种思考这个制定者做什么的方式是将其视为一个“愿望”。如在“我希望设置这个值,但如果你不能,尽可能接近” – Rado 2012-04-10 13:07:11

+1

我不同意。拥有“二传手”财产的关键在于保护支持领域(可以公开)。财产应该对价值进行检查和确认,并确保其有效性。如果不是,抛出异常并不总是一个实际的解决方案。 – 2013-05-31 15:50:37

2

当绑定到控件的SelectedValue和/或SelectedItem属性时,我遇到了适当更新控件的问题。

为了解决这个问题,我不得不绑定到SelectedIndex属性。处理ViewModel中的'index'属性更麻烦,但它解决了ComboBox未更新的问题。

+0

我实际上尝试了所有SelectedValue,SelectedItem和SelectedIndex具有相同的结果。 – Rado 2012-04-10 12:45:51