How do you handle multiple windows with different threads?

Emon Haque 3,176 Reputation points
2021-06-19T23:11:10.063+00:00

In an empty WPF Project first I've deleted everything except AssemblyInfo.cs, then added App.cs, where I start the app, and four other windows. RootWindow acts as the positioner/resizer of three other windows, TopWindow, MiddleWindow and BottomWindow. These three windows are identical, all they have is a constructor with these:

class MiddleWindow : Window  
{  
    public MiddleWindow() {  
        Title = "Middle";  
        ResizeMode = ResizeMode.NoResize;  
        AllowsTransparency = true;  
        WindowStyle = WindowStyle.None;  
        ShowInTaskbar = false;  
    }  
}  

in the RootWindow, I've three Rectangle, topRect, midRect, botRect, and in its ArrangeOverride I set the Top, Left, Width and Height of those three Windows based on the position of those Rectangles. Here's what happens when I run the app:

107284-test.gif

initially there's always a gap between the MiddleWindow and BottomWindow, As I resize the RootWindow those windows resizes sometimes with the gap and other times without gap. If I maximize and restore the RootWindow, those three also resize accordingly and when I move the RootWindow, they also move along. The problem arises when I click on any of those three windows or minimize the RootWindow. If I click on any of those three, all three starts blinking and all of them disappears eventually!

In the OnDeactivated of the RootWindow, I hide those so they become invisible and in the OnActivated of the RootWindow, I set Visibility of those to Visible BUT only the last one appears when I restore the RootWindow by clicking on taskbar!

Here's the RootWindow.cs:

class RootWindow : Window  
{  
    double radius = 5;  
    Border windowBorder, titleBar;  
    Grid contentGrid, titlebarIconGrid, rectGrid;  
    ActionButton close, minimize, maxRestore;  
    Rectangle topRect, midRect, botRect;  
    public TopWindow TopWin { get; set; }  
    public MiddleWindow MidWin { get; set; }  
    public BottomWindow BotWin { get; set; }  
    public RootWindow() {  
        Height = 800;  
        Width = 1200;  
        WindowStartupLocation = WindowStartupLocation.CenterScreen;  
        WindowStyle = WindowStyle.None;  
        AllowsTransparency = true;  
        WindowChrome.SetWindowChrome(this, new WindowChrome() {  
            ResizeBorderThickness = new Thickness(0, 0, 5, 5),  
            CaptionHeight = 0  
        });  
        addTitleIcons();  
        titleBar = new Border() {  
            CornerRadius = new CornerRadius(radius, radius, 0, 0),  
            Background = Brushes.LightGray,  
            Height = 32,  
            Effect = new DropShadowEffect() { BlurRadius = 5, Opacity = 0.5, Direction = -90 },  
            Child = titlebarIconGrid  
        };  
        initRects();  
        Grid.SetRow(rectGrid, 1);  
        contentGrid = new Grid() {  
            RowDefinitions = {  
                new RowDefinition() { Height = GridLength.Auto },  
                new RowDefinition()  
            },  
            Children = { titleBar, rectGrid }  
        };  
        windowBorder = new Border() {  
            Background = Brushes.Transparent,  
            CornerRadius = new CornerRadius(radius),  
            BorderThickness = new Thickness(1),  
            BorderBrush = Brushes.LightBlue,  
            Child = contentGrid  
        };  
        AddVisualChild(windowBorder);  
        titleBar.MouseLeftButtonDown += handleResize;  
        titleBar.MouseMove += move;  
        Loaded += onLoaded;  
    }  
    void onLoaded(object sender, RoutedEventArgs e) {  
        TopWin.Dispatcher.Invoke(TopWin.Show);  
        MidWin.Dispatcher.Invoke(MidWin.Show);  
        BotWin.Dispatcher.Invoke(BotWin.Show);  
    }  
    void initRects() {  
        topRect = new Rectangle()/* { IsHitTestVisible = false }*/;  
        midRect = new Rectangle()/* { IsHitTestVisible = false }*/;  
        botRect = new Rectangle()/* { IsHitTestVisible = false }*/;  
        Grid.SetRow(midRect, 1);  
        Grid.SetRow(botRect, 2);  
        rectGrid = new Grid() {  
            IsHitTestVisible = false,  
            RowDefinitions = {  
                new RowDefinition(),  
                new RowDefinition(),  
                new RowDefinition()  
            },  
            Children = { topRect, midRect, botRect }  
        };  
    }  
    void move(object sender, MouseEventArgs e) {  
        if (e.LeftButton == MouseButtonState.Pressed) {  
            DragMove();  
            InvalidateArrange();  
        }  
    }  
    void handleResize(object sender, MouseButtonEventArgs e) {  
        if (e.ClickCount == 2) resize();  
    }  
    void addTitleIcons() {  
        close = new ActionButton() {  
            Width = 24,  
            Height = 24,  
            ToolTip = "Close",  
            Margin = new Thickness(0, 0, 5, 0),  
            Icon = Icons.CloseCircle,  
            Command = Application.Current.Shutdown  
        };  
        maxRestore = new ActionButton() {  
            Width = 18,  
            Height = 18,  
            ToolTip = "Maximize",  
            Margin = new Thickness(0, 0, 5, 0),  
            Icon = Icons.Maximize,  
            Command = resize  
        };  
        minimize = new ActionButton() {  
            Width = 18,  
            Height = 18,  
            ToolTip = "Minimize",  
            Margin = new Thickness(0, 0, 5, 0),  
            Icon = Icons.Minimize,  
            Command = () => WindowState = WindowState.Minimized  
        };  
        Grid.SetColumn(close, 3);  
        Grid.SetColumn(maxRestore, 2);  
        Grid.SetColumn(minimize, 1);  
        titlebarIconGrid = new Grid() {  
            ColumnDefinitions = {  
                new ColumnDefinition(),  
                new ColumnDefinition(){ Width = GridLength.Auto },  
                new ColumnDefinition(){ Width = GridLength.Auto },  
                new ColumnDefinition(){ Width = GridLength.Auto }  
            },  
            Children = { close, maxRestore, minimize }  
        };  
    }  
    void resize() {  
        if (WindowState == WindowState.Maximized) {  
            ResizeMode = ResizeMode.CanResizeWithGrip;  
            WindowState = WindowState.Normal;  
            maxRestore.Icon = Icons.Maximize;  
            maxRestore.ToolTip = "Maximize";  
        }  
        else {  
            ResizeMode = ResizeMode.NoResize;  
            WindowState = WindowState.Maximized;  
            maxRestore.Icon = Icons.Restore;  
            maxRestore.ToolTip = "Restore";  
        }  
    }  

    protected override Size ArrangeOverride(Size arrangeBounds) {  
        windowBorder.Width = arrangeBounds.Width;  
        windowBorder.Height = arrangeBounds.Height;  
        windowBorder.Measure(arrangeBounds);  
        windowBorder.Arrange(new Rect(windowBorder.DesiredSize));  
        double width = topRect.ActualWidth;  
        double height = topRect.ActualHeight;  
        var topPoint = topRect.PointToScreen(new Point(0, 0));  
        var midPoint = midRect.PointToScreen(new Point(0, 0));  
        var botPoint = botRect.PointToScreen(new Point(0, 0));  
        TopWin.Dispatcher.Invoke(() => {  
            TopWin.Left = topPoint.X;  
            TopWin.Top = topPoint.Y;  
            TopWin.Width = width;  
            TopWin.Height = height;  
        });  
        MidWin.Dispatcher.Invoke(() => {  
            MidWin.Left = midPoint.X;  
            MidWin.Top = midPoint.Y;  
            MidWin.Width = width;  
            MidWin.Height = height;  
        });  
        BotWin.Dispatcher.Invoke(() => {  
            BotWin.Left = botPoint.X;  
            BotWin.Top = botPoint.Y;  
            BotWin.Width = width;  
            BotWin.Height = height;  
        });  
        return windowBorder.DesiredSize;  
    }  
    protected override void OnActivated(EventArgs e) {  
        TopWin.Dispatcher.Invoke(() => TopWin.Visibility = Visibility.Visible);  
        MidWin.Dispatcher.Invoke(() => MidWin.Visibility = Visibility.Visible);  
        BotWin.Dispatcher.Invoke(() => BotWin.Visibility = Visibility.Visible);  
        //TopWin.Dispatcher.Invoke(TopWin.Show);  
        //MidWin.Dispatcher.Invoke(MidWin.Show);  
        //BotWin.Dispatcher.Invoke(BotWin.Show);  
        //TopWin.Dispatcher.Invoke(() => TopWin.WindowState = WindowState.Normal);  
        //MidWin.Dispatcher.Invoke(() => MidWin.WindowState = WindowState.Normal);  
        //BotWin.Dispatcher.Invoke(() => BotWin.WindowState = WindowState.Normal);  
        //InvalidateArrange();  
        InvalidateVisual();  
    }  
    protected override void OnDeactivated(EventArgs e) {  
        TopWin.Dispatcher.Invoke(() => TopWin.Visibility = Visibility.Collapsed);  
        MidWin.Dispatcher.Invoke(() => MidWin.Visibility = Visibility.Collapsed);  
        BotWin.Dispatcher.Invoke(() => BotWin.Visibility = Visibility.Collapsed);  
        //TopWin.Dispatcher.Invoke(TopWin.Hide);  
        //MidWin.Dispatcher.Invoke(MidWin.Hide);  
        //BotWin.Dispatcher.Invoke(BotWin.Hide);  
        //TopWin.Dispatcher.Invoke(() => TopWin.WindowState = WindowState.Minimized);  
        //MidWin.Dispatcher.Invoke(() => MidWin.WindowState = WindowState.Minimized);  
        //BotWin.Dispatcher.Invoke(() => BotWin.WindowState = WindowState.Minimized);  
    }  
    protected override void OnClosed(EventArgs e) {  
        TopWin.Dispatcher.Invoke(TopWin.Close);  
        MidWin.Dispatcher.Invoke(MidWin.Close);  
        BotWin.Dispatcher.Invoke(BotWin.Close);  
        TopWin.Dispatcher.InvokeShutdown();  
        MidWin.Dispatcher.InvokeShutdown();  
        BotWin.Dispatcher.InvokeShutdown();  
        App.Current.Shutdown();  
    }  
    protected override Visual GetVisualChild(int index) => windowBorder;  
    protected override int VisualChildrenCount => 1;  
    protected override void OnClosing(CancelEventArgs e) {  
        titleBar.MouseLeftButtonDown -= handleResize;  
        titleBar.MouseMove -= move;  
    }  
}  

and here's the App.cs:

class App : Application  
{  
    [STAThread]  
    static void Main() => new App().Run();  
    protected override void OnStartup(StartupEventArgs e) {  
        TopWindow top = null;  
        MiddleWindow mid = null;  
        BottomWindow bot = null;  
        var thread1 = new Thread(() => {   
            top = new TopWindow() {  
                Content = new TextBlock() {   
                    Text = "Top Window",  
                    HorizontalAlignment = HorizontalAlignment.Center,  
                    VerticalAlignment = VerticalAlignment.Center,  
                    FontSize = 36  
                }   
            };   
            Dispatcher.Run();   
        });  
        var thread2 = new Thread(() => {   
            mid = new MiddleWindow() {  
                Content = new TextBlock() {  
                    Text = "Middle Window",  
                    HorizontalAlignment = HorizontalAlignment.Center,  
                    VerticalAlignment = VerticalAlignment.Center,  
                    FontSize = 36  
                }  
            };   
            Dispatcher.Run();   
        });  
        var thread3 = new Thread(() => {   
            bot = new BottomWindow() {  
                Content = new TextBlock() {  
                    Text = "Bottom Window",  
                    HorizontalAlignment = HorizontalAlignment.Center,  
                    VerticalAlignment = VerticalAlignment.Center,  
                    FontSize = 36  
                }  
            };   
            Dispatcher.Run();   
        });  
        thread1.SetApartmentState(ApartmentState.STA);  
        thread2.SetApartmentState(ApartmentState.STA);  
        thread3.SetApartmentState(ApartmentState.STA);  
        thread1.Start();  
        thread2.Start();  
        thread3.Start();  
        App.Current.MainWindow = new RootWindow() {  
            TopWin = top,  
            MidWin = mid,  
            BotWin = bot  
        };  
        App.Current.ShutdownMode = ShutdownMode.OnExplicitShutdown;  
        App.Current.MainWindow.Show();  
    }  
}  

You could download the project from GitHub. How do you solve these issues?

Developer technologies | Windows Presentation Foundation
0 comments No comments
{count} votes

1 answer

Sort by: Most helpful
  1. Emon Haque 3,176 Reputation points
    2021-06-20T03:37:51.037+00:00

    No clue, from where did I get the idea of using ArrangeOcerride in subclass of Window. It works fine without that:

    107312-test.gif

    Still couldn't figure out why is there the gap between Middle and Bottom window! Tried SnapsToDevicePixels = true in several places BUT that didn't solve the issue! Other than that, looks like everything works as expected. Updated the project in GitHub.


Your answer

Answers can be marked as Accepted Answers by the question author, which helps users to know the answer solved the author's problem.