Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to prevent focus steal from window in same application?

Tags:

c#

winapi

Using SetWindowLong() and WS_EX_NOACTIVATE I've made a window non-activatable.

  • If I put the cursor in Notepad and click the non-activatable window the cursor remains active in Notepad.
  • If I put the cursor in another window inside my application and click the non-activatable window the cursor will be removed from the other window.

I made a mini project to reproduce the issue: http://s000.tinyupload.com/index.php?file_id=78888532457762447347

Basically a WPF application with two windows:

MainWindow

// XAML code
<Window x:Class="CS_test.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Background="LightGray" Height="300" Width="400">
    <Grid>
        <TextBox Width="300" Height="200"></TextBox>
    </Grid>
</Window>

// C# code
namespace CS_test
{
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
            new NonActiveWindow().Show();
        }
    }
}

NonActiveWindow

// XAML code
<Window x:Class="CS_test.NonActiveWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="NonActiveWindow" Height="300" Width="300"
        Loaded="Window_Loaded">
    <Grid>
    </Grid>
</Window>

// C# code
namespace CS_test
{
    public partial class NonActiveWindow : Window
    {
        [DllImport("user32.dll")]
        public static extern IntPtr SetWindowLong(IntPtr hWnd, int nIndex, int dwNewLong);

        [DllImport("user32.dll")]
        public static extern int GetWindowLong(IntPtr hWnd, int nIndex);

        private const int GWL_EXSTYLE = -20;
        private const int WS_EX_NOACTIVATE = 0x08000000;

        public NonActiveWindow()
        {
            InitializeComponent();
        }

        private void Window_Loaded(object sender, RoutedEventArgs e)
        {
            WindowInteropHelper windowHelper = new WindowInteropHelper(this);
            SetWindowLong(windowHelper.Handle, GWL_EXSTYLE, GetWindowLong(windowHelper.Handle, GWL_EXSTYLE) | WS_EX_NOACTIVATE);
        }
    }
}

I know it is possible to fix the issue by instantiating NonActiveWindow in a separate thread (with a separate Dispatcher), but I can't use this approach due to threading issues.

Is there a way to prevent the focus loss without creating the non-activatable window in a separate thread?

Edit:

Video to show the issue (in response to Justins answer):

https://www.youtube.com/watch?v=6RNmJc7ya48

And just in case anyone wonders why the window isn't updated while dragging: Create a window using the WS_EX_NOACTIVATE flag ,but it can't be dragged until I release the mouse.

like image 494
Woodgnome Avatar asked Nov 22 '22 09:11

Woodgnome


1 Answers

The ShowActivated property is working fine for me:

public MainWindow()
{
    InitializeComponent();
    var otherWindow = new NonActiveWindow()
    {
        ShowActivated = false
    };
    otherWindow.Show();
}

My OtherWindow is just an empty WPF window and doesn't have any of the P/Invoke calls that your example has.

Note that MainWindow is shown very slightly after OtherWindow is shown because this call is in the constructor. If you want to fix this then you should call Show outside of the constructor, for example in the Loaded event.

like image 112
Justin Avatar answered Nov 23 '22 21:11

Justin