使用一些切换按钮激活列表框中的过滤器
Activating filters in a ListBox with some toggle buttons
好的,我有一个 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
}
现在真正的问题是……
我如何(在视图中)过滤列表框以仅显示基于 StatusCode 的列表的一部分,为每个 StatusCode 使用 4 个 ToggleButtons。因此,如果我按下(或激活)完成按钮,并且错误仅显示在满足该条件的列表框中。如果他们都活跃,则向所有人展示。如果有 none 标记则不显示任何内容。
我知道它可以使用 ICollectionView 来完成。如果它可以在 xaml 中更好,尽管它可以在视图后面的代码中。我的想法是将多个 ICollectionView 绑定到另一个。作为一个链条,每个切换按钮都会激活或停用每个切换按钮的过滤(尽管我仍然必须了解如何为单个切换按钮执行此操作)。我不知道这是否是最好的方法,所以欢迎任何帮助。
对不起我的英语我只会说西班牙语...
立即给link一个给source。
它的视觉效果可以在 the screenshot
中看到
对于过滤器值,我创建了 FilterValue
模型。
这对于了解我们的属性何时更改是必要的。
public class FilterValue
{
private bool _value;
public bool Value
{
get { return _value; }
set
{
_value = value;
ValueChanged?.Invoke();
}
}
public event Action ValueChanged;
}
在 QueueTask
class 中,我添加了 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
.
...
<Window.DataContext>
<viewModel:MainViewModel></viewModel:MainViewModel>
</Window.DataContext>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition/>
</Grid.RowDefinitions>
<ItemsControl ItemsSource="{Binding Filters}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<ToggleButton Margin="5" Height="25" Width="100" Content="{Binding Key}" IsChecked="{Binding Value.Value}"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
<ListBox Grid.Row="1" ItemsSource="{Binding QueueTasks}" DisplayMemberPath="TaskName"/>
</Grid>
</Window>
首先要感谢 Sean Sexton post
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>
它会在任何 ToggleButton 更改其状态以及队列中的任何项目更新其状态时更新过滤器。要使 LiveFilter 正常工作,属性 必须实施 INotifyPropertyChanged,以便它可以通知视图进行更改。
好的,我有一个 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
}
现在真正的问题是…… 我如何(在视图中)过滤列表框以仅显示基于 StatusCode 的列表的一部分,为每个 StatusCode 使用 4 个 ToggleButtons。因此,如果我按下(或激活)完成按钮,并且错误仅显示在满足该条件的列表框中。如果他们都活跃,则向所有人展示。如果有 none 标记则不显示任何内容。
我知道它可以使用 ICollectionView 来完成。如果它可以在 xaml 中更好,尽管它可以在视图后面的代码中。我的想法是将多个 ICollectionView 绑定到另一个。作为一个链条,每个切换按钮都会激活或停用每个切换按钮的过滤(尽管我仍然必须了解如何为单个切换按钮执行此操作)。我不知道这是否是最好的方法,所以欢迎任何帮助。
对不起我的英语我只会说西班牙语...
立即给link一个给source。
它的视觉效果可以在 the screenshot
中看到对于过滤器值,我创建了 FilterValue
模型。
这对于了解我们的属性何时更改是必要的。
public class FilterValue
{
private bool _value;
public bool Value
{
get { return _value; }
set
{
_value = value;
ValueChanged?.Invoke();
}
}
public event Action ValueChanged;
}
在 QueueTask
class 中,我添加了 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
.
...
<Window.DataContext>
<viewModel:MainViewModel></viewModel:MainViewModel>
</Window.DataContext>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition/>
</Grid.RowDefinitions>
<ItemsControl ItemsSource="{Binding Filters}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<ToggleButton Margin="5" Height="25" Width="100" Content="{Binding Key}" IsChecked="{Binding Value.Value}"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
<ListBox Grid.Row="1" ItemsSource="{Binding QueueTasks}" DisplayMemberPath="TaskName"/>
</Grid>
</Window>
首先要感谢 Sean Sexton post 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>
它会在任何 ToggleButton 更改其状态以及队列中的任何项目更新其状态时更新过滤器。要使 LiveFilter 正常工作,属性 必须实施 INotifyPropertyChanged,以便它可以通知视图进行更改。