#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 TaskCreateAsync() { 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();
Комментариев нет:
Отправить комментарий