【问题标题】:How can I tie two combo boxes together in wpf如何在 wpf 中将两个组合框绑定在一起
【发布时间】:2013-11-27 07:15:51
【问题描述】:

我有 2 个组合框,一个包含“项目”列表,另一个包含“子项目”列表。

子项列表取决于当前选择的项。

我已经完成了大部分工作(通过将 Subitems 的 ItemSource 绑定到 PossibleSubitems 属性),但是问题是当我更改 Item 并且 Subitem 不再对新项目有效时。在这种情况下,我只想选择第一个有效的子项,但我得到了一个空白的组合框。请注意,我认为类中的属性设置正确,但绑定似乎没有正确反映它。

这里有一些代码可以告诉你我在做什么。在这种情况下,我有: '项目 1' 可以有子项目 A 或子项目 B 和 'Item 2' 可以有子项 B 或子项 C

当我选择了子项 A 后切换到第 2 项时出现问题。

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));
      }
    }

  }
}

【问题讨论】:

    标签: c# wpf mvvm binding combobox


    【解决方案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));
            }
        }
    }
    

    【讨论】:

    • 同意其他发布者关于采用 MVVM 方法的观点。不过,这里的关键是您需要明确地对强制进行建模,而不是绕开它。
    • 我不是 100% 确定如果他以正确的方式进行操作,他是否需要这样做……我相信 INPC 会解决这个问题……
    • 您可以使用 INPC 完成此操作,但效率低下,并且在使用更复杂的模型时可能会崩溃一次,而这些模型的准确性很重要。过去我把自己绑在了结上,没有明确地这样做。我认为最好从一开始就明确。 YMMV...
    • 那个代码太糟糕了(后面的代码,不是你的)......是的,它可以被黑客攻击,但正确地做会好得多,这样他可以将它扩展为嗯...
    • 它更好,但是从第2项切换回项目1(选择子项c时)不起作用,但是踩到它跟随相同的代码路径的代码!这一点,再加上它让我觉得我在这里有点偏离滑雪道。我想我需要回去阅读更多 MVVM 的东西......
    【解决方案2】:

    您通知了错误的属性。在您的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");
        }
    }
    

    所以基本上,当当前项目发生变化时,它会创建新的子项目集合...
    丑陋的 !!!我知道...您可以重复使用这些集合,并做很多其他事情...但正如我所说,我很好奇是否可以这样做... :)

    如果这弄坏了你的键盘,或者你的猫跑掉了,我不承担任何责任。

    【讨论】:

    • 但是当我更改项目时,可能是子项目发生了变化(当然我应该通知 CurrentItem 也发生了变化)。如果我没有通知可能的子项,则子项组合框根本不会更新并显示完全不正确的值而不是空白
    • 嗯......你改变了 CurrentItem......问题是,我对你的实现很不满意......我会选择一些 MVVM(不是背后的代码),并且有 2 个不同的子项列表(您仍然可以这样做)。将子项集合绑定到正确的集合,当您更改 CurrentItem 时,您可以执行PossibleItems = this_or_that_collection 之类的操作。然后它会改变,INPC 属性也会触发,gui 也会改变
    • Darnit ...我应该把它放在答案中并用他们 10 个代表点来做某事.... :)
    • 我不确定我错过了什么,但恐怕无法让您的代码正常工作。我明白了这个理论,但实际上,当我切换项目时,它只会给我留下一个空白的子项目复选框。
    猜你喜欢
    • 2015-10-19
    • 2012-11-12
    • 2012-07-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多