使用Dispatcher在WPF列表框中异步加载项目列表 [英] Loading the list of items asynchronously in a WPF listbox using Dispatcher

查看:146
本文介绍了使用Dispatcher在WPF列表框中异步加载项目列表的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在创建一个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时使用 BackgroundWorker 更新UI线程上的对象正在运行-在此示例中,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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆