在列表框中过滤组合框
我试图创建一个UserControl来显示一个ListBox(我想在稍后绑定到视图模型中的一个集合),以便每个项目都显示在一个ComboBox中(这允许打开它的下拉菜单,并且不同的值将被选择)。 我也想确保没有值可以被选择两次,我想添加一个按钮来创建额外的列表项。
我的想法是让ListBox的每个DataTemplate在其资源中都包含一个CollectionViewSource,对其进行过滤,然后将组合框绑定到过滤的值。 我的问题是,我不明白如何让绑定在这种情况下工作 - 只要我的绑定是单向的,一切都可以正常工作,但是即时我使ComboBoxes双向绑定,我得到一个异常告诉我我需要为双向绑定工作设置一个路径。
我的XAML(为了清晰起见略作删节):
<UserControl x:yadayada x:Name="MultiSelectList">
<Grid>
<ListBox ItemsSource="{Binding ElementName=MultiSelectList, Path=ChosenItems, Mode=TwoWay}">
<ListBox.ItemTemplate>
<DataTemplate>
<ComboBox Loaded="FrameworkElement_OnLoaded" DropDownOpened="ComboBox_OnDropDownOpened">
<ComboBox.Resources>
<CollectionViewSource Source="{Binding ElementName=MultiSelectList, Path=AllItems}" x:Key="Src" />
</ComboBox.Resources>
<ComboBox.ItemsSource>
<Binding Source="{StaticResource Src}" />
</ComboBox.ItemsSource>
</ComboBox>
<!-- uncommenting the following line crashes the program -->
<!-- <ComboBox.SelectedValue><Binding></Binding></ComboBox.SelectedValue> -->
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
<Button Content="New" Click="NewButton_Pressed"/>
</Grid>
后面的代码:
using System;
using System.Collections.ObjectModel;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
namespace WPFCentralOffice.UserControls
{
public class Element : IEquatable<Element>
{
public int ID { get; set; }
public string Caption { get; set; }
public bool Equals(Element other)
{
return ID == other.ID;
}
public override string ToString()
{
return ID + " " + Caption;
}
}
public partial class MultiSelectListUserControl : UserControl
{
public ObservableCollection<Element> ChosenItems
{
get { return (ObservableCollection<Element>)GetValue(ChosenItemsProperty); }
set { SetValue(ChosenItemsProperty, value); }
}
public ObservableCollection<Element> AllItems
{
get { return (ObservableCollection<Element>)GetValue(AllItemsProperty); }
set { SetValue(AllItemsProperty, value); }
}
public static DependencyProperty AllItemsProperty = DependencyProperty.Register(
"AllItems",
typeof(ObservableCollection<Element>),
typeof(MultiSelectListUserControl));
public static DependencyProperty ChosenItemsProperty = DependencyProperty.Register(
"ChosenItems",
typeof(ObservableCollection<Element>),
typeof(MultiSelectListUserControl));
public void NewButton_Pressed(object sender, RoutedEventArgs e)
{
if (!AllItems.Any() || AllItems.Count == ChosenItems.Count)
{
return;
}
var elem = AllItems.First(x => ChosenItems.All(y => x.ID != y.ID));
ChosenItems.Add(elem);
}
public MultiSelectListUserControl()
{
InitializeComponent();
SetValue(AllItemsProperty, new ObservableCollection<Element>());
SetValue(ChosenItemsProperty, new ObservableCollection<Element>());
}
private void ComboBox_OnDropDownOpened(object sender, EventArgs e)
{
var c = (ComboBox)sender;
c.Items.Filter = x => ChosenItems.All(y => ((Element)x).ID != y.ID) || ((Element)c.SelectedValue).ID == ((Element)x).ID;
}
}
}
这不仅不起作用,它也感觉像是一种实现我想要的非常复杂的方式。 是否有一个更简单的方法来获得具有不同值的组合框列表? 或者有人可以给我指针,这样我就可以正确实现它了吗?
我最终通过@Ed Plunkett的建议使用了ComboBox
es绑定的代理对象。 这允许我在相关操作(如添加新列表项或在组合框中选择不同值)触发时刷新每个ComboBox
的可用项目,只需替换每个ComboBoxBindingSource
上的AvailableItems
属性值即可。
这也允许XAML更清洁,因为每个ComboBox
现在都有适当的绑定:
<UserControl x:foobar x:Name="MultiSelectList">
<Grid>
<ListBox ItemsSource="{Binding ElementName=MultiSelectList, Path=ComboBoxBindingSources, Mode=TwoWay}">
<ListBox.ItemTemplate>
<DataTemplate>
<ComboBox ItemsSource="{Binding Path=AvailableElements}" SelectedValue="{Binding Path=SelectedElement}" SelectionChanged="ComboBox_SelectionChanged" />
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
<Button Content="New" Click="NewButton_Pressed"/>
</Grid>
</UserControl>
后面的工作代码是这样的:
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
namespace WPFCentralOffice.UserControls
{
public class Element : IEquatable<Element>
{
public int ID { get; set; }
public string Caption { get; set; }
public bool Equals(Element other)
{
return ID == other.ID;
}
public override string ToString()
{
return Caption;
}
}
public class ComboBoxBindingSource : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public ComboBoxBindingSource(Element selectedElement, IEnumerable<Element> availableElements)
{
_selectedElement = selectedElement;
AvailableElements = availableElements;
}
private Element _selectedElement;
public Element SelectedElement
{
get { return _selectedElement; }
set
{
_selectedElement = value;
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs("SelectedElement"));
}
}
}
private IEnumerable<Element> _availableElements;
public IEnumerable<Element> AvailableElements
{
get { return _availableElements; }
set
{
_availableElements = value;
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs("AvailableElements"));
}
}
}
}
public partial class MultiSelectListUserControl : UserControl
{
public IEnumerable<Element> ChosenItems
{
get { return ComboBoxBindingSources.Select(x => x.SelectedElement); }
}
public ObservableCollection<Element> AllItems
{
get { return (ObservableCollection<Element>)GetValue(AllItemsProperty); }
set { SetValue(AllItemsProperty, value); }
}
// ReSharper disable once InconsistentNaming
public ObservableCollection<ComboBoxBindingSource> ComboBoxBindingSources
{
get { return (ObservableCollection<ComboBoxBindingSource>)GetValue(ComboBoxBindingSourcesProperty); }
set { SetValue(ComboBoxBindingSourcesProperty, value); }
}
public static DependencyProperty AllItemsProperty = DependencyProperty.Register(
"AllItems",
typeof(ObservableCollection<Element>),
typeof(MultiSelectListUserControl),
new FrameworkPropertyMetadata(OnAllItemsChanged));
private static void OnAllItemsChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
((MultiSelectListUserControl)d).RefreshAvailableItems();
}
public static DependencyProperty ComboBoxBindingSourcesProperty = DependencyProperty.Register(
"ComboBoxBindingSources",
typeof(ObservableCollection<ComboBoxBindingSource>),
typeof(MultiSelectListUserControl));
public void NewButton_Pressed(object sender, RoutedEventArgs e)
{
if (!AllItems.Any() || AllItems.Count == ComboBoxBindingSources.Count)
{
return;
}
var remainingItems = AllItems.Where(x => ChosenItems.All(y => x.ID != y.ID)).ToList();
ComboBoxBindingSources.Add(new ComboBoxBindingSource(remainingItems.First(), remainingItems));
RefreshAvailableItems();
}
public void ComboBox_SelectionChanged(object sender, RoutedEventArgs e)
{
RefreshAvailableItems();
}
private void RefreshAvailableItems()
{
if (ComboBoxBindingSources == null)
{
ComboBoxBindingSources = new ObservableCollection<ComboBoxBindingSource>();
}
foreach (var source in ComboBoxBindingSources)
{
var newAvailables =
AllItems.Where(
x => source.SelectedElement == x || ComboBoxBindingSources.All(y => y.SelectedElement != x));
source.AvailableElements = newAvailables;
}
}
public MultiSelectListUserControl()
{
InitializeComponent();
SetValue(AllItemsProperty, new ObservableCollection<Element>());
}
}
}
链接地址: http://www.djcxy.com/p/41267.html