2013-11-14 83 views
0

我有兩個組合框,一個包含'Items'列表,另一個包含'Subitems'列表。如何將兩個組合框綁在一起wpf

子項目列表取決於當前選擇的項目。

我已經得到了大部分工作(通過將子項的ItemSource綁定到PossibleSubitems屬性),但是問題是當我更改Item並且Subitem不再對新項目有效時。在這種情況下,我只想選擇第一個有效的子項目,但是我得到一個空白的組合框。請注意,我認爲該類中的屬性設置正確,但綁定似乎沒有正確反映它。

下面是一些代碼,告訴你我在做什麼。在這種情況下,我有: 「項目1」可以具有子項A或子項B和 「項目2」可以具有子項B或子項Ç

當我切換到第2項時,我有這個問題是選擇子項A.

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="134" Width="136"> 
    <StackPanel Height="Auto" Width="Auto"> 
    <ComboBox ItemsSource="{Binding PossibleItems, Mode=OneWay}" Text="{Binding CurrentItem}"/> 
    <ComboBox ItemsSource="{Binding PossibleSubitems, Mode=OneWay}" Text="{Binding CurrentSubitem}"/> 
    </StackPanel> 
</Window> 

代碼背後:

using System; 
using System.Collections.ObjectModel; 
using System.ComponentModel; 
using System.Windows; 

namespace WpfApplication1 
{ 
    public partial class MainWindow : Window, INotifyPropertyChanged 
    { 
    // List of potential Items, used to populate the options for the Items combo box 
    public ObservableCollection<string> PossibleItems 
    { 
     get 
     { 
     ObservableCollection<string> retVal = new ObservableCollection<string>(); 
     retVal.Add("Item 1"); 
     retVal.Add("Item 2"); 
     return retVal; 
     } 
    } 

    // List of potential Items, used to populate the options for the Subitems combo box 
    public ObservableCollection<string> PossibleSubitems 
    { 
     get 
     { 
     ObservableCollection<string> retVal = new ObservableCollection<string>(); 
     if (CurrentItem == PossibleItems[0]) 
     { 
      retVal.Add("Subitem A"); 
      retVal.Add("Subitem B"); 
     } 
     else 
     { 
      retVal.Add("Subitem B"); 
      retVal.Add("Subitem C"); 
     } 
     return retVal; 
     } 
    } 

    // Track the selected Item 
    private string _currentItem; 
    public string CurrentItem 
    { 
     get { return _currentItem; } 
     set 
     { 
     _currentItem = value; 
     // Changing the item changes the possible sub items 
     NotifyPropertyChanged("PossibleSubitems"); 
     } 
    } 

    // Track the selected Subitem 
    private string _currentSubitem; 
    public string CurrentSubitem 
    { 
     get { return _currentSubitem; } 
     set 
     { 
     if (PossibleSubitems.Contains(value)) 
     { 
      _currentSubitem = value; 
     } 
     else 
     { 
      _currentSubitem = PossibleSubitems[0]; 
      // We're not using the valuie specified, so notify that we have in fact changed 
      NotifyPropertyChanged("CurrentSubitem"); 
     } 
     } 
    } 


    public MainWindow() 
    { 
     InitializeComponent(); 

     this.DataContext = this; 
     CurrentItem = PossibleItems[0]; 
     CurrentSubitem = PossibleSubitems[0]; 
    } 

    public event PropertyChangedEventHandler PropertyChanged; 
    internal void NotifyPropertyChanged(String propertyName = "") 
    { 
     if (PropertyChanged != null) 
     { 
     PropertyChanged(this, new PropertyChangedEventArgs(propertyName)); 
     } 
    } 

    } 
} 

回答

1

我改寫了我自己的樣品 - 保留了它後面的代碼,以便不從你的樣品偏離太多。另外,我正在使用.NET 4.5,因此不必在OnPropertyChanged調用中提供屬性名稱 - 如果在.NET 4.0上,則需要插入它們。這適用於所有情況。

實際上,我建議根據MVVM模式將此代碼定位到視圖模型中。除了DataContext的綁定之外,它與這個實現並沒有太大的不同。

using System.Collections.ObjectModel; 
using System.ComponentModel; 
using System.Runtime.CompilerServices; 
using System.Windows; 

namespace WpfApplication1 
{ 
    public partial class MainWindow: Window, INotifyPropertyChanged 
    { 
     private string _currentItem; 
     private string _currentSubitem; 
     private ObservableCollection<string> _possibleItems; 
     private ObservableCollection<string> _possibleSubitems; 

     public MainWindow() 
     { 
      InitializeComponent(); 

      LoadPossibleItems(); 
      CurrentItem = PossibleItems[0]; 

      UpdatePossibleSubItems(); 

      DataContext = this; 
      CurrentItem = PossibleItems[0]; 
      CurrentSubitem = PossibleSubitems[0]; 

      PropertyChanged += (s, o) => 
       { 
        if (o.PropertyName != "CurrentItem") return; 
        UpdatePossibleSubItems(); 
        ValidateCurrentSubItem(); 
       }; 
     } 

     private void ValidateCurrentSubItem() 
     { 
      if (!PossibleSubitems.Contains(CurrentSubitem)) 
      { 
       CurrentSubitem = PossibleSubitems[0]; 
      } 
     } 

     public ObservableCollection<string> PossibleItems 
     { 
      get { return _possibleItems; } 
      private set 
      { 
       if (Equals(value, _possibleItems)) return; 
       _possibleItems = value; 
       OnPropertyChanged(); 
      } 
     } 

     public ObservableCollection<string> PossibleSubitems 
     { 
      get { return _possibleSubitems; } 
      private set 
      { 
       if (Equals(value, _possibleSubitems)) return; 
       _possibleSubitems = value; 
       OnPropertyChanged(); 
      } 
     } 

     public string CurrentItem 
     { 
      get { return _currentItem; } 
      private set 
      { 
       if (value == _currentItem) return; 
       _currentItem = value; 
       OnPropertyChanged(); 
      } 
     } 

     public string CurrentSubitem 
     { 
      get { return _currentSubitem; } 
      set 
      { 
       if (value == _currentSubitem) return; 
       _currentSubitem = value; 
       OnPropertyChanged(); 
      } 
     } 

     public event PropertyChangedEventHandler PropertyChanged; 

     private void LoadPossibleItems() 
     { 
      PossibleItems = new ObservableCollection<string> 
       { 
        "Item 1", 
        "Item 2" 
       }; 
     } 

     private void UpdatePossibleSubItems() 
     { 
      if (CurrentItem == PossibleItems[0]) 
      { 
       PossibleSubitems = new ObservableCollection<string> 
        { 
         "Subitem A", 
         "Subitem B" 
        }; 
      } 

      else if (CurrentItem == PossibleItems[1]) 
      { 
       PossibleSubitems = new ObservableCollection<string> 
        { 
         "Subitem B", 
         "Subitem C" 
        }; 
      } 
     } 

     protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null) 
     { 
      PropertyChangedEventHandler handler = PropertyChanged; 
      if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName)); 
     } 
    } 
} 
+0

同意其他海報對採用MVVM方法的評論。這裏的關鍵在於你需要明確地模擬Coersion而不是繞開它。 –

+0

我不是100%肯定他需要,如果他以正確的方式做...我確信INPC會照顧那個...... – Noctis

+0

你可以使用INPC做到這一點,但效率不高,並且可能會在更復雜的模型中爆炸一次,而這些模型在事前很重要。過去我並沒有明確地把自己束縛住。我認爲最好從一開始就明確。 YMMV ... –

1

您正在通知錯誤的屬性。在您的CurrentItem上,您撥打"PossibleSubitems"

private string _currentItem; 
public string CurrentItem 
{ 
    get { return _currentItem; } 
    set 
    { 
    _currentItem = value; 
    // Changing the item changes the possible sub items 
    NotifyPropertyChanged("PossibleSubitems"); 
    } 
} 

修復了,然後再試一次:)


警告...這是一個黑客... 我改變了這使它工作(只是因爲我很好奇)但這並不意味着該適當的方式,也不是一個優雅的一個:

// List of potential Items, used to populate the options for the Subitems combo box 
public ObservableCollection<string> PossibleSubitems { get; set; } 

// Track the selected Item 
private string _currentItem; 
public string CurrentItem 
{ 
    get { return _currentItem; } 
    set 
    { 
     _currentItem = value; 
     // Changing the item changes the possible sub items 
     if (value == "Item 1") 
     PossibleSubitems = new ObservableCollection<string>() {"A","B"} ; 
     else 
     PossibleSubitems = new ObservableCollection<string>() { "C", "D" }; 


     RaisePropertyChanged("CurrentItem"); 
     RaisePropertyChanged("PossibleSubitems"); 
    } 
} 

所以基本上,當電流項目更改,它會創建新的子集合...
醜陋!!!我知道......你可以重複使用這些藏品,並做很多其他的事情......但正如我所說,我很好奇它是否可以這樣做...... :)

如果這打破了你的鍵盤,或你的貓跑掉了,我不承擔任何責任。

+0

但是,當我改變項目,這是可能的子項目的變化(我可能應該通知CurrentItem已經改變)。如果我沒有通知PossibleSubItems,那麼SubItems組合框根本不會更新,並顯示一個完全不正確的值,而不是空白 – steeveeet

+1

呃...你改變了CurrentItem ...事情是,我寧願對你的實現不滿意......我會用一些MVVM(不是代碼背後),並有2個不同的子項列表(你仍然可以這樣做)。讓子項目集合綁定到正確的子項,並且當您更改CurrentItem時,可以執行像PossibleItems = this_or_that_collection這樣的操作。然後它會改變,並且INPC屬性也會啓動,並且gui將改變 – Noctis

+0

對於MVVM評論 –