Страницы

Поиск по вопросам

вторник, 31 декабря 2019 г.

Как изменить элемент UI запущеный в отдельном потоке в консольном приложении

#c_sharp #wpf #многопоточность


Консольное приложение выводит текстовую информацию на экран. Требуется дуплицировать
текст в окне WPF. Окно создается в отдельном потоке (в том же самом не получается):

var loggerThread = new Thread(() =>
{
    var logWindow = new Window
    {
        Title = "Logger",
        Width = 100,
        Height = 100
    };

    var stackPanel = new StackPanel 
    {
        Name = "stackPanel"
    };

    stackPanel.Children.Add(new TextBlock 
    {
        Name = "textBlock", Text = "new text\n"
    });

    logWindow.Content = stackPanel;
    logWindow.ShowDialog();
});

loggerThread.SetApartmentState(ApartmentState.STA);
loggerThread.IsBackground = true;
loggerThread.Start();


Как можно добавлять строки в textBlock из разных классов приложение? Или в общем,
как получить доступ к UI элементам внутри отдельного потока loggerThread?
    


Ответы

Ответ 1



Очень просто. Из вашего UI-потока, сохраните значение Dispatcher.CurrentDispatcher в глобальное место. (Вы можете ещё воспользоваться Dispatcher.FromThread из главного потока, но вы должны быть уверены, что диспетчер в UI-потоке уже создан.) Пусть это «глобальное место» называется dispatcher. Теперь вы можете отправлять код в UI-поток, используя dispatcher.BeginInvoke или dispatcher.InvokeAsync: await dispatcher.InvokeAsync(() => textBlock.Text = "Всем привет!"); Кстати, вам скорее всего бы не понадобился дополнительный поток, если бы вы отмаркировали функцию Main атрибутом [STAThread]. Но при этом для длительных синхронных операций (а такие обычно часто встречаются в консольных программах) вам всё равно понадобился бы фоновый поток. Вот более современная версия DispatcherThread: public class DispatcherThread : IDisposable { readonly Dispatcher dispatcher; readonly Thread thread; static public async Task CreateAsync() { var waitCompletionSource = new TaskCompletionSource(); var thread = new Thread(() => { waitCompletionSource.SetResult(new DispatcherThread()); Dispatcher.Run(); }); thread.SetApartmentState(ApartmentState.STA); thread.Start(); return await waitCompletionSource.Task; } private DispatcherThread() { dispatcher = Dispatcher.CurrentDispatcher; thread = Thread.CurrentThread; } public DispatcherOperation Execute(Action a) => dispatcher.InvokeAsync(a); public DispatcherOperation Get(Func getter) => dispatcher.InvokeAsync(getter); public async Task CloseAsync() { var waitCompletionSource = new TaskCompletionSource(); EventHandler shutdownWatch = (sender, args) => waitCompletionSource.SetResult(0); dispatcher.ShutdownFinished += shutdownWatch; try { if (dispatcher.HasShutdownFinished) return; dispatcher.BeginInvokeShutdown(DispatcherPriority.Normal); await waitCompletionSource.Task; } finally { dispatcher.ShutdownFinished -= shutdownWatch; } } public void Dispose() { dispatcher.InvokeShutdown(); if (thread != Thread.CurrentThread) thread.Join(); } } Пользоваться просто: DispatcherThread dt = await DispatcherThread.CreateAsync(); Window w = await dt.Get(() => { var w = new Window() { Height = 100, Width = 100, Content = "hello world" }; w.Show(); return w; }); // ... await dt.Execute(() => w.Content = "goodbye world"); // ... await dt.Execute(() => w.Close()); await dt.CloseAsync(); dt.Dispose();

Комментариев нет:

Отправить комментарий