Окно Силы должно быть реактивным, но никогда не получать Фокус - PullRequest
0 голосов
/ 07 апреля 2020

Основному окну моего приложения нельзя позволять получать фокус в любое время, так как оно вызывается другим приложением, которое постоянно нуждается в фокусе при работе с ним. Кроме того, мое приложение всегда TopMost, разрешено запускать только с одним экземпляром и при вызове будет обрабатывать аргументы командной строки для запуска в другом контексте. Эта часть уже обработана и работает хорошо.

Я не могу добиться, по какой-то причине не получая фокусировки. Я несколько часов пытался использовать разные решения - все не работает так, как я ожидаю. Прежде всего, вот соответствующий XAML главного окна:

<mah:MetroWindow x:Class="Sprachrekorder.Code.View.Gui"
    x:Name="G"
    ...
    mc:Ignorable="d"
    ResizeMode="NoResize"
    WindowStartupLocation="Manual" 
    ShowTitleBar="true" 
    Topmost="True"
    ShowActivated="False" <- ONLY WORKS WHEN INITIALLY RUNNING THE PROGRAM, BUT FAILS WHEN CALLING THE SAME (ALREADY RUNNING) INSTANCE AGAIN
    Focusable="False"     <- HAS NO IMPACT
    SourceInitialized="Gui_OnSourceInitialized"
    d:DataContext="{d:DesignInstance {x:Type viewModel:MainViewModel}, IsDesignTimeCreatable=False}"
    ShowCloseButton="False"
    cust:WindowPosition.IsLocked="{Binding Settings.ActualConfig.WindowFixed, UpdateSourceTrigger=PropertyChanged}">

Код основного окна позади:

[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern int GetWindowLong(IntPtr hwnd, int index);

[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern int SetWindowLong(IntPtr hwnd, int index, int newStyle);

//private static IntPtr GetWindowLongPtr ( IntPtr hWnd, int index ) <- USING THIS CODE ALSO HAS NO IMPACT
//{
//    return (IntPtr.Size == 4) ? GetWindowLongPtrA(hWnd, index) : GetWindowLongPtrW(hWnd, index);
//}

//private static IntPtr SetWindowLongPtr ( IntPtr hWnd, int index, IntPtr newValue )
//{
//    return (IntPtr.Size == 4) ? SetWindowLongPtrA(hWnd, index, newValue) : SetWindowLongPtrW(hWnd, index, newValue);
//}

//[DllImport("user32.dll", EntryPoint = "GetWindowLongPtr", SetLastError = true)]
//private static extern IntPtr GetWindowLongPtrW ( IntPtr hWnd, int nIndex );

//[DllImport("user32.dll", EntryPoint = "GetWindowLong", SetLastError = true)]
//private static extern IntPtr GetWindowLongPtrA ( IntPtr hWnd, int nIndex );

//[DllImport("user32.dll", EntryPoint = "SetWindowLongPtr", SetLastError = true)]
//private static extern IntPtr SetWindowLongPtrW ( IntPtr hWnd, int nIndex, IntPtr dwNewLong );

//[DllImport("user32.dll", EntryPoint = "SetWindowLong", SetLastError = true)]
//private static extern IntPtr SetWindowLongPtrA ( IntPtr hWnd, int nIndex, IntPtr dwNewLong );

public Gui()
{
    Main = new MainViewModel();
    DataContext = Main;
    InitializeComponent();
    Main.Init();
    Loaded += (s, e) =>
    {
        WindowHandle = new WindowInteropHelper(Application.Current.MainWindow ?? throw new InvalidOperationException()).Handle;
        HwndSource.FromHwnd(WindowHandle)?.AddHook(HandleMessages);

        //Set the window style to noactivate.  <- DOES NOT WORK
        var currentValue = GetWindowLongPtr(WindowHandle, GWL_EXSTYLE);
        var newValue = new IntPtr(currentValue.ToInt64() | WS_EX_NOACTIVATE);
        SetWindowLongPtr(WindowHandle, GWL_EXSTYLE, newValue);
    };
}

private static IntPtr HandleMessages(IntPtr handle, int message, IntPtr wParameter, IntPtr lParameter, ref Boolean handled)
{
    //Deactivate Window focus completely <- ONLY SEEMS TO WORK IN DEBUG
    if (message == WM_MOUSEACTIVATE)
    {
        handled = true;
        return new IntPtr(MA_NOACTIVATE);
    }

    if (handle != WindowHandle) return IntPtr.Zero;

    var data = UnsafeNative.GetMessage(message, lParameter); <- THIS PART HANDLES THE COMMAND LINE ARGS IF APPLICATION IS ALREADY RUNNING AND IS CALLED AGAIN
    if (data == null || Application.Current.MainWindow == null) return IntPtr.Zero;
    if (Application.Current.MainWindow.WindowState == WindowState.Minimized) Application.Current.MainWindow.WindowState = WindowState.Normal;

    UnsafeNative.SetForegroundWindow(new WindowInteropHelper(Application.Current.MainWindow).Handle);

    var args = data.Split(' ');
    HandleParameter(args);
    handled = true;
    return IntPtr.Zero;
}

private void Gui_OnSourceInitialized(object sender, EventArgs e)
{
    var interopHelper = new WindowInteropHelper(this); // <- THIS CODE PART HAS NO IMPACT
    int exStyle = GetWindowLong(interopHelper.Handle, GWL_EXSTYLE);
    SetWindowLong(interopHelper.Handle, GWL_EXSTYLE, exStyle | WS_EX_NOACTIVATE);
}

Части класса UnsafeNative:

public const int WM_COPYDATA = 0x004A;

public static string GetMessage(int message, IntPtr lParam)
{
    if (message != WM_COPYDATA) return null;
    try
    {
        var data = Marshal.PtrToStructure<COPYDATASTRUCT>(lParam);
        var result = string.Copy(data.lpData);
        return result;
    }
    catch
    {
        return null;
    }
}

Я прокомментировал соответствующие части в моем коде. Кажется, что наиболее эффективная часть -

if (message == WM_MOUSEACTIVATE)
{
    handled = true;
    return new IntPtr(MA_NOACTIVATE);
}

в методе HandleMessages основного класса, но это работает только в Debug. Также при нажатии на приложение на панели задач windows оно восстанавливает фокус. Что я могу сделать, чтобы убрать фокус окна? У кого-нибудь есть идея?

1 Ответ

0 голосов
/ 07 апреля 2020

Для тех, кто заинтересован - это то, как я решил свою проблему: все вышеперечисленное не привело меня к той точке, которой я хотел быть, поэтому я подумал, может быть, я могу просто вернуть фокус в окно, потеряв фокус , Это сработало, и я добавил следующий код в код позади класса главного окна:

public partial class Gui
{
    //...

    //Added code below**************************
    [DllImport("user32.dll", SetLastError = true)]
    static extern IntPtr GetWindow(IntPtr hWnd, uint uCmd);
    enum GetWindow_Cmd : uint
    {
        GW_HWNDFIRST = 0,
        GW_HWNDLAST = 1,
        GW_HWNDNEXT = 2,
        GW_HWNDPREV = 3,
        GW_OWNER = 4,
        GW_CHILD = 5,
        GW_ENABLEDPOPUP = 6
    }
    [DllImport("user32.dll", ExactSpelling = true, CharSet = CharSet.Auto)]
    public static extern IntPtr GetParent(IntPtr hWnd);
    //******************************************

    private static IntPtr HandleMessages(IntPtr handle, int message, IntPtr wParameter, IntPtr lParameter, ref Boolean handled)
    {
        if (handle != WindowHandle) return IntPtr.Zero;

        var data = UnsafeNative.GetMessage(message, lParameter);
        if (data == null || Application.Current.MainWindow == null) return IntPtr.Zero;
        if (Application.Current.MainWindow.WindowState == WindowState.Minimized) Application.Current.MainWindow.WindowState = WindowState.Normal;

        UnsafeNative.SetForegroundWindow(new WindowInteropHelper(Application.Current.MainWindow).Handle);

        var args = data.Split(' ');
        HandleParameter(args);
        handled = true;

        //Added code below********************************
        //Set focus to the window that previously had the focus
        IntPtr lastWindowHandle = GetWindow(Process.GetCurrentProcess().MainWindowHandle, (uint)GetWindow_Cmd.GW_HWNDNEXT);
        while (true)
        {
            IntPtr temp = GetParent(lastWindowHandle);
            if (temp.Equals(IntPtr.Zero)) break;
            lastWindowHandle = temp;
        }
        UnsafeNative.SetForegroundWindow(lastWindowHandle);
        //************************************************
        return IntPtr.Zero;
    }

    //***
}

Остальная часть кода для этого класса находится в моем исходном посте. Код от https://www.whitebyte.info/programming/how-to-get-main-window-handle-of-the-last-active-window

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...