c# - 关于从后台线程访问 Gui 的基本线程

标签 c# wpf multithreading

我有以下代码 XAML:

MainWindow.xaml:

<Window x:Class="MultiThreadListBox.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:MultiThreadListBox"
        mc:Ignorable="d"
        Title="MainWindow" 
        Width="250"
        Height="450">
   <Grid>
      <Grid.RowDefinitions>
         <RowDefinition Height="*"/>
         <RowDefinition Height="Auto"/>
         <RowDefinition Height="Auto"/>
      </Grid.RowDefinitions>
      <ListBox x:Name="listBox" 
               Grid.Row="0"
               ItemsSource="{Binding ItemList}" 
               HorizontalAlignment="Stretch" 
               VerticalAlignment="Stretch" 
               Margin="10,10" 
               Width="Auto" >
         <ListBox.ItemTemplate>
            <DataTemplate>
               <StackPanel Orientation="Horizontal">
                  <TextBlock Text="{Binding Comment}"/>
                  <TextBlock Text="{Binding Id}"/>
               </StackPanel>
            </DataTemplate>
         </ListBox.ItemTemplate>
      </ListBox>
      <Grid Grid.Row="1">
         <Grid.ColumnDefinitions>
            <ColumnDefinition Width="*"/>
            <ColumnDefinition Width="*"/>
            <ColumnDefinition Width="*"/>
         </Grid.ColumnDefinitions>
         <Button x:Name="sameThreadButton" 
                 Grid.Column="0"
                 Click="sameThreadButton_Click" 
                 Content="Same Thread" 
                 Width="75"
                 Height="30"/>
         <Button x:Name="newThreadButton" 
                 Grid.Column="1"
                 Click="newThreadButton_Click" 
                 Content="New Thread" 
                 Height="30"
                 Width="75"/>
         <Button x:Name="autoMultiThreadButton" 
                 Grid.Column="2"
                 Click="newThreadButton_Click" 
                 Content="New Thread" 
                 Height="30"
                 Width="75"/>
      </Grid>
      <StatusBar Grid.Row="2" 
                 Background="Gray"
                 HorizontalAlignment="Stretch" 
                 Margin="0,10, 0, 0"
                 Height="Auto" 
                 VerticalAlignment="Bottom">
         <Label Foreground="White" 
                FontSize="11"
                Content="{Binding StatusMessage}"/>
      </StatusBar>
   </Grid>
</Window>   

这与下面的代码完美配合:

MainWindow.xaml.cs:

public partial class MainWindow : Window, INotifyPropertyChanged
{
    private ObservableCollection<JoeyObject> itemList;
    private static object _locker = new object(); 
    private string statusMessage;

    public MainWindow()
    {
        InitializeComponent();
        InitializeData();
        BindingOperations.EnableCollectionSynchronization(ItemList, _locker);
        DataContext = this;
    }

    private void InitializeData()
    {
        ItemList = new ObservableCollection<JoeyObject>();
        ItemList.Add(new JoeyObject("hello ", 1234));
        ItemList.Add(new JoeyObject("listbox ", 4567));

        StatusMessage = $"Initialized with {ItemList.Count} items";
    }

    private void sameThreadButton_Click(object sender, RoutedEventArgs e)
    {
        int count = ItemList.Count;

        ItemList.Add(new JoeyObject("more ", 7890));
        ItemList.Add(new JoeyObject("items ", 9876));
        ItemList.Add(new JoeyObject("here ", 7890));

        StatusMessage = $"Added {ItemList.Count - count} items on the UI thread";
    }

    private async void newThreadButton_Click(object sender, RoutedEventArgs e)
    {
        await Task.Run(() =>
        {
            int count = ItemList.Count;

            ItemList.Add(new JoeyObject("background thread-pool thread added 1", 7890));
            ItemList.Add(new JoeyObject("background thread-pool thread added 2", 9876));
            ItemList.Add(new JoeyObject("background thread-pool thread added 3", 7890));

            // StatusMessage = $"Added {ItemList.Count - count} items on a background thread"; <- THIS WORKS.
        });

        // Back on UI thread, do major UI work here.
        StatusMessage = $"Added {ItemList.Count - count} items on a background thread";

        // using async/await and leveraging .NET4.5+. This essentially sets up a 
        // Task continuation _for you_ and is syntactic sugar for doing the following:
        //Task.Factory.StartNew(() =>
        //{
        //  int count = ItemList.Count;

        //  ItemList.Add(new JoeyObject("background thread-pool thread added 1", 7890));
        //  ItemList.Add(new JoeyObject("background thread-pool thread added 2", 9876));
        //  ItemList.Add(new JoeyObject("background thread-pool thread added 3", 7890));

        //  StatusMessage = $"Added {ItemList.Count - count} items on a background thread";
        //}).ContinueWith((ant) =>
        //{
        //  // Back on UI thread, do major UI work here.
        //  StatusMessage = $"Added {ItemList.Count - count} items on a background thread";
        //}, TaskScheduler.FromCurrentSynchronizationContext());
    }

    public event PropertyChangedEventHandler PropertyChanged;

    protected void OnPropertyChanged(string propertyName)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }

    public string StatusMessage
    {
        get { return statusMessage; }
        set
        {
            statusMessage = value;
            OnPropertyChanged("StatusMessage");
        }
    }

    public ObservableCollection<JoeyObject> ItemList
    {
        get { return itemList; }
        private set
        {
            itemList = value;
            OnPropertyChanged("ItemList");
        }
    }
}

我写这篇文章是为了说明如何从后台线程池线程更新集合。我的问题是关于做事的能力

await Task.Run(() =>
{
    StatusMessage = "Some message";
});

这是通过后台线程更新 UI,我的问题是:

  1. 为什么这可以在不抛出 InvalidOperationException 的情况下工作 [因为我们正在通过后台线程更新 UI]?

当然,我还将展示如何使用 IProgress<T> 和线程安全集合对集合和 UI 进行这些更新。

感谢您的宝贵时间。

最佳答案

您不是在更新 UI,而是在更改绑定(bind)数据。绑定(bind)系统隐式地将绑定(bind)更新分派(dispatch)给 UI。

关于c# - 关于从后台线程访问 Gui 的基本线程,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/38395862/

相关文章:

c# - 如何在使用 Newtonsoft.Json 反序列化时忽略类型

c# - DataGridCell 的行索引

c# - 有关如何教程(WPF/Entity Framework/ObservableCollections)的问题

当进程数和工作线程数增加时,Python 多处理池 API 无法有效工作

c# - ComboBox items.count 与数据源不匹配

c# - 加载关卡后声音无法播放

java - 查找 : '' The specified procedure could not be found 时出错

c# - 显示分层数据的 WPF 组合框

java - 数据库的多线程问题

Java的ExecutorService性能