1

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?

A.J. Uppal
  • 19,117
  • 6
  • 45
  • 76
monstr
  • 1,680
  • 1
  • 25
  • 44
  • see here: http://stackoverflow.com/questions/7428260/wp7-invalid-cross-thread-access-scheduledtaskagent/7428442#7428442 – thumbmunkeys May 26 '14 at 09:46
  • @monstr, define _works fine_. Two of them will fail with exception and 2 will still block UI thread – dkozl May 26 '14 at 09:47
  • thumbmunkeys, I already have used the `Dispatcher` – monstr May 26 '14 at 09:49
  • dkozl, I have no exceptions, I defined first and third buttons as `(works)` – monstr May 26 '14 at 09:50
  • `ParallelThreadClickWithBindings` works because binding automatically marshalls execution to Dispatcher. `ParallelThreadClickWithoutBindings` works not as expected because you are blocking UI thread with `Thread.Sleep` method. `ParalleTaskClickWithBindings` works for the same reason as first method. `ParallelTaskWithoutBindings` works not as expected because you set content directly, overriding the binding, thus 'Clear' button doesn't work anymore. – amnezjak May 26 '14 at 10:02
  • I blocked method only on 2000 msec. Then it go further and set to `Label` "afterSleep" string as well as other working methods. Don't understand... – monstr May 26 '14 at 10:36
  • So how can I make the same behavior via the direct set content approach? Namely, first I want see "beforeSleep" after I must see "afterSleep" – monstr May 26 '14 at 10:45
  • What's wrong with setting content through binding? – amnezjak May 26 '14 at 10:49
  • Nothing, but I used to work with direct set content if I not using MVVM pattern :) And I just want investigate this problem completely. I consider that behavior of `Dispatcher.Invoke` very strange and unexpected. I understand that I dunno something important thing about it... May be you can give me some link to read about it? – monstr May 26 '14 at 10:55
  • Well, use `Dispatcher` to set the label content from non-UI thread. But don't block thread from `Dispatcher`. See: http://pastebin.com/Vem29fvW. For .NET 4.5 `async-await` version see: http://pastebin.com/q5ZE0qZQ. – amnezjak May 26 '14 at 11:36
  • thx. All works fine till use simple `Action` delegate` in `Task`. . When I try to use any `Func` delegate, for example, 'Task' - UI locked till `Task` logic will be done :( – monstr May 26 '14 at 12:02
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/54441/discussion-between-monstr-and-amnezjak). – monstr May 26 '14 at 12:11

0 Answers0