Страницы

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

вторник, 25 декабря 2018 г.

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

Консольное приложение выводит текстовую информацию на экран. Требуется дуплицировать текст в окне 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
" });
logWindow.Content = stackPanel; logWindow.ShowDialog(); });
loggerThread.SetApartmentState(ApartmentState.STA); loggerThread.IsBackground = true; loggerThread.Start();
Как можно добавлять строки в textBlock из разных классов приложение? Или в общем, как получить доступ к UI элементам внутри отдельного потока loggerThread?


Ответ

Очень просто.
Из вашего 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();

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

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