我正在创建一个 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 中,和ItemsSource
是 UserControl
的属性类型 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/