Изменения на вкладке WPF TabControl не обновляются

У меня есть WPF TabControl с двумя TabItems. Я пытаюсь изменить выбранную вкладку в коде позади события щелчка Button и выполнить какой-то другой код. В этом примере:

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

Я бы ожидал, что пользовательский интерфейс обновится и переместится с Tab 0 на Tab 1 и только затем выполнит метод Sleep, но пользовательский интерфейс обновляется только после того, как Button_Click завершает выполнение. Я пытался позвонить InvalidateVisual, но это не работает.

Есть ли способ принудительно обновить пользовательский интерфейс перед выполнением Sleep?


person paccic    schedule 21.03.2013    source источник
comment
В WPF многие вещи откладываются, особенно когда речь идет о вводе, создании представления и рендеринге. Лучше избегать такого кода и реагировать на ситуации или использовать правильный подход, подобный конечному автомату. Если вы хотите что-то сделать при изменении страницы, отреагируйте на событие Loaded этой конкретной страницы.   -  person dowhilefor    schedule 21.03.2013


Ответы (3)


Ваш код запускается в потоке пользовательского интерфейса по умолчанию, поэтому ничто другое не может быть выполнено в потоке пользовательского интерфейса (например, обновление макета), пока поток не завершит выполнение.

Существует много способов освободить управление потоком пользовательского интерфейса до завершения выполнения кода, но я считаю, что самый простой — это использовать Task из Библиотека параллельных задач, которую можно использовать для запуска кода в отдельном потоке.

Например,

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

    // Other code here
});

Следует отметить, что объекты пользовательского интерфейса можно изменять только в потоке пользовательского интерфейса, поэтому, если ваш «другой код здесь» обновляет объект пользовательского интерфейса, вы, вероятно, захотите использовать файл Dispatcher для выполнения кода в потоке пользовательского интерфейса, например:

Dispatcher.BeginInvoke(() =>
{
    // Code to update the UI
});
person Rachel    schedule 21.03.2013
comment
Для Dispatcher.BeginInvoke() он не компилировался для меня, мне пришлось воспользоваться советом Скита отсюда: stackoverflow.com/questions/3760777/ - person dcp; 08.10.2014

пытаться

Dispatcher.BeginInvoke(()=>
{
    ConvertDataTabControl.SelectedIndex = 1;
});
person David    schedule 21.03.2013
comment
Как именно это должно помочь? отправить сон? отправить выбранный индекс? Таким образом, это еще больше задерживает выполнение. - person dowhilefor; 21.03.2013
comment
вы не должны называть сон! Прежде чем жаловаться, проверьте msdn BeginInvoke. - person David; 21.03.2013
comment
Я не жаловался, я просто думаю, что ваш ответ немного неинформативен. Конечно, вы не должны этого делать, вы это знаете, я знаю, но, может быть, не ОП. - person dowhilefor; 21.03.2013
comment
Можете ли вы объяснить, почему вы бы вызвали sleep()? - person David; 21.03.2013
comment
Я использую его только для примера. Я хочу перейти на вторую вкладку, увидеть эту вкладку и выполнить последующий код. - person paccic; 21.03.2013
comment
Я бы не стал, я никогда не говорил, что буду. Но опять же, ваш предыдущий ответ и новый не объяснят, почему он не работает, как он думает. Не привязывайтесь ко сну, я думаю, он хотел бы знать, почему после того, как он изменил SelectedIndex, вкладка еще не создана. - person dowhilefor; 21.03.2013

Проблема в том, что вы выполняете свою работу (спите) в потоке пользовательского интерфейса. Вы можете использовать task/backgroundworker/etc для выполнения работы в другом потоке, а затем установить изменения пользовательского интерфейса обратно в поток пользовательского интерфейса:

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

Это просто пример, чтобы понять идею.

person Peter    schedule 10.07.2013