2012-04-09 76 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