3

I have the following code 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> 

This works with the following code behind perfectly:

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"); } } } 

I am writing this as one example of how to update a collection from a background thread-pool thread. My question is concerning the ability to do

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

This is updating the UI via a background thread, my question is:

  1. Why does this work without throwing InvalidOperationException [as we are updating the UI, via background thread]?

Of course I will also be showing how these updates to the collection and UI can be done using IProgress<T> and a thread safe collection.

Thanks for your time.

1
  • I'm voting to close this question as off-topic because it is awful and I should have never asked it! Commented Jul 15, 2016 at 12:19

1 Answer 1

2

You are not updating the UI, you change bound data. The binding system implicitly dispatches binding updates to the UI.

Sign up to request clarification or add additional context in comments.

3 Comments

You are always free to delete your questions if you think they are of no use to anyone.
Then only flags would be an option, also: I've seen worse.
Note that only modern versions of WPF will automatically marshal, and even then it only works automatically for scalar properties, not collections. Other MVVM frameworks may not support automatic marshalling at all.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.