如果ComboBox的SelectedItem不在ItemsSource中,它是如何显示的?
就像一个简单的例子......
假设我有一个" Class"教师"教师"它的财产。
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}" />
现在假设我打开了#34;维护课程&#34;已保存的教师现在处于非活动状态的类的GUI。现在......我只想让活跃的个人出现在组合框-PLUS中,这是之前选择的老师(即使他们现在处于非活动状态而不是在ItemsSource中)。
目前,我发现这样做的唯一方法是将非活动个体添加到集合中,并为集合引发PropertyChanged事件。但是,我真的想归档这个结果而不向集合中添加内容。优选地,一些使用xaml,选择器和/或转换器的方法。
答案 0 :(得分: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
}
}
如何在Xaml中使用
<adaptor:ComboBoxAdaptor
NullItem="Please Select an Item.."
ItemsSource="{Binding MyItemsSource}"
SelectedItem="{Binding MySelectedItem}">
<ComboBox Width="100" />
</adaptor:ComboBoxAdaptor>
一些笔记
如果SelectedItem更改为不在ComboBox中的值,它将被添加到ComboBox(但不是ItemsSource)。下次通过Binding更改SelectedItem时,任何不在ItemsSource中的项目都将从ComboBox中删除。
此外,ComboBoxAdaptor
允许您将Null项插入到ComboBox中。这是一项可选功能,您可以通过在xaml中设置AllowNull="False"
来关闭。