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