使用Dispatcher在WPF列表框中异步加载项目列表 [英] Loading the list of items asynchronously in a WPF listbox using Dispatcher
问题描述
我正在创建一个WPF解决方案,该解决方案使用MVVM模式来异步加载搜索控件中的搜索项。搜索控件是WPF用户控件,创建时使用一个文本框来输入搜索文本和搜索按钮,以及一个隐藏的列表框,当在其中加载搜索到的项目列表时,该列表框将可见。该用户控件又嵌入到另一个WPF视图中,该视图具有某些项目的树形视图。该视图具有视图模型,在该模型中将加载树视图的搜索项的逻辑将加载到搜索控件中。一直以来,这是在不使用任何Dispatcher调用的情况下同步发生的。但是,在发出更改请求之后,我想使用Dispatcher在另一个线程中异步进行此操作。
I am working on creating a WPF solution which uses MVVM pattern to load searched items in a search control asynchronously. The search control which is a WPF usercontrol is created with a textbox to enter search text and search button and a hidden listbox which would be visible when it loads the searched items list in it. This user control is in turn embedded into another WPF view which has a treeview of certain items. This view has a view model in which the logic to load the searched items of the tree view would be loaded in the search control. All the while, this has been happening synchronously without the use of any Dispatcher call. But, after a change request, I would like to make this happen asynchronously in a different thread using Dispatcher.
有人可以让我知道如何获取视图模型类中Search控件的Dispatcher的句柄,以便使用MVVM模式在其上调用BeginInvoke,其中我的视图模型不了解视图?
Could anyone please let me know how to get handle of the Dispatcher of the Search control in the view model class so as to call BeginInvoke on it using MVVM pattern wherein my View model is not aware of the view? Any clue would be highly appreciated.
public ObservableCollection<Details> CatalogSearchResults { get; private set; }
private void ExecuteSearchCommand(object parameter)
{
CatalogSearchResults.Clear();
if (string.IsNullOrEmpty(parameter.ToString())) return;
searchtext = (string)parameter;
searchtext.Trim();
SetSearchResults();
}
private void SetSearchResults()
{
BackgroundWorker bw = new BackgroundWorker();
bw.DoWork += LoadResults;
bw.RunWorkerCompleted += this.LoadResultsCompleted;
bw.RunWorkerAsync();
}
private void LoadResults(object sender, DoWorkEventArgs args)
{
IsSearchInProgress = true;
foreach (var category in _rootCategory.Recurse(FindChildren))
{
if (category.CommentDetails != null)
{
//limitation - there is no direct way to add range to observable collection.
//Using linq query would result in two loops rather than one.
foreach (var node in category.Details)
{
if (node.Name.IndexOf(searchtext, StringComparison.CurrentCultureIgnoreCase) >= 0
|| node.PrecannedText.IndexOf(searchtext, StringComparison.CurrentCultureIgnoreCase) >= 0)
{
Application.Current.Dispatcher.BeginInvoke(DispatcherPriority.Normal,
(ThreadStart)delegate { CatalogSearchResults.Add(node); });
Thread.Sleep(100);
}
}
}
}
IsSearchInProgress = false;
}
在xaml中,我将Search控件的Items属性设置为CatalogSearchResults:
In the xaml, I am biding the Items property of the Search control to the CatalogSearchResults:
<ctrl:SearchControl x:Name="Ctrl" Grid.RowSpan="2" HorizontalAlignment="Stretch" VerticalAlignment="Top" ToolTip="Search" Command="{Binding SearchCommand}" Grid.ColumnSpan="3"
CommandParameter="{Binding Text, RelativeSource={RelativeSource Self}}"
Items ="{Binding CatalogSearchResults}" > </ctrl:SearchControl>
谢谢,
Sowmya
Thanks, Sowmya
推荐答案
这是一个简单的实现,展示了如何在 DoWork时使用
正在运行-在此示例中,UI中绑定了 BackgroundWorker
更新UI线程上的对象 FilteredItems
的 ListBox
ItemsSource
是 IEnumerable
类型的 UserControl
的属性:
Here's a simple implementation showing how to use BackgroundWorker
to update objects on the UI thread while DoWork
is running - in this example, there's a ListBox
in the UI that's bound to FilteredItems
, and ItemsSource
is a property of the UserControl
of type IEnumerable
:
FilteredItems = new ObservableCollection<object>();
BackgroundWorker bw = new BackgroundWorker();
bw.WorkerReportsProgress = true;
bw.DoWork += bw_DoWork;
bw.RunWorkerCompleted += bw_RunWorkerCompleted;
bw.ProgressChanged += bw_ProgressChanged;
bw.RunWorkerAsync();
private void bw_DoWork(object sender, DoWorkEventArgs e)
{
BackgroundWorker bw = (BackgroundWorker) sender;
var result = ItemsSource
.OfType<object>()
.Where(x => x.ToString().Contains(_FilterText));
foreach (object o in result)
{
// Pass each object found to bw_ProgressChanged in the UserState argument.
// This updates the UI as each item is found.
bw.ReportProgress(0, o);
}
}
void bw_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
// FilteredItems is bound to the UI, but it's OK to update it here because
// the ProgressChanged event handler runs on the UI thread.
FilteredItems.Add(e.UserState);
}
private void bw_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
if (e.Error != null)
{
MessageBox.Show(e.Error.Message);
}
}
请注意,调用 ReportProgress
每次找到一个项目的效率都非常低,因为您要通过 Invoke
调用来整理线程中找到的每个项目。根据过滤实际需要多长时间,最好累积一堆结果,然后将 List< object>
传递给 bw_ReportProgress
而不是单个 object
。
Note that calling ReportProgress
every time you find an item is pretty inefficient, as you're marshalling every item found across threads with an Invoke
call. Depending on how long the filtering is actually taking, it may be better to accumulate a bunch of results and pass a List<object>
to bw_ReportProgress
instead of just a single object
.
这篇关于使用Dispatcher在WPF列表框中异步加载项目列表的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!