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