使用一些切换按钮激活ListBox中的过滤器

时间:2018-01-11 15:35:02

标签: wpf xaml filter listbox togglebutton

好的,我有一个ListBox绑定到ViewModel的属性。 它已经填充了像这样的Caliburn.Micro.BindableCollection

public BindableCollection<QueueTask> QueueTasks
        {
            get
            {
                return this._queueProcessor.Queue;
            }
        }

队列任务有一些这样的属性:

public class QueueTask : PropertyChangedBase
    {
        private StatusCode _status;
        private int _completedPercent;

        public StatusCode Status
        {
            get => _status;
            set
            {
                _status = value;
                this.NotifyOfPropertyChange(() => Status);
            }
        }

        public int CompletedPercent
        {
            get { return _completedPercent; }
            set
            {
                _completedPercent = value;
                this.NotifyOfPropertyChange(() => CompletedPercent);
            }
        }
    }

现在您可以看到他的一个属性是StatusCode枚举

public enum StatusCode
    {
        Waiting = 1,    
        Processing,    
        Finished,    
        Error
    } 

现在真正的问题是...... 我如何(在视图中)过滤ListBox以仅显示StatusCode中列表的一部分,每个StatusCode使用4个ToggleButtons。因此,如果我按下(或激活)完成按钮,则只在ListBox中显示符合该条件的那些按钮。如果他们都活跃,请向所有人展示。如果没有标记,则不要显示任何内容。

我知道可以使用ICollectionView进行处理。如果它可以在xaml中更好,虽然它可以在View后面的代码中。我的想法是让几个ICollectionView绑定到另一个。作为一个链和每个切换按钮将激活或停用每个过滤器(虽然我仍然需要看到如何做到这一点,即使是一个切换按钮)。我不知道这是否是最好的方法,所以欢迎任何帮助。

对不起我的英语我只会说西班牙语......

2 个答案:

答案 0 :(得分:1)

立即提供指向source的链接。

the screenshot

中可以看到它的外观

对于过滤器值,我创建了-A1模型。 这有必要找出我们的属性何时发生变化。

FilterValue

public class FilterValue { private bool _value; public bool Value { get { return _value; } set { _value = value; ValueChanged?.Invoke(); } } public event Action ValueChanged; } 类中,我添加了QueueTask属性,以明确列表中的任务内容。

TaskName

然后我创建了一个包含所有元素public class QueueTask : INotifyPropertyChanged { private StatusCode _status; private int _completedPercent; private string _taskName; public StatusCode Status { get { return _status; } set { _status = value; OnPropertyChanged(nameof(Status)); } } public int CompletedPercent { get { return _completedPercent; } set { _completedPercent = value; OnPropertyChanged(nameof(CompletedPercent)); } } public string TaskName { get { return _taskName; } set { _taskName = value; OnPropertyChanged(nameof(Status)); } } public event PropertyChangedEventHandler PropertyChanged; protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null) { PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); } } 的列表和一个_allQueueTasks的视图列表。 用于测试的集合我填写方法QueueTasks。 我还创建了FillTempData ()字典,其中包含所有可能的Filters值。它填入StatusCode方法。

FillFilters ()

嗯,最后一步是视觉部分的布局。 我使用public class MainViewModel : INotifyPropertyChanged { private List<QueueTask> _allQueueTasks; private List<QueueTask> _queueTasks; public Dictionary<StatusCode, FilterValue> Filters { get; set; } public List<QueueTask> QueueTasks { get { return _queueTasks; } set { _queueTasks = value; OnPropertyChanged(nameof(QueueTasks)); } } public MainViewModel() { FillTempData(); FillFilters(); } private void FillFilters() { Filters = new Dictionary<StatusCode, FilterValue>(); foreach (StatusCode code in Enum.GetValues(typeof(StatusCode))) { var newFilter = new FilterValue(); newFilter.ValueChanged += FilterOnValueChanged; Filters.Add(code, newFilter); } } private void FilterOnValueChanged() { var filtredItems = _allQueueTasks.Where(task => Filters[task.Status].Value); QueueTasks = new List<QueueTask>(filtredItems); } private void FillTempData() { _allQueueTasks = new List<QueueTask>() { new QueueTask() { Status = StatusCode.Waiting, TaskName = "WaitingTask1"}, new QueueTask() { Status = StatusCode.Waiting, TaskName = "WaitingTask2"}, new QueueTask() { Status = StatusCode.Processing, TaskName = "ProcessingTask1"}, new QueueTask() { Status = StatusCode.Processing, TaskName = "ProcessingTask2"}, new QueueTask() { Status = StatusCode.Finished, TaskName = "FinishedTask1"}, new QueueTask() { Status = StatusCode.Finished, TaskName = "FinishedTask2"}, new QueueTask() { Status = StatusCode.Error, TaskName = "ErrorTask1"}, new QueueTask() { Status = StatusCode.Error, TaskName = "ErrorTask2"}, }; QueueTasks = new List<QueueTask>(); } public event PropertyChangedEventHandler PropertyChanged; protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null) { PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); } } 来显示ItemsControl。 为了显示列表本身,我使用了ToggleButton

ListBox

答案 1 :(得分:0)

首先感谢Sean Sexton的这篇文章 https://wpf.2000things.com/2014/01/14/986-filtering-a-listbox-using-a-collectionviewsource/

现在使用CollectionViewSource的解决方案很简单(或多或少)。它在视图中用一些代码实现。

首先创建(CollectionViewSource绑定到完整队列):

<Window.Resources>        
        <CollectionViewSource Source="{Binding QueueTasks, Mode=OneWay}" x:Key="FilteredQueueTasks" IsLiveFilteringRequested="True">
            <CollectionViewSource.LiveFilteringProperties>
                <clr:String>Status</clr:String>
            </CollectionViewSource.LiveFilteringProperties>
        </CollectionViewSource>
    </Window.Resources>

然后将ListBox绑定到此CollectionViewSource(简化)。然后它只显示已由CollectionViewSource

过滤的任务
<ListBox Grid.Row="2" ItemsSource="{Binding Source={StaticResource FilteredQueueTasks}}" />

视图背后的代码看起来像这样(很大)

public partial class QueueView : Window, INotifyPropertyChanged
    {
        public QueueView()
        {
            InitializeComponent();
        }

        private void QueueTasks_Filter(object sender, FilterEventArgs e)
        {
            StatusCode status = ((QueueTask) e.Item).Status;

            switch (status)
            {
                case StatusCode.Waiting:
                    e.Accepted = showWaiting;
                    break;
                case StatusCode.Processing:
                    e.Accepted = showProcessing;
                    break;
                case StatusCode.Finished:
                    e.Accepted = showFinished;
                    break;
                case StatusCode.Error:
                    e.Accepted = showWithErrors;
                    break;
                case StatusCode.Warning:
                    e.Accepted = showWithWarnings;
                    break;
                default:
                    e.Accepted = false;
                    break;
            }
        }

        private bool showWaiting = true;

        public bool ShowWaiting
        {
            get => showWaiting;

            set
            {
                showWaiting = value;
                OnPropertyChanged(nameof(ShowWaiting));
                ((CollectionViewSource)this.Resources["FilteredQueueTasks"]).View.Refresh();
            }
        }

        private bool showProcessing = true;

        public bool ShowProcessing
        {
            get => showProcessing;

            set
            {
                showProcessing = value;
                OnPropertyChanged(nameof(ShowProcessing));
                ((CollectionViewSource)this.Resources["FilteredQueueTasks"]).View.Refresh();
            }
        }

        private bool showFinished;

        public bool ShowFinished
        {
            get => showFinished;

            set
            {
                showFinished = value;
                OnPropertyChanged(nameof(ShowFinished));
                ((CollectionViewSource)this.Resources["FilteredQueueTasks"]).View.Refresh();
            }
        }

        private bool showWithErrors = true;

        public bool ShowWithErrors
        {
            get => showWithErrors;

            set
            {
                showWithErrors = value;
                OnPropertyChanged(nameof(ShowWithErrors));
                ((CollectionViewSource)this.Resources["FilteredQueueTasks"]).View.Refresh();
            }
        }

        private bool showWithWarnings = true;

        public bool ShowWithWarnigs
        {
            get => showWithWarnings;

            set
            {
                showWithWarnings = value;
                OnPropertyChanged(nameof(ShowWithWarnigs));
                ((CollectionViewSource)this.Resources["FilteredQueueTasks"]).View.Refresh();
            }
        }

        public event PropertyChangedEventHandler PropertyChanged;

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

        private void Window_Initialized(object sender, EventArgs e)
        {
            ((CollectionViewSource)this.Resources["FilteredQueueTasks"]).Filter += QueueTasks_Filter;
        }
    }

然后每个ToggleButton都像这样附加到它:

<StackPanel Grid.Row="1" HorizontalAlignment="Right" Height="22" VerticalAlignment="Bottom" Orientation="Horizontal" Margin="-1,0,5,5">
            <ToggleButton IsChecked="{Binding ShowWithErrors, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type local:QueueView}}}">
                <Image Source="Images/Error.png" Margin="1" />
            </ToggleButton>
            <ToggleButton IsChecked="{Binding ShowWithWarnigs, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type local:QueueView}}}">
                <Image Source="Images/Warning.png" Margin="1" />
            </ToggleButton>
            <ToggleButton IsChecked="{Binding ShowFinished, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type local:QueueView}}}">
                <Image Source="Images/Complete.png" Margin="1" />
            </ToggleButton>
            <ToggleButton IsChecked="{Binding ShowProcessing, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type local:QueueView}}}">
                <Image Source="Images/Working.png" Margin="1"/>
            </ToggleButton>
            <ToggleButton IsChecked="{Binding ShowWaiting, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type local:QueueView}}}">
                <Image Source="Images/Movies.png" Margin="1" />
            </ToggleButton>
        </StackPanel>

当任何ToggleButtons更改其状态以及队列中的任何项目更新其状态时,它会更新过滤器。要使LiveFilter工作,属性必须实现INotifyPropertyChanged,以便它可以通知视图进行更改。