【问题标题】:Force Window to be reactive, but never gain Focus强制窗口反应,但永远不会获得焦点
【发布时间】:2020-04-07 06:36:30
【问题描述】:

我的应用程序的主窗口在任何时候都不应被允许获得焦点,因为它被另一个在使用它时不断需要焦点的应用程序调用。此外,我的应用程序始终是 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-Method 中,但这仅适用于 Debug。此外,当单击 Windows 任务栏中的应用程序时,它会重新获得焦点。我该怎么做才能消除窗口的焦点?有人有想法吗?

【问题讨论】:

    标签: c# wpf window focus


    【解决方案1】:

    致所有感兴趣的人-这就是我解决问题的方法:以上所有内容都没有使我达到我想要的程度,所以我想,也许我可以将焦点放回失去焦点的窗口.解决了,我在主窗口的类后面的代码中添加了以下代码:

    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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-09-04
      • 1970-01-01
      • 2011-12-06
      • 1970-01-01
      • 2011-03-10
      • 2011-09-11
      相关资源
      最近更新 更多