简体   繁体   English

如何在WPF中将两个组合框捆绑在一起

[英]How can I tie two combo boxes together in wpf

I have 2 combo boxes, one that contains a list of 'Items' and another that contains a list of 'Subitems'. 我有2个组合框,其中一个包含“项”列表,另一个包含“子项”列表。

The list of Subitems depends on the currently selected Item. 子项目列表取决于当前选择的项目。

I've got most of this working (by binding the ItemSource of the Subitems to a PossibleSubitems property), however the problem is when I change the Item and the Subitem is no longer valid for the new item. 我已经完成了大部分工作(通过将Subitems的ItemSource绑定到PossibleSubitems属性),但是问题是当我更改Item且Subitem对新项目不再有效时。 In this case I just want to pick the first valid subitem, but instead I get a blank combo-box. 在这种情况下,我只想选择第一个有效的子项目,但我得到一个空白的组合框。 Note that I think that the property in the class is set correctly, but the binding doesn't seem to reflect it correctly. 请注意,我认为该类中的属性设置正确,但是绑定似乎无法正确反映它。

Here's some code to show you what I'm doing. 这是一些代码,向您展示我在做什么。 In this case, I have: 'Item 1' which can have SubItem A or Subitem B and 'Item 2' which can have SubItem B or Subitem C 在这种情况下,我具有:可以具有SubItem A或Subitem B的“ Item 1”,可以具有SubItem B或Subitem C的“ Item 2”

The problem comes when I switch to Item 2 when I have Subitem A selected. 当我选择了子项目A时,切换到项目2时就会出现问题。

XAML 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>

Code Behind: 背后的代码:

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

  }
}

I rewrote my own sample - kept it code behind so as not to deviate from your sample too much. 我重写了我自己的示例-将其代码保留在后面,以免过多偏离您的示例。 Also, I'm using .NET 4.5 so didn't have to provide property names in OnPropertyChanged calls - you will need to insert them if on .NET 4.0. 另外,我使用的是.NET 4.5,因此不必在OnPropertyChanged调用中提供属性名称-如果在.NET 4.0上,则需要插入属性名称。 This works in all scenarios. 这适用于所有情况。

In practice, I'd recommend locating this code in a view-model as per the MVVM pattern. 在实践中,我建议按照MVVM模式在视图模型中定位此代码。 Aside from the binding of the DataContext, It wouldn't look too different from this implemenation though. 除了绑定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));
        }
    }
}

You're notifying the wrong property. 您正在通知错误的属性。 On your CurrentItem , you call the "PossibleSubitems" . CurrentItem ,您称为"PossibleSubitems"

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

Fix that and try again :) 解决此问题,然后重试:)


WARNING ... THIS IS A HACK ... I changed this to make it work (just because I was curious), but this is by no mean the proper way, nor an elegant one: 警告...这是一个问题...我更改了此设置以使其正常工作(只是因为我很好奇),但这绝不是正确的方法,也不是一种优雅的方法:

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

So basically, when current item change, it'll create new collection of subitems ... 因此,基本上,当当前项目更改时,它将创建新的子项目集合...
UGLY !!! 丑陋 !!! I know ... You could reuse those collections, and do lots of other things ... but as I said, I was curious about if it can be done this way ... :) 我知道...您可以重用这些集合,并做很多其他事情...但是正如我所说,我很好奇是否可以通过这种方式完成... :)

If this breaks your keyboard, or your cat runs away, I TAKE NO RESPONSIBILITY WHATSOEVER. 如果这弄坏了您的键盘,或者您的猫逃走了,我将不承担任何责任。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM