Обновить пользовательский интерфейс из потока в WinRT - программирование
Подтвердить что ты не робот

Обновить пользовательский интерфейс из потока в WinRT

Поскольку предварительный просмотр Windows 8 был выпущен несколько дней назад, я работаю над новым WinRT (для приложений Metro) на С#, и я портировал свой собственный IRC-класс на новую потоковую передачу и сетевое взаимодействие.

Проблема: в моем классе работает поток для получения сообщений с сервера. Если это происходит, поток выполняет некоторый синтаксический анализ, а затем запускает событие, чтобы сообщить об этом программе. Подписанная функция затем "должна" обновлять пользовательский интерфейс (текстовый блок).

Это проблема, нить не может обновить интерфейс, а метод invoker, который работал с .NET 4.0, кажется, невозможен. Существует ли новое решение для этого или даже лучшего способа обновления пользовательского интерфейса? Если я попытаюсь обновить пользовательский интерфейс от подписчика событий, я получу этот Exception:

Приложение называется интерфейсом, который был другой поток (исключение из HRESULT: 0x8001010E (RPC_E_WRONG_THREAD))

4b9b3361

Ответ 1

Предпочтительным способом справиться с этим в WinRT (и С# 5 в целом) является использование async - await:

private async void Button_Click(object sender, RoutedEventArgs e)
{
    string text = await Task.Run(() => Compute());
    this.TextBlock.Text = text;
}

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

Но если вы не хотите или не можете использовать async, вы можете использовать Dispatcher аналогичным способом (хотя и другим), как в WPF:

private void Button_Click(object sender, RoutedEventArgs e)
{
    Task.Run(() => Compute());
}

private void Compute()
{
    // perform computation here

    Dispatcher.Invoke(CoreDispatcherPriority.Normal, ShowText, this, resultString);
}

private void ShowText(object sender, InvokedHandlerArgs e)
{
    this.TextBlock.Text = (string)e.Context;
}

Ответ 2

Вот более простой способ сделать это, я думаю!

Сначала запишите свой интерфейс SynconizationContext интерфейса пользователя следующим образом:

    var UISyncContext = TaskScheduler.FromCurrentSynchronizationContext();

Запустите операцию вызова сервера или любую другую операцию фонового потока, которая вам нужна:

    Task serverTask= Task.Run(()=> { /* DoWorkHere(); */} );

Затем выполните операцию UI в UISyncContext, который вы перенесли на первый шаг:

    Task uiTask= serverTask.ContinueWith((t)=>{TextBlockName.Text="your value"; }, UISyncContext);

Ответ 3

IMO Я думаю, что рекомендуемый маршрут "ThreadPool".

https://msdn.microsoft.com/en-us/library/windows/apps/xaml/hh465290.aspx

public static Task InvokeBackground(Func<Task> action)
    {
        var tcs = new TaskCompletionSource<bool>();

        var unused = ThreadPool.RunAsync(async (obj) =>
        {
            await action();
            tcs.TrySetResult(true);
        });

        return tcs.Task;
    }