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

时间:2016-07-15 12:13:38

标签: 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]?
  2. 当然,我还将展示如何使用IProgress<T>和线程安全集合对集合和UI进行这些更新。

    感谢您的时间。

1 个答案:

答案 0 :(得分:2)

您没有更新UI,更改绑定数据。绑定系统隐式地将绑定更新分派给UI。