I make simple example window to demonstrate the problem. It is a complete example, you can copy and paste and try it by yourself.
When I try to set Content
property of Label
control via Binding
- UI updates nice in both cases (Task
and Thread
). But when I try to set Content
property of Label
directly to Label
like _label.Content
= "somestuff" - UI doesn't update.
And Clear Button
doesn't work anymore :(
XAML:
<Window x:Class="WpfApplication1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow"
Height="350"
ResizeMode="NoResize"
WindowStartupLocation="CenterScreen">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition></ColumnDefinition>
<ColumnDefinition></ColumnDefinition>
<ColumnDefinition></ColumnDefinition>
<ColumnDefinition></ColumnDefinition>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition></RowDefinition>
<RowDefinition Height="Auto"></RowDefinition>
</Grid.RowDefinitions>
<Label Grid.Row="0"
Grid.ColumnSpan="3"
Name="_label"
HorizontalAlignment="Center"
VerticalAlignment="Center"
FontSize="20"
Content="{Binding LabelContent}"></Label>
<Button Grid.Row="0"
Grid.Column="3"
Width="100"
Height="100"
Content="ClearLabel"
Click="ClearClick">
</Button>
<Button Name="_button"
Height="50"
Grid.Row="1"
Margin="5"
Content="ThreadWithBindings(works)"
Click="ParallelThreadClickWithBindings">
</Button>
<Button Name="_button1"
Grid.Column="1"
Grid.Row="1"
Height="50"
Margin="5"
Content="ThreadWithoutBindings"
Click="ParallelThreadClickWithoutBindings">
</Button>
<Button Name="_button2"
Grid.Column="2"
Grid.Row="1"
Height="50"
Margin="5"
Content="TaskWithBindings(works)"
Click="ParalleTaskClickWithBindings">
</Button>
<Button Name="_button3"
Grid.Column="3"
Grid.Row="1"
Height="50"
Margin="5"
Content="TaskWithoutBindings"
Click="ParallelTaskWithoutBindings">
</Button>
</Grid>
</Window>
CodeBehind:
public partial class MainWindow : INotifyPropertyChanged
{
private String _labelContent;
public String LabelContent
{
get { return _labelContent; }
set
{
_labelContent = value;
OnPropertyChanged("LabelContent");
}
}
public MainWindow()
{
InitializeComponent();
DataContext = this;
}
private void ParallelThreadClickWithBindings(Object sender, RoutedEventArgs e)
{
var thred = new Thread(t =>
{
LabelContent = "beforeSleep";
Thread.Sleep(2000);
LabelContent = "afterSleep";
});
thred.Start();
}
private void ParallelThreadClickWithoutBindings(Object sender, RoutedEventArgs e)
{
var thred = new Thread(t => Dispatcher.Invoke(new Action(() =>
{
_label.Content = "beforeSleep";
Thread.Sleep(2000);
_label.Content = "afterSleep";
})));
thred.Start();
}
private void ParalleTaskClickWithBindings(Object sender, RoutedEventArgs e)
{
Task.Factory.StartNew(() =>
{
LabelContent = "beforeSleep";
Thread.Sleep(2000);
LabelContent = "afterSleep";
});
}
private void ParallelTaskWithoutBindings(Object sender, RoutedEventArgs e)
{
Task.Factory.StartNew(new Action(() => Dispatcher.Invoke(new Action(() =>
{
_label.Content = "beforeSleep";
Thread.Sleep(2000);
_label.Content = "afterSleep";
}))));
}
private void ClearClick(Object sender, RoutedEventArgs e)
{
LabelContent = String.Empty;
}
public event PropertyChangedEventHandler PropertyChanged;
[NotifyPropertyChangedInvocator]
protected virtual void OnPropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
}
}
I expected the same behavior from all of these methods. Two works fine, two - doesn't work. I try use both - Invoke
and BeginInvoke
methods - all the same. Can anybody explain me the reason?