wpf - 使用 Dispatcher 在 WPF 列表框中异步加载项目列表

标签 wpf mvvm dispatcher

我正在创建一个 WPF 解决方案,该解决方案使用 MVVM 模式在搜索控件中异步加载搜索项。搜索控件是一个 WPF 用户控件,它带有一个用于输入搜索文本和搜索按钮的文本框以及一个隐藏列表框,该列表框在加载搜索项列表时可见。该用户控件又嵌入到另一个 WPF View 中,该 View 具有某些项目的 TreeView 。该 View 有一个 View 模型,其中加载 TreeView 的搜索项的逻辑将加载到搜索控件中。一直以来,这都是同步发生的,无需使用任何调度程序调用。但是,在更改请求之后,我想使用 Dispatcher 在不同的线程中异步发生此操作。

有人可以让我知道如何获取 View 模型类中搜索控件的调度程序的句柄,以便使用 MVVM 模式对其调用 BeginInvoke,其中我的 View 模型不知道该 View 吗?任何线索将不胜感激。

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 属性绑定(bind)到 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>

谢谢, 索米亚

最佳答案

这是一个简单的实现,展示了如何使用 BackgroundWorker DoWork 时更新 UI 线程上的对象正在运行 - 在此示例中,有一个 ListBox在绑定(bind)到 FilteredItems 的 UI 中,和ItemsSourceUserControl 的属性类型 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 .

关于wpf - 使用 Dispatcher 在 WPF 列表框中异步加载项目列表,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/3886236/

相关文章:

c# - 将 DbContext 与依赖注入(inject)一起使用

c# - 调用调度程序和执行之间的时间真的很长

c# - CollectionViewSource.Source 的调度程序更新在同一线程上抛出错误的线程异常

c# - RelayCommand 在一段时间后停止工作

c# - 记录致命异常

c# - 检查 BalloonTooltip 是否被用户关闭

wpf - 在单独的线程WPF上更新两个单独的UI

wpf - 使用带有 Ninject 和 WPF 的 Caliburn.Micro 在 View 模型之间绑定(bind)/传递数据

c# - 通知集合中某项的属性更改

c# - 如何测试使用 DispatcherTimer 的类?