首页 > 解决方案 > WPF过滤的动态菜单

问题描述

我正在努力让 WPF 中的 UserControl 工作,它有一个填充了 ItemsSource 的 MenuItem,它创建了一个深度为 n 级的菜单(尽管我现在只是在看 TopMenuItem\Branches\Leaves)。我遇到的问题是我想通过嵌入菜单的文本框过滤叶子。如果一个分支没有叶子,它也会被过滤掉。目前看起来像这样:

目前看起来像这样

我正在使用 IMenuTreeItem 的 ObservableCollection,它可以包含分支(它又具有 IMenuTreeItem 的 ObservableCollection)或叶子。

public interface IMenuTreeItem
{
    string Name { get; set; }
}

public class MenuTreeLeaf : IMenuTreeItem
{
    public string Name { get; set; }
    public Guid UID { get; set; }
    public ObjectType Type { get; set; }
    public Requirement Requirement { get; set; }

    public MenuTreeLeaf(string name, ObjectType type, Guid uID)
    {
        Type = type;
        Name = name;
        UID = uID;
    }
    public MenuTreeLeaf(string name)
    {
        Name = name;
    }
}
public class MenuTreeBranch : IMenuTreeItem, INotifyPropertyChanged
{
    public string Name { get; set; }
    private ObservableCollection<IMenuTreeItem> _items;
    public ObservableCollection<IMenuTreeItem> Items
    {
        get
        {
            return _items;
        }
        set
        {
            _items = value; OnPropertyChanged();
        }
    }

    public MenuTreeBranch(string name)
    {
        Name = name;
    }

    public event PropertyChangedEventHandler PropertyChanged;

    protected void OnPropertyChanged(string name = null)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
    }
}

这就是我过滤的方式。感觉有更好的方法。

            ObservableCollection<IMenuTreeItem> result = new ObservableCollection<IMenuTreeItem>(ItemsSource);
            for (int i = 0; i < result.Count; i++)
            {
                if (result[i] is MenuTreeBranch currentBranch)
                {
                    if (currentBranch.Items != null)
                        currentBranch.Items = new ObservableCollection<IMenuTreeItem>(currentBranch.Items.Where(x => x.Name.ToLower().Contains(SearchField.ToLower())));
                }
            }
            result = new ObservableCollection<IMenuTreeItem>(result.Where(x => (x as MenuTreeBranch).Items.Count > 0));
            result.Insert(0, new MenuTreeLeaf("[Search]"));
            return result;

所以我的主要问题是:

有什么建议吗?

标签: wpf

解决方案


您可能有一个菜单项类,它公开一个递归过滤器字符串和一个返回过滤子项的集合属性:

public class FilteredMenuItem : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;

    public string Name { get; set; }

    public ICommand Command { get; set; }

    private string filter;

    public string Filter
    {
        get { return filter; }
        set
        {
            filter = value;

            foreach (var childItem in ChildItems)
            {
                childItem.Filter = filter;
            }

            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(Filter)));
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(FilteredChildItems)));
        }
    }

    public List<FilteredMenuItem> ChildItems { get; set; } = new List<FilteredMenuItem>();

    public IEnumerable<FilteredMenuItem> FilteredChildItems
    {
        get { return string.IsNullOrEmpty(Filter)
                ? ChildItems
                : ChildItems.Where(childItem => (bool)childItem.Name?.Contains(Filter)); }
    }
}

RootItem在视图模型中有一个属性,例如

public FilteredMenuItem RootItem { get; }
    = new FilteredMenuItem { Name = "Items" };

您可以像这样在 XAML 中绑定到它:

<StackPanel DataContext="{Binding RootItem}">
    <TextBox Text="{Binding Filter, UpdateSourceTrigger=PropertyChanged}"/>
    <Menu>
        <Menu.Resources>
            <Style TargetType="MenuItem">
                <Setter Property="Header" Value="{Binding Name}"/>
                <Setter Property="Command" Value="{Binding Command}"/>
                <Setter Property="ItemsSource"
                        Value="{Binding FilteredChildItems}"/>
            </Style>
        </Menu.Resources>
        <MenuItem/>
    </Menu>
</StackPanel>

当您填充ChildItems每个 FilteredMenuItem 的属性时,视图只显示FilteredChildItems集合。

您可能还注意到,上述内容根本没有使用ObservableCollection,因为在运行时没有向任何集合添加或删除任何项目。您只需确保在加载视图之前填充项目树。


推荐阅读