The console application displays text information on the screen. It is required to duplicate the text in the WPF window. The window is created in a separate thread (in the same it does not work):

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(); 

How can I add lines to textBlock from different application classes? Or in general, how to access the UI elements within a separate loggerThread thread?

  • By the way, why don't you use XAML? Well this is not WinForms. - VladD
  • Just this project is initially a DLL, but at certain moments it is necessary to display information in the Windows window. - andreikashin
  • Well, yes, but it does not contradict anything. Manually creating controls is difficult and unnecessary. And you can create windows in XAML anywhere. - VladD 2:53 pm

1 answer 1

Very simple.

From your UI stream , save the value of Dispatcher.CurrentDispatcher to a global location. (You can still use Dispatcher.FromThread from the main thread, but you must be sure that the dispatcher in the UI thread has already been created.) Let this “global place” be called dispatcher .

Now you can send code to a UI stream using dispatcher.BeginInvoke or dispatcher.InvokeAsync :

 await dispatcher.InvokeAsync(() => textBlock.Text = "Всем привет!"); 

By the way, you probably wouldn't need an extra thread if you tagged the Main function with the [STAThread] attribute. But at the same time for long-term synchronous operations (and these are usually often found in console programs), you would still need a background thread.


Here is a more modern version of DispatcherThread :

 public class DispatcherThread : IDisposable { readonly Dispatcher dispatcher; readonly Thread thread; static public async Task<DispatcherThread> CreateAsync() { var waitCompletionSource = new TaskCompletionSource<DispatcherThread>(); 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<T> Get<T>(Func<T> getter) => dispatcher.InvokeAsync(getter); public async Task CloseAsync() { var waitCompletionSource = new TaskCompletionSource<int>(); 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(); } } 

It's easy to use:

 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(); 
  • Did I understand correctly that in one of the classes, let's call it a bus (Bus) in the constructor, I launch a new stream, in which I save to the global position of the dispatcher: dispatcher = Dispatcher.CurrentDispatcher; and then in the same thread I create a window. And then, in the PutInfo method (string message); I call dispatcher.BeginInvoke (() => textBlock.Text + = message); ? - andreikashin
  • @andreikashin: Well, yes, it is. By the way, the code can be stolen from this answer: ru.stackoverflow.com/a/458425/10105 (class DispatcherThread ) - VladD
  • For some reason it is not possible to save the dispatcher globally. During a call to dispatcher.BeginInvoke, dispatcher is null. - andreikashin
  • @andreikashin: Are you sure that you are not contacting the dispatcher before the UI thread starts? Put a breakpoint and look. - VladD 2:55 pm
  • I'm not sure that I have a UI thread. since this class is created from the console application, and the thread is launched in the constructor of this class, the assignment dispatcher = Dispatcher.CurrentDispatcher; - andreikashin