尝试基于另一个ComboBox值不起作用将绑定的ObservableCollection筛选到组合框

时间:2018-10-28 23:09:34

标签: c# wpf observablecollection collectionviewsource

我还看到了其他几篇有关此内容的文章,但我似乎无法确切了解如何正确使用它。

简而言之,这就是我所拥有的。

我有两个组合框-角色和职位。

我将两者都绑定到一个ObservableCollection上,该实例的Enum值转换为实例化时加载到其中的字符串。

<ComboBox  x:Name="empRoleCB" ItemsSource="{Binding Role}" SelectedItem="{Binding RoleStr}"/>
<ComboBox  x:Name="empPositionCB" ItemsSource="{Binding Pos}" SelectedItem="{Binding PosStr}"/>

在我的ViewModel中:

public abstract class EmployeeMenuVMBase : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;

    protected bool SetProperty<T>(ref T field, T newValue, [CallerMemberName] string propertyName = null)
    {
        if(!EqualityComparer<T>.Default.Equals(field, newValue))
        {
            field = newValue;
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
            return true;
        }
        return false;
    }
}

class EmployeeMenuVM : EmployeeMenuVMBase
{
    private ObservableCollection<string> _pos = new ObservableCollection<string>(Enum.GetNames(typeof(Global.Positions)));
    private ObservableCollection<string> _role = new ObservableCollection<string>(Enum.GetNames(typeof(Global.Roles)));
    public ObservableCollection<string> Pos { get => _pos; }
    public ObservableCollection<string> Role { get => _role; }
    public string RoleStr
    {
        get => _roleStr;
        set => SetProperty(ref _roleStr, value);
    }
    public string PosStr
    {
        get => _posStr;
        set => SetProperty(ref _posStr, value);
    }
}

我要发生的事情是,当选择一个角色时,基于该选择,仅应显示某些职位。例如,如果我选择“客户服务”作为角色,那么职位应仅包含“经理”,“ CSS”和“无”。如果角色是“管理员”,那么职位应该只包含“无”,依此类推。

我所面临的难题是如何正确地对此进行过滤。我使用CollectionViewSource看到了一些东西,但不确定如何使它与我的示例一起使用。
我有5个角色,每个角色将有不同的职位清单需要显示。

使此功能与MINIMAL额外代码或XAML一起使用的最佳方法是什么?

我最不喜欢WPF的一件事是看似简单的事情,需要大量的代码才能使其多次正常工作。

3 个答案:

答案 0 :(得分:0)

首先,如果您认为WPF很复杂。因此,您使用的是错误的。

我建议您使用 CollectionViewSource 过滤器作为流:

<ComboBox  x:Name="empPositionCB" ItemsSource="{Binding MyPositionFilter}" SelectionChanged="RoleComboBox_SelectionChanged" ....../>


public ICollectionView MyPositionFilter { get; set; }

//ctor
public MyUserControlOrWindow()
{
    //Before InitComponent()
    this.MyPositionFilter = new CollectionViewSource { Source = MyPosObservableCollection }.View;


    InitComponent();
}

public void RoleComboBox_SelectionChanged(object sender,EventArgs e)
{
    //Get the selected Role (the ? is to prevent NullException (VS 2015 >))
    Role r = empRoleCB.SelectedItem as Role;

    //Apply the filter
    this.MyPositionFilter.Filter = item =>
    {
        //Make you sure to convert correcteley your Enumeration, I used it here like a class
        Position p = item as Position;

        //Put your condition here. For example:
        return r.ToLowers().Contains(p.ToLower());

        //Or

        return (r != null && r.Length >= p.Length);
    };
}

过滤器不会更改您的收藏,所有隐藏的项目都保留在您的ObservableCollection中。

答案 1 :(得分:0)

这一切都可以在ViewModel中完成,只需在角色更改时更改Positions(Pos)可观察集合的值即可。

class EmployeeMenuVM : EmployeeMenuVMBase
{
    public EmployeeMenuVM()
    {
        var emptyPositions = new List<Global.Positions>()
        { Global.Positions.None };

        _rolePositions.Add(Global.Roles.None, emptyPositions);

        var customerServicePositions = new List<Global.Positions>()
        { Global.Positions.None, Global.Positions.CSS, Global.Positions.Manager };

        _rolePositions.Add(Global.Roles.CustomerService, customerServicePositions);
    }

    private Dictionary<Global.Roles, List<Global.Positions>> _rolePositions = new Dictionary<Global.Roles, List<Global.Positions>>();

    private string _roleStr;
    private string _posStr;

    private ObservableCollection<string> _pos = new ObservableCollection<string>(Enum.GetNames(typeof(Global.Positions)));
    private ObservableCollection<string> _role = new ObservableCollection<string>(Enum.GetNames(typeof(Global.Roles)));

    public ObservableCollection<string> Pos
    {
        get => _pos;

        set
        {
            SetProperty(ref _pos, value);
        }
    }
    public ObservableCollection<string> Role
    {
        get => _role;
    }
    public string RoleStr
    {
        get => _roleStr;
        set
        {
            if (SetProperty(ref _roleStr, value))
            {
                Global.Roles role = (Global.Roles)Enum.Parse(typeof(Global.Roles), value);
                var positions = _rolePositions[role].Select(p => p.ToString());
                Pos = new ObservableCollection<string>(positions);
            }
        }
    }
    public string PosStr
    {
        get => _posStr;
        set => SetProperty(ref _posStr, value);
    }
}

答案 2 :(得分:0)

这是一个有效的测试人员代码,旨在了解如何进行过滤的主要思想:

MainWindow.xaml

<Window x:Class="WpfApplication3.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:WpfApplication3"
        x:Name="ThisView"
        mc:Ignorable="d"
        Title="MainWindow" Height="350" Width="600">
    <StackPanel Orientation="Horizontal">
        <ComboBox ItemsSource="{Binding Path=Roles, ElementName=ThisView}" 
                  SelectedItem="{Binding Path=SelectedRole, ElementName=ThisView}"
                  Width="300" Height="60"/>
        <ComboBox ItemsSource="{Binding Path=PositionCollectionView, ElementName=ThisView}" Width="300" Height="60"/>
    </StackPanel>
</Window>

MainWindow.xaml.cs

public partial class MainWindow : Window, INotifyPropertyChanged
    {
        public ICollectionView PositionCollectionView { get; set; }

        public ObservableCollection<string> Roles { get; set; } = new ObservableCollection<string>();
        public ObservableCollection<string> Positions { get; set; } = new ObservableCollection<string>();


        private string _selectedRole = String.Empty;

        public string SelectedRole
        {
            get { return _selectedRole; }
            set
            {
                _selectedRole = value;
                OnPropertyChanged();

                //This Refresh activates the Filter again, so that every time you select a role, this property will call it.
                PositionCollectionView.Refresh();

            }
        }

        public MainWindow()
        {
            PositionCollectionView = CollectionViewSource.GetDefaultView(Positions);
            PositionCollectionView.Filter = PositionsFilter;

            //use you enums here
            Roles.Add("Role1");
            Roles.Add("Role2");
            Roles.Add("Role3");
            Roles.Add("Role4");

            Positions.Add("Position1");
            Positions.Add("Position2");
            Positions.Add("Position3");
            Positions.Add("Position4");

            InitializeComponent();
        }

        private bool PositionsFilter(object position)
        {
            bool result = true;

            //place your code according to the Role selected to decide wheather "position" should be in the position list or not

            return result;
        }

        public event PropertyChangedEventHandler PropertyChanged;

        [NotifyPropertyChangedInvocator]
        protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
        }
    }

希望有帮助。