我有以下代码 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,我的问题是:
- 为什么这可以在不抛出
InvalidOperationException
的情况下工作 [因为我们正在通过后台线程更新 UI]?
当然,我还将展示如何使用 IProgress<T>
和线程安全集合对集合和 UI 进行这些更新。
感谢您的宝贵时间。
最佳答案
您不是在更新 UI,而是在更改绑定(bind)数据。绑定(bind)系统隐式地将绑定(bind)更新分派(dispatch)给 UI。
关于c# - 关于从后台线程访问 Gui 的基本线程,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/38395862/