使用BackgroundWorker填充ListView:UI元素不属于线程

时间:2019-03-05 14:02:16

标签: c# wpf

我正在尝试使用包含SearchResult的{​​{1}}的ObservableCollection填充ListView。我的(简化的)数据结构是:

ObservableCollection<Inline>

它包含一个public class SearchResult { public static ObservableCollection<Inline> FormatString(string s) { ObservableCollection<Inline> inlineList = new ObservableCollection<Inline> { new Run("a"), new Run("b") { FontWeight = FontWeights.Bold }, new Run("c") }; return inlineList; } public ObservableCollection<Inline> Formatted { get; set; } public string Raw { get; set; } } ,因为这些SearchResults将与支持富文本的自定义ObservableCollection<Inline>一起显示:

BindableTextBlock

但是当填充ListView时

public class BindableTextBlock : TextBlock
{
    public ObservableCollection<Inline> InlineList
    {
        get { return (ObservableCollection<Inline>)GetValue(InlineListProperty); }
        set { SetValue(InlineListProperty, value); }
    }

    public static readonly DependencyProperty InlineListProperty = DependencyProperty.Register("InlineList", typeof(ObservableCollection<Inline>), typeof(BindableTextBlock), new UIPropertyMetadata(null, OnPropertyChanged));

    private static void OnPropertyChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
    {
        BindableTextBlock textBlock = (BindableTextBlock)sender;
        textBlock.Inlines.Clear();
        textBlock.Inlines.AddRange((ObservableCollection<Inline>)e.NewValue);
    }
}

使用以下BackgroundWorker

<ListView Name="allSearchResultsListView">
    <ListView.ItemTemplate>
        <DataTemplate>
            <WrapPanel>
                <local:BindableTextBlock InlineList="{Binding Formatted}" />
                <TextBlock Text="{Binding Raw}" />
            </WrapPanel>
        </DataTemplate>
    </ListView.ItemTemplate>
</ListView>

程序崩溃与

public partial class MainWindow : Window { private readonly BackgroundWorker worker = new BackgroundWorker(); ObservableCollection<SearchResult> searchResults = new ObservableCollection<SearchResult>(); public MainWindow() { InitializeComponent(); worker.DoWork += worker_DoWork; worker.RunWorkerCompleted += worker_RunWorkerCompleted; worker.RunWorkerAsync(); } private void worker_DoWork(object sender, DoWorkEventArgs e) { for (long i = 0; i < 1000; i++) { searchResults.Add(new SearchResult() { Formatted = SearchResult.FormatString("a*b*c"), Raw = "abc" }); } } private void worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) { allSearchResultsListView.ItemsSource = searchResults; } }

Exception thrown: 'System.Windows.Markup.XamlParseException' in PresentationFramework.dll

问题是在后台工作程序UI元素(Inner Exception: The calling thread cannot access this object because a different thread owns it)中创建了不属于UI线程的元素。在工作人员完成后分配ItemsSource时,将引发异常。

相似的问题似乎被问了很多,但我找不到适合我的特殊情况的东西。

感谢您的帮助!

1 个答案:

答案 0 :(得分:2)

要与UI元素进行交互,您必须在UI线程分派器中使用“ Invoke”或“ BeginInvoke”

 Application.Current.Dispatcher.Invoke((Action)delegate
       {
           //CHANGE DATA BOUND TO THE UI HERE
       });

我喜欢使用静态方法:

public static class Helpers
{
 public static void RunInUIThread(Action method)
   {
       if (Application.Current == null)
       {
           return;
       }
       Application.Current.Dispatcher.BeginInvoke((Action)delegate
       {
           method();
       });
   }
}

您可以这样使用它:

  private void worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
  { 
   Helpers.RunInUIThread(()=>allSearchResultsListView.ItemsSource = searchResults);
  }

顺便说一句,您应该使用后台线程进行长时间运行的操作,例如从Web服务获取数据。