Windows On A Different Thread

Introduction

 
This is a helpful solution if you want to dive deeper into multithreading windows using WPF. I hope it helped you understand how to develop Windows in a different thread and sort of situation which you need to figure out. You can use it for your own purposes and even bind more complex logic to your window which runs in different threads.
 

Plan

  1. Purpose and problem description
  2. Annoying while trying to find the right solution and meanwhile solution
  3. Good idea with a custom window and final solution
  4. Summarize
  5. Links and greetings

Purpose and Problem Description 

 
I’m an experienced WPF/.NET developer. And everyone who works with that amazing technology knows that there are a lot of backside things which unfortunately don’t have right or good solutions. I’d like to tell you about this kind of exceptional case.
 
This time, I was developing a desktop application and on the right top of the working window, I have to put the EKG gif (The Graphics Interchange Format, which is by simple words just animated image). This EKG should show you that the application doesn’t stick, that it’s alive, and gives a good feeling to the user who is working with it.
 
So, before, I had some experience with that kind of apps. Of course, I implemented everything together, don’t mind and sometimes it stuck.
 
A little step to left. When you work with a big amount of data in WPF that kind things unfortunately happen. Because, when you are sending data to the UI the WPF-renderer at the moment stuck and all your live objects stopped moving, which makes a bad impression for users. Of course, there are existing solutions, that can work for your needs for some point: you can send it partially (old school solution using BackgroundWorker), using virtualization (which is also far from an ideal solution), and so on. Always controlling these things is annoying me, I’d say.
 
That’s why I started to think on some better solution which could make this EKG animation works totally independent from the rest of app processes. Finally, I found it, but on my way, I met a lot of difficulties and showstopper cases.
 

Annoying trying to find the right solution and meanwhile solution 

 
Yes, my decision was to make it running on a different thread. You know it is easy with the backend process but isn’t so on UI. In WPF starts with two threads: one for handling rendering and another for managing the UI. The rendering thread effectively runs hidden in the background while the UI thread receives input, handles events, paints the screen, and runs application code. The WPF-applications use a single UI thread, although in some situations it is not the best solution.
 
First of all, I wasn’t sure if this is a really good way, but when I’m starting something new which I’m not sure I always say to myself that this is programming and there is everything is possible.
 
So, I started with running a new window, but you can’t write just a new Window() (because it will run in the same thread), you should run it in a different one, specially dedicated thread. The solution didn’t make me wait:
  1. internal void MakeEkgOnDifferentThread()  
  2. {  
  3.    try  
  4.    {  
  5.       Thread newWindowThread = new Thread(new ThreadStart(ThreadStartingPoint));  
  6.       newWindowThread.SetApartmentState(ApartmentState.STA);  
  7.       newWindowThread.IsBackground = true;  
  8.       newWindowThread.Start();  
  9.    }  
  10.    catch (Exception ex)  
  11.    {  
  12.       // logging it  
  13.    }  
  14. }  
  15.   
  16. private void ThreadStartingPoint()  
  17. {  
  18.    try  
  19.    {  
  20.       EkgController = new EkgController();  
  21.       EkgController.Show();  
  22.       System.Windows.Threading.Dispatcher.Run();  
  23.    }  
  24.    catch (Exception ex)  
  25.    {  
  26.    // logging it  
  27.    }  
  28. }  
I have authentication in my app and I call the method MakeEkgOnDifferentThread() right after a successful login. From this time the Other thread window is running in other thread and it will not be stuck even if you will call something like Thread.Sleep(5000) in your main app. This is exactly what we need, but there are a lot of “underwater stones”, mostly with UI part and user feelings from using the app.
 
My WPF application designed for adaptive layout, you can maximize or change the size of the window how you want, or moving it from place to place. In the window, by design, I have also place for EKG, the user should see it through all app lifecycle. From the information above you can understand that EKG-window should be glued to the main window. This wasn’t a big problem to resolve. I was subscribed to main window events StateChanged, LocationChanged, SizeChanged.
 
I made 2 help methods, which will return you real window coefficient for calculating real width and height (also depends on which screen you use (in case of multiple connected screens), which scaling settings you have set in windows settings (very often for HD 125% and for 4k 200 or 150%), etc.),
  1. internal static double GetWindowsScalingWidth()  
  2. {  
  3.    return Screen.PrimaryScreen.Bounds.Width / SystemParameters.PrimaryScreenWidth;  
  4. }  
  5.   
  6. internal static double GetWindowsScalingHeight()  
  7. {  
  8.    return Screen.PrimaryScreen.Bounds.Height / SystemParameters.PrimaryScreenHeight;  
  9. }  
FYI: In the main window view I use the Grid to make a layout markup. And I put the Grid with name GridForOtherThreadWindow where I’d like to see the EKG-Window. And I decided to wrap the EKG-window to some controller class EkgManager which will handle my window and keep some settings there, just for comfort.
 
Below is the method that I will call a lot of times when events StateChanged, LocationChanged, SizeChanged will be invoked. This method defines the position of the EKG-window,
  1. private void GetEkgSettings()  
  2. {  
  3.    if (EkgManager.Current == nullreturn;  
  4.    var content = (FrameworkElement)this.GridForOtherThreadWindow;  
  5.    var currentPosition = content.PointToScreen(new Point(0, 0));  
  6.    var widthFactor = MainWindow.GetWindowsScalingWidth();  
  7.    var heightFactor = MainWindow.GetWindowsScalingHeight();  
  8.   
  9.    EkgManager.Current.Settings["Left"] = currentPosition.X / widthFactor;  
  10.    EkgManager.Current.Settings["Top"] = currentPosition.Y / heightFactor;  
  11.    EkgManager.Current.Settings["Width"] = content.ActualWidth * widthFactor;  
  12.    EkgManager.Current.Settings["Height"] = content.ActualHeight * heightFactor;  
  13.    EkgManager.Current.WindowSettingsChanged();  
  14. }  
  15.   
  16. // EkgController is our target EKG-window managed from EkgManager.  
  17. internal void WindowSettingsChanged()  
  18. {  
  19.    try  
  20.    {  
  21.       if (EkgController == nullreturn;  
  22.       EkgController.Dispatcher.Invoke(() => UpdateWindowSettings());  
  23.    }  
  24.    catch (Exception ex)  
  25.    {  
  26.    // log it  
  27.    }  
  28. }  
  29.   
  30. private void UpdateWindowSettings()  
  31. {  
  32.    EkgController.Width = Settings["Width"];  
  33.    EkgController.Height = Settings["Height"];  
  34.    EkgController.Left = Settings["Left"];  
  35.    EkgController.Top = Settings["Top"];  
  36. }  
Here we are. Ready to go deeper and see what happens when event LocationChanged is invoked?
  1. private void WindowLocationChangedHandler(object sender, EventArgs e)  
  2. {  
  3.    GetEkgSettings();  
  4. }  
Go to the next one SizeChanged. But be careful, it will not work properly if you will subscribe to the event of your Main Window SizeChanged.
 
You should subscribe on the event of your container (Grid, StackPanel, whatever) which holds your content, that’s why I named it as Content SizeChanged.
  1. private void ContentSizeChanged(object sender, SizeChangedEventArgs e)  
  2. {  
  3.    GetEkgSettings();  
  4. }  
For now, it looks pretty easy, doesn’t it?
 
Implementing handlers for LocationChanged and SizeChanged events let us stick our other thread window to the main window. But we also want to see it above our main window. And in this case, TopMost property helps us to implement it. Just set it is true and you will get the needed result. Of course, I will not remind you about it if all has been done without trouble.
 
The most interesting part is coming here when we implement handler for the StateChanged event. You should make it because, in case of minimizing your Main Window, the Ekg-Window will not be minimized. And this isn’t only one bad thing which happens. Your Main Window will not restore back anymore, because Windows OS somehow thinks that it is already restored because of visible Ekg-Window. So, for this reason, I implemented a handler for StateChanged event and I’d like to minimize my window I’m killing the Ekg-window and after restoring creating it again. Believe me, this works fine solution (Maybe, you will find better, describe it in comments). Also, one more reason to use wrapper for another thread window. Here is the implementation:
  1. private void MainRootStateChanged(object sender, EventArgs e)  
  2. {  
  3.    if (this.WindowState == WindowState.Minimized)  
  4.    {  
  5.       // remove ekg  
  6.       if (EkgManager.Current == nullreturn;  
  7.       EkgManager.Current.KillEkg();  
  8.    }  
  9.    else  
  10.    {  
  11.       // make ekg  
  12.       if (EkgManager.Current != null && EkgManager.Current.EkgController != nullreturn;  
  13.       EkgManager.Current.MakeEkgOnDifferentThread();  
  14.       if (this.WindowState != WindowState.Maximized)  
  15.       {  
  16.       // this is some trick for refreshing main window, as I said before WPF and Windows  
  17.       // management there isn’t ideal.  
  18.          Task.Run(() =>  
  19.          {  
  20.             Thread.Sleep(1000);  
  21.             this.Dispatcher.Invoke(() =>  
  22.             {  
  23.                this.Left += 0.1;  
  24.             });  
  25.          });  
  26.       }  
  27.    }  
  28. }  
  29.   
  30. internal void KillEkg()  
  31. {  
  32.       try  
  33.       {  
  34.          EkgController.Dispatcher.Invoke(DispatcherPriority.ApplicationIdle, new ThreadStart(() =>  
  35.          {  
  36.             EkgController.Close();  
  37.             if (EkgController != null)  
  38.             {  
  39.                EkgController.Dispose();  
  40.             }  
  41.          }));  
  42.   
  43.          EkgController = null;  
  44.       }  
  45.       catch (Exception ex)  
  46.       {  
  47.          // log it  
  48.       }  
  49. }  
After these tricks, I’d be sure that I was on the right way. And here is coming one more problem related to TopMost property of Ekg-window. Of course, somehow you should manage it when you switching between windows. Because, if not – your ekg-window will be above others.
 
As a rule, all that happened in our app should be related to Main Window and in this case, we need to play with Activated and Deactivated events. The main idea was to play with property TopMost of Ekg-window when Main Window activated or deactivated. For this purpose, I made two methods on EkgManager which set TopMost to true, and to false depends on MainWindow IsActive property changed. Here is some piece of code:
  1. internal void WindowActivated()  
  2. {  
  3.    try  
  4.    {  
  5.       if (EkgController == nullreturn;  
  6.       if (EkgController.Dispatcher.CheckAccess())  
  7.       {  
  8.          EkgController.Topmost = true;  
  9.       }  
  10.       else  
  11.       {  
  12.          EkgController.Dispatcher.Invoke(DispatcherPriority.ApplicationIdle, new ThreadStart(() =>  
  13.          {  
  14.             EkgController.Topmost = true;  
  15.          }));  
  16.       }  
  17.    }  
  18.    catch (Exception ex)  
  19.    {  
  20.       // log it  
  21.    }  
  22. }  
It was not working fine for me. When I was clicking between other app windows, very often Ekg-window still appeared above them and didn’t hide. I said, okay, let’s try to manage it from native functions provided by User32.dll. I imported “SetWindowPos” function and played with it. From the first few tests it seemed like a working solution, but finally, I went to similar problems and I’d like to ask Microsoft corp.: “WTF with it?”.
 
Meanwhile, I also wanted to resolve the issue with visibility Ekg-window from Task Manager. Every window in WPF has the property ShowInTaskbar, which when you set to false will hide your window from Processes tab in Task Manager, but in Details tab there you still able to find it which I didn’t want to. Here is some trick which helped me totally hide it, and here is User32 helped me. Below is a listing of class OtherThreadWindowBase which Ekg-window inherits:
  1. public class OtherThreadWindowBase : Window, IDisposable  
  2. {  
  3.    private const int GWL_EXSTYLE = -20;  
  4.    private const int WS_EX_TOOLWINDOW = 0x00000080;  
  5.    internal WindowInteropHelper _wndHelper;  
  6.   
  7.    public OtherThreadWindowBase()  
  8.    {  
  9.       this.Loaded += OtherThreadWindowBase_Loaded;  
  10.    }  
  11.      
  12.    private void OtherThreadWindowBase_Loaded(object sender, RoutedEventArgs e)  
  13.    {  
  14.       _wndHelper = new WindowInteropHelper(this);  
  15.       int exStyle = GetWindowLong(_wndHelper.Handle, GWL_EXSTYLE);  
  16.       SetWindowLong(_wndHelper.Handle, GWL_EXSTYLE, exStyle | WS_EX_TOOLWINDOW);  
  17.    }  
  18.   
  19.    [DllImport("user32.dll")]  
  20.    internal static extern int SetWindowLong(IntPtr window, int index, int value);  
  21.   
  22.    [DllImport("user32.dll")]  
  23.    internal static extern int GetWindowLong(IntPtr window, int index);  
  24.   
  25.    public void Dispose()  
  26.    {  
  27.       try  
  28.       {  
  29.          this.Loaded -= OtherThreadWindowBase_Loaded;  
  30.          GC.Collect();  
  31.          GC.WaitForPendingFinalizers();  
  32.       }  
  33.       catch (Exception ex)  
  34.       {  
  35.          // log it  
  36.       }  
  37.    }  
  38. }  
So, we are happy with a lot of useful things, but still here is a problem with appearing EKG above other app windows. I understood, that keeping TopMost property of the Ekg-window set in true is a troublemaker solution. Let’s look at the solution what I did to resolve this problem in the next paragraph.
 

Finally, a good idea with custom window 

 
The right solution was lying on the top. This is making the custom window (Main Window). I will not describe all subtleties of the creation custom window in WPF (because it could get the place for a separate story if you want you can add the questions related to comments or just review how it implemented in the attached sources), but I’ll describe the most important goals of it.
 
Default Window has property AllowsTransparency, but you can set it to true only when other property WindowStyle set to None, in other words, it will not work. WindowStyle property is setting the style of the window which defines the top-buttons - minimize, maximize and close the window and providing you the title with an icon if defined. As you understand, here is a key, but I can’t publish the app without title and top-buttons. If you need to mix these properties, you should define your own window style and related behavior, which is what I did.
 
How did this all behave on my solution? I made the background of my custom window transparent and I colored different pieces of the window to the background which I need, but that one piece which I named GridForOtherThreadWindow kept transparent. And I modified the handlers for methods Activated and Deactivated related to Main Window. Deactivated handler I removed, cause you don’t need it anymore and Activated modified,
  1. private void MainRootActivated(object sender, EventArgs e)  
  2. {  
  3.    if (EkgManager.Current == nullreturn;  
  4.    EkgActivator();  
  5. }  
  6.   
  7. private void EkgActivator()  
  8. {  
  9.    if (EkgManager.Current == nullreturn;  
  10.    EkgManager.Current.WindowActivated();  
  11. }  
  12.   
  13. internal void WindowActivated()  
  14. {  
  15.    try  
  16.    {  
  17.       if (EkgController == nullreturn;  
  18.       if (EkgController.Dispatcher.CheckAccess())  
  19.       {  
  20.          EkgController.Topmost = true;  
  21.          EkgController.Topmost = false;  
  22.          EkgController.Focus();  
  23.       }  
  24.       else  
  25.       {  
  26.          EkgController.Dispatcher.Invoke(DispatcherPriority.ApplicationIdle, new ThreadStart(() =>  
  27.          {  
  28.             EkgController.Topmost = true;  
  29.             EkgController.Topmost = false;  
  30.             EkgController.Focus();  
  31.          }));  
  32.       }  
  33.    }  
  34.    catch (Exception ex)  
  35.    {  
  36.       // log it by your favorite logger  
  37.    }  
  38. }  
As you can see above, I just setting Ekg-window TopMost property to true for appear the Ekg-window above other app-windows and setting it back to false which returns it back to our main window, but above of 3d party app-windows, and we still can see the Ekg because it located in our transparent peace of Main Window.
 
We are almost done, but one more thing, when the app is starting and you switched to a different window which makes the main window deactivated then Ekg-Window appearing above of 3d party app-window. I resolved this issue by the trick – when window loaded (in my case you successfully login to the system) I’m setting TopMost to true and back for Main Window also and as a result, everything appears above other all windows, which you could see in the behavior of the different apps and it will not take questions from users, because it seems like normal.
 

Summary

 
This is a helpful solution if you want to dive deeper into multithreading windows using WPF. I hope it helped you understand how to develop Windows in different threads and sort of situation which you need to figure out. You can use it for your own purposes and even bind more complex logic to your window which runs in different threads. You need to use Dispatcher if you want to communicate between these windows. For my apps, I also use this kind solution for showing a busy indicator (progress ring) or for “notification” progress bar.
 
I will be glad to get all of your thoughts and advice to improve this solution, don’t hesitate to describe negative opinions or your own experiences.
 
Links and greetings
  1. Threading model
  2. How to create custom window chrome in WPF?
  3. Custom WPF Window with Windows System Functionality Restored
  4. Also, thanks to StackOverflow, C# Corner, Code project, and WPF/.NET developers’ communities.