首页 文章

WPF Combobox SelectedItem不在Itemssource中

提问于
浏览
1

即使它不在ItemsSource中,如何显示ComboBox的SelectedItem?

就像一个简单的例子......

假设我有一个“Class”对象,上面有一个“Teacher”属性 .

public class Class: INotifyPropertyChanged
{
   private Individual _teacher
   public Individual Teacher
   {
      get { return _teacher; }
      set 
      {
         teacher = value;
         RaisePropertyChanged("Teacher");
      }
   }
   ...
}

在“维护类”GUI上,有一个ComboBox可以选择一个教师,我只希望活动的个人出现在ComboBox中 . 我不希望用户能够在ComboBox中键入自由格式文本 . 为了实现这一点,我将ItemsSource绑定到我的ViewModel中的一个集合,该集合仅包含活动的个人,并且SelectedItem绑定到我的“Class”对象的Teacher属性 .

public class MaintainClasses_ViewModel:INotifyPropertyChanged
{
    private ObservableCollection<Individual> _activeIndividuals
        = GetAllActiveIndividuals();

    public ObservableCollection<Individual> ActiveIndividuals
    {
        get { return _activeIndividuals
    }

    public Class SelectedClass
    {
        get; 
        set;
    }
}

使用我的ComboBox的xaml ...

<ComboBox ItemsSource="{Binding ActiveIndividuals}" 
          SelectedItem="{Binding SelectedClass.Teacher}" />

现在假设我打开一个类的“维护类”GUI,其中已经保存的教师现在处于非活动状态 . 现在......我只想让活跃的个人出现在组合框-PLUS中,这是之前选择的老师(即使他们现在处于非活动状态而不是在ItemsSource中) .

目前,我发现这样做的唯一方法是将非活动个体添加到集合中,并为集合引发PropertyChanged事件 . 但是,我真的想归档这个结果而不向集合添加内容 . 优选地,一些使用xaml,选择器和/或转换器的方法 .

1 回答

  • 1

    这是我一直在使用的,我希望它有助于:

    ComboBoxAdaptor.cs:

    using System;
        using System.Collections;
        using System.Collections.Generic;
        using System.ComponentModel;
        using System.Linq;
        using System.Windows;
        using System.Windows.Controls;
        using System.Windows.Data;
        using System.Windows.Markup;
    
        namespace Adaptors
        {
            [ContentProperty("ComboBox")]
            public class ComboBoxAdaptor : ContentControl
            {
                #region Protected Properties
                protected bool IsChangingSelection
                {get; set;}
    
                protected ICollectionView CollectionView
                {get; set;}
                #endregion
    
                #region Dependency Properties
                public static readonly DependencyProperty ComboBoxProperty = 
                    DependencyProperty.Register("ComboBox", typeof(ComboBox), typeof(ComboBoxAdaptor),
                    new FrameworkPropertyMetadata(new PropertyChangedCallback(ComboBox_Changed)));
                public ComboBox ComboBox
                {
                    get { return (ComboBox)GetValue(ComboBoxProperty);}
                    set { SetValue(ComboBoxProperty, value);}
                }
    
                public static readonly DependencyProperty NullItemProperty =
                    DependencyProperty.Register("NullItem", typeof(object), typeof(ComboBoxAdaptor),
                    new PropertyMetadata("(None)");
                public object NullItem
                {
                    get {return GetValue(NullItemProperty);}
                    set {SetValue(NullItemProperty, value);}
                }
    
                public static readonly DependencyProperty ItemsSourceProperty =
                    DependencyProperty.Register("ItemsSource", typeof(IEnumerable), typeof(ComboBoxAdaptor),
                    new FrameworkPropertyMetadata(new PropertyChangedCallback(ItemsSource_Changed)));
                public IEnumerable ItemsSource
                {
                    get {return (IEnumerable)GetValue(ItemsSourceProperty);}
                    set {SetValue(ItemsSourceProperty, value);}
                }
    
                public static readonly DependencyProperty SelectedItemProperty =
                    DependencyProperty.Register("SelectedItem", typeof(object), typeof(ComboBoxAdaptor), 
                    new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault,
                    new PropertyChangedCallback(SelectedItem_Changed)));
                public object SelectedItem
                {
                    get {return GetValue(SelectedItemProperty);}
                    set {SetValue(SelectedItemProperty, value);}
                }
    
                public static readonly DependencyProperty AllowNullProperty =
                    DependencyProperty.Register("AllowNull", typeof(bool), typeof(ComboBoxAdaptor),
                    new PropertyMetadata(true, AllowNull_Changed));
                public bool AllowNull
                {
                    get {return (bool)GetValue(AllowNullProperty);}
                    set {SetValue(AllowNullProperty, value);}
                }
                #endregion
    
                #region static PropertyChangedCallbacks
                static void ItemsSource_Changed(DependencyObject d, DependencyPropertyChangedEventArgs e)
                {
                    ComboBoxAdaptor adapter = (ComboBoxAdaptor)d;
                    adapter.Adapt();
                }
    
                static void AllowNull_Changed(DependencyObject d, DependencyPropertyChangedEventArgs e)
                {
                    ComboBoxAdaptor adapter = (ComboBoxAdaptor)d;
                    adapter.Adapt();
                }
    
                static void SelectedItem_Changed(DependencyObject d, DependencyPropertyChangedEventArgs e)
                {
                    ComboBoxAdaptor adapter = (ComboBoxAdaptor)d;
                    if (adapter.ItemsSource != null)
                    {
                        //If SelectedItem is changing from the Source (which we can tell by checking if the
                        //ComboBox.SelectedItem is already set to the new value), trigger Adapt() so that we
                        //throw out any items that are not in ItemsSource.
                        object adapterValue = (e.NewValue ?? adapter.NullItem);
                        object comboboxValue = (adapter.ComboBox.SelectedItem ?? adapter.NullItem);
                        if (!object.Equals(adapterValue, comboboxValue))
                        {
                            adapter.Adapt();
                            adapter.ComboBox.SelectedItem = e.NewValue;
                        }
                        //If the NewValue is not in the CollectionView (and therefore not in the ComboBox)
                        //trigger an Adapt so that it will be added.
                        else if (e.NewValue != null && !adapter.CollectionView.Contains(e.NewValue))
                        {
                            adapter.Adapt();
                        }
                    }
                }
                #endregion
    
                #region Misc Callbacks
                void ComboBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
                {
                    if (ComboBox.SelectedItem == NullItem)
                    {
                        if (!IsChangingSelection)
                        {
                            IsChangingSelection = true;
                            try
                            {
                                int selectedIndex = ComboBox.SelectedIndex;
                                ComboBox.SelectedItem = null;
                                ComboBox.SelectedIndex = -1;
                                ComboBox.SelectedIndex = selectedIndex;
                            }
                            finally
                            {
                                IsChangingSelection = false;
                            }
                        }
                    }
                    object newVal = (ComboBox.SelectedItem == null? null: ComboBox.SelectedItem);
                    if (!object.Equals(SelectedItem, newVal)
                    {
                        SelectedItem = newVal;
                    }
                }
    
                void CollectionView_CurrentChanged(object sender, EventArgs e)
                {
                    if (AllowNull && (ComboBox != null) && (((ICollectionView)sender).CurrentItem == null) && (ComboBox.Items.Count > 0))
                    {
                        ComboBox.SelectedIndex = 0;
                    }
                }
                #endregion
    
                #region Methods
                protected void Adapt()
                {
                    if (CollectionView != null)
                    {
                        CollectionView.CurrentChanged -= CollectionView_CurrentChanged;
                        CollectionView = null;
                    }
                    if (ComboBox != null && ItemsSource != null)
                    {
                        CompositeCollection comp = new CompositeCollection();
                        //If AllowNull == true, add a "NullItem" as the first item in the ComboBox.
                        if (AllowNull)
                        {
                            comp.Add(NullItem);
                        }
                        //Now Add the ItemsSource.
                        comp.Add(new CollectionContainer{Collection = ItemsSource});
                        //Lastly, If Selected item is not null and does not already exist in the ItemsSource,
                        //Add it as the last item in the ComboBox
                        if (SelectedItem != null)
                        {
                            List<object> items = ItemsSource.Cast<object>().ToList();
                            if (!items.Contains(SelectedItem))
                            {
                                comp.Add(SelectedItem);
                            }
                        }
                        CollectionView = CollectionViewSource.GetDefaultView(comp);
                        if (CollectionView != null)
                        {
                            CollectionView.CurrentChanged += CollectionView_CurrentChanged;
                        }
                        ComboBox.ItemsSource = comp
                    }
                }
                #endregion
            }
        }
    

    How To Use It In Xaml

    <adaptor:ComboBoxAdaptor 
             NullItem="Please Select an Item.."
             ItemsSource="{Binding MyItemsSource}"
             SelectedItem="{Binding MySelectedItem}">
          <ComboBox Width="100" />
    </adaptor:ComboBoxAdaptor>
    

    Some Notes

    如果SelectedItem更改为不在ComboBox中的值,则它将添加到ComboBox(但不是ItemsSource) . 下次通过Binding更改SelectedItem时,任何不在ItemsSource中的项目都将从ComboBox中删除 .

    此外, ComboBoxAdaptor 允许您将Null项插入到ComboBox中 . 这是一个可选功能,您可以通过在xaml中设置 AllowNull="False" 来关闭它 .

相关问题