代码之家  ›  专栏  ›  技术社区  ›  MattE

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

  •  1
  • MattE  · 技术社区  · 6 年前

    我看到了一些关于这个的其他帖子,但我似乎不能确切地理解如何让我的使用这个工作正常。

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

    我将这两个都绑定到ObservaleCollection,它在实例化时将枚举值转换为加载到其中的字符串。

    <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”和“无”。如果角色是“Admin”,那么Position应该只包含“None”,以此类推。


    用最少的额外代码或XAML实现这一点的最佳方法是什么?

    3 回复  |  直到 6 年前
        1
  •  0
  •   nadjibnet    6 年前

    过滤器 属于 集合视图源

    <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中。

        2
  •  0
  •   Kip Morgan    6 年前

    这一切都可以在ViewModel中完成,方法是在角色更改时更改Positions(Pos)observable集合的值。

    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);
        }
    }
    
        3
  •  0
  •   Eibi    6 年前

    下面是一个正在运行的测试程序代码,旨在了解如何进行过滤的主要思想:

    主窗口.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>
    

    主窗口.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));
            }
        }
    

    希望有帮助。。