我有以下代码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进行这些更新。
感谢您的时间。
答案 0 :(得分:2)
您没有更新UI,更改绑定数据。绑定系统隐式地将绑定更新分派给UI。