异步CollectionViewSource过滤与进度报告?
我正在修改CollectionViewSource
上的搜索,从同步到异步,因为集合的典型大小已经大量增加。原因是给予用户进度报告。问题是我没有看到在过滤事件处理程序中引用Progress<int>
对象的方法。下面的例子有一个可以工作的异步方法,带有进度报告和Filter
方法的框架,显示了我的困惑。I don’我不知道在CollectionViewSource
上实现ProgressBar
以进行Filter
操作的方法。
<Window x:Class="testProgressFilter.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:testProgressFilter"
mc:Ignorable="d"
Title="MainWindow" Height="400" Width="500">
<Window.Resources>
<local:VM x:Key="VM" />
</Window.Resources>
<Window.DataContext>
<Binding Source="{StaticResource VM}"/>
</Window.DataContext>
<Grid>
<StackPanel Margin="30">
<StackPanel Orientation="Horizontal" >
<ProgressBar Minimum="{Binding ProgressBarMinimum}" Maximum="{Binding ProgressBarMaximum}" Value="{Binding ProgressBarValue}"
Height="30" Width="300"/>
<Button Margin="20,0,0,0" Height="30" Width="75" Command="{Binding CancelCommand}" Content="Cancel"/>
</StackPanel>
<Button Margin="0,30,0,0" HorizontalAlignment="Center" Height="30" Width="125" Command="{Binding FilterCommand}"
Content="Do a filter" IsEnabled="{Binding ButtonsAreEnabled}"/>
<Button Margin="0,30,0,0" HorizontalAlignment="Center" Height="30" Width="125" Command="{Binding AltCommand}"
Content="Do something else" IsEnabled="{Binding ButtonsAreEnabled}"/>
</StackPanel>
</Grid>
</Window>
视图模型
using System;
using System.Collections.ObjectModel;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Data;
using System.Windows.Input;
using custAssembliesx64;
namespace testProgressFilter
{
internal class VM : Notifier
{
private int progressBarMinimum = 0;
public int ProgressBarMinimum
{
get { return progressBarMinimum; }
set
{
if (progressBarMinimum != value)
{
progressBarMinimum = value;
OnPropertyChanged("ProgressBarMinimum");
}
}
}
private int progressBarMaximum = 100;
public int ProgressBarMaximum
{
get { return progressBarMaximum; }
set
{
if (progressBarMaximum != value)
{
progressBarMaximum = value;
OnPropertyChanged("ProgressBarMaximum");
}
}
}
private int progressBarValue;
public int ProgressBarValue
{
get { return progressBarValue; }
set
{
if (progressBarValue != value)
{
progressBarValue = value;
OnPropertyChanged("ProgressBarValue");
}
}
}
private bool buttonsAreEnabled = true;
public bool ButtonsAreEnabled
{
get { return buttonsAreEnabled; }
set
{
if (buttonsAreEnabled != value)
{
buttonsAreEnabled = value;
OnPropertyChanged("ButtonsAreEnabled");
}
}
}
internal static bool IsCancelled = false;
ObservableCollection<int> ints = new ObservableCollection<int>();
CollectionViewSource CollectionViewSource = new CollectionViewSource();
const int maxvals = 2000;
const int halfMaxvals = maxvals / 2;
int ItemsCount;
int ItemsCountPercent;
int prevItemsCountPercent;
const float cent = (float)100 / (float)maxvals;
public VM()
{
CollectionViewSource.Source = ints;
// populate the collection
for (int i = 0; i < maxvals; i++)
{
ints.Add(i);
}
}
public ICommand FilterCommand => new RelayCommand(DoFilter);
private async void DoFilter(object parm)
{
// filter the collection, showing progress
ButtonsAreEnabled = false;
int totalItems = maxvals;
var progress = new Progress<int>(percent => { ProgressBarValue = percent; });
await Task.Run(() => DoFilterTask(totalItems, progress));
IsCancelled = false;
ButtonsAreEnabled = true;
CollectionViewSource.Filter -= FilterCriteria;
}
private void DoFilterTask(int totalItems, IProgress<int> progress)
{
System.Windows.Application.Current.Dispatcher.Invoke(() =>
{
CollectionViewSource.Filter += FilterCriteria;
});
}
private void FilterCriteria(object sender, FilterEventArgs e)
{
if (IsCancelled) return;
int j = (int)e.Item;
// REPORT PROGRESS HERE!
if (j < halfMaxvals)
{
e.Accepted = false;
}
else
{
e.Accepted = true;
}
}
public ICommand AltCommand => new RelayCommand(DoAlt);
private async void DoAlt(object parm)
{
ButtonsAreEnabled = false;
int totalItems = maxvals;
ProgressBarMinimum = 0;
ProgressBarMaximum = 100;
ProgressBarValue = 0;
var progress = new Progress<int>(percent => { ProgressBarValue = percent; });
await Task.Run(() => DoAltTask(totalItems, progress));
IsCancelled = false;
ButtonsAreEnabled = true;
}
private void DoAltTask(int totalItems, IProgress<int> progress)
{
// doing something else with the collection, showing progress
ItemsCount = 0;
ItemsCountPercent = 0;
prevItemsCountPercent = 0;
double x = 1;
for (int i = 0; i < totalItems; i++)
{
if (IsCancelled) break;
x += i / 1048576;
Thread.Sleep(1);
ItemsCount++;
// report progress when > 1 additional percent is surpassed
ItemsCountPercent = (int)(cent * (float)ItemsCount);
if (ItemsCountPercent > prevItemsCountPercent)
{
prevItemsCountPercent = ItemsCountPercent;
if (progress != null)
{
System.Windows.Application.Current.Dispatcher.Invoke(() =>
{
progress.Report(ItemsCountPercent);
});
}
}
}
}
public ICommand CancelCommand => new RelayCommand(DoCancel);
private void DoCancel(object parm)
{
IsCancelled = true;
}
}
}
2条答案
按热度按时间vvppvyoh1#
首先,创建一个后台线程,除了将它的全部工作返回给UI线程(参见你的
DoFilterTask
方法)之外什么也不做,这是非常无用和浪费资源的。它会产生不必要的开销。同样适用于这个代码气味:
使用
IProgress<T>
的全部目的是将进度委托的执行返回到原始的调度器线程。那么为什么还要调用Dispatcher:Invoke
呢?Dispatcher.Invoke
是多余的。在这一点上,我不想提到你的代码的其余部分,没有多大意义。只是必须强调IProgress<T>
和Dispatcher
的错误使用。(尤其是在后台线程的上下文中)。第二,只有在没有启用UI虚拟化或不支持UI虚拟化的视图(如普通的
ItemsControl
)中显示数据项,或者加载了太多项时,才会遇到糟糕的过滤性能。如果我们有这么多项,我们通常从数据库或某种数据服务中获取它们。在这种情况下,您不能筛选视图的源集合,而是查询数据源以获得筛选结果。例如,将工作卸载到服务器或数据库将显著提高性能。
如果您希望在对源代码集合应用重大更改(如过滤)时减少UI的不稳定感,则必须使用支持UI虚拟化的
ItemsControl
(例如ListBox
)。我也希望
Thread.Sleep
不在你的产品代码中,因为你为什么要人为地减慢你的算法(然后显示一个进度条)?但是,由于
CollectionView
的Dispatcher
关联性(如果用作Binding.Source
),您不能在与CollectionView
关联的调度器线程之外的其他线程上操作它。出于这个原因,您必须创建一个临时的
CollectionView
,它与UI线程断开连接,以便从后台线程操作它。在您的情况下,创建一个临时集合会更容易,稍后将其分配给
ListBox
(或任何其他支持UI虚拟化的ItemsControl
)的ItemsSource
属性。下面的示例在过滤视图中的大型源集合时保持UI响应:MainWindow.xaml
MainWindow.xaml.cs
ctrmrzij2#
第一个解决方案非常好
另一种仅使用调度器的解决方案:
1.在XAML上的进度栏定义中创建加载的事件(称为ProgressBarLoaded)
在视图模型中使用它
并在FilterCriteria中更新值:
无需定义绑定到ProgressBarValue..
在这个示例中,我在过滤集合的过程中使用了进度条