0

I have a WPF TabControl with two TabItems. I am trying to change the selected tab on code behind on a Button click event and execute some other code. In this example:

private void Button_Click(object sender, RoutedEventArgs e)
{
    ConvertDataTabControl.SelectedIndex = 1;
    System.Threading.Thread.Sleep(2000);
    ...
}

I would expect the UI to refresh and move from Tab 0 to Tab 1 and only then execute the Sleep method, but the UI is refreshed only after Button_Click finishes execution. I tried calling InvalidateVisual, but it does not work.

Is there a way to force the UI to refresh before executing Sleep?

Anatoliy Nikolaev
  • 22,370
  • 15
  • 69
  • 68
paccic
  • 299
  • 4
  • 10
  • In WPF alot of stuff is defered, especially when it comes to input, view creation and rendering. Its best to avoid code like this, and "react" on the situations or use a proper state machine like approach. If you want to do something when the page is changed, react on the Loaded event of that particular page. – dowhilefor Mar 21 '13 at 13:46

3 Answers3

2

Your code runs on the UI thread by default, so nothing else can be executed on the UI thread (such as updating the layout) until the thread finishes executing.

There are many ways of releasing control of the UI thread before the code finishes executing, but I find the simplest is to use a Task from the Task Parallel Library which can be used to run code on a separate thread.

For example,

Task.Factory.StartNew(() =>
{
    Thread.Sleep(2000);

    // Other code here
});

It should be noted that UI objects can only be modified on the UI thread, so if your "other code here" updates a UI object, you'll probably want to use the Dispatcher to execute code on the UI thread, like this:

Dispatcher.BeginInvoke(() =>
{
    // Code to update the UI
});
Rachel
  • 130,264
  • 66
  • 304
  • 490
  • For the Dispatcher.BeginInvoke(), it wasn't compiling for me, I had to use Skeet's advice from here: http://stackoverflow.com/questions/3760777/dispatcher-begininvoke-syntax – dcp Oct 07 '14 at 21:16
1

try

Dispatcher.BeginInvoke(()=>
{
    ConvertDataTabControl.SelectedIndex = 1;
});
David
  • 15,894
  • 22
  • 55
  • 66
  • How exactly should that help? dispatch the sleep? dispatch the selectedindex? That way it just delays the execution even more. – dowhilefor Mar 21 '13 at 13:47
  • you should not call sleep! Check msdn BeginInvoke before you complain. – David Mar 21 '13 at 14:08
  • I wasn't complaining i just think your answer is a bit uninformative. Of course you shouldn't do it, you know it, i know it but maybe not the OP. – dowhilefor Mar 21 '13 at 14:10
  • Can you explain why you would call sleep()? – David Mar 21 '13 at 14:11
  • I am only using it for the purpose of example. I want to move to second tab, see that tab and execute the afterwards code. – paccic Mar 21 '13 at 14:28
  • I wouldn't not, i never said i would. But again, your previous answer and the new one will not explain why its not working as he think it is. Don't get to attached to the sleep, i think he would like to know, why after he changed the SelectedIndex the tab is not yet created. – dowhilefor Mar 21 '13 at 14:29
0

The problem is you are doing your work (sleep) on the UI thread. You can use a task/backgroundworker/etc to do the work in an other thread and then do set the ui changes back to the UI thread:

private void Button_Click(object sender, RoutedEventArgs e)
{
    Dispatcher callback = Dispatcher.CurrentDispatcher;
    ThreadPool.QueueUserWorkItem(new WaitCallback((o) =>
    {
         //Do some work
         System.Threading.Thread.Sleep(2000);

         //callbackk to ui thread to do ui work. You can also use BeginInvoke...
         callback.Invoke(new Action(() => {ConvertDataTabControl.SelectedIndex = 1;}));

         //Do some more work
         System.Threading.Thread.Sleep(2000);
         ...
    }
}

It is just a example to get the idea.

Peter
  • 27,590
  • 8
  • 64
  • 84