it-swarm.com.ru

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

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

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

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

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

16
Suchiman

Предпочтительным способом решения этой проблемы в WinRT (и C # 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;
}
27
svick

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

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

    var UISyncContext = TaskScheduler.FromCurrentSynchronizationContext();

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

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

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

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

ИМО Я думаю, что "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;
    }
0
Quincy