禁用 WPF 窗口焦点

25

我有一个WPF窗口,只有在按住Tab键时通过Visibility.Hidden和Visibility.Visible才会显示出来。然而,按住键会将焦点从活动应用程序转移到WPF窗口。我能否禁用此行为?更进一步:即使单击控件,仍然可以注册控件的单击操作,但是否可能完全防止窗口获得焦点?


可能是在不窃取焦点的情况下显示表单?的重复问题。 - rugk
1
@rugk 这与其他问题不同,因为另一个问题是询问如何在不获取焦点的情况下显示,但模糊了是否允许用户交互。答案也只解决了无激活部分,但不清楚是否允许用户交互(顶部选定的答案没有)。 - Lunyx
@rugk,但是这个问题涉及到WPF,而你的链接是针对WinForms问题的。尽管两种GUI都允许回退到Win32,但根据我的经验,在WPF中,焦点情况是一个特别复杂和精细的功能区域,相比之下,意味着(对于“焦点”问题),适用于Win32 / WinForms的解决方法可能不适用于WPF。 - Glenn Slayden
5个回答

25

找到了其他地方的答案:

protected override void OnSourceInitialized(EventArgs e)
{
    base.OnSourceInitialized(e);

    //Set the window style to noactivate.
    var helper = new WindowInteropHelper(this);
    SetWindowLong(helper.Handle, GWL_EXSTYLE,
        GetWindowLong(helper.Handle, GWL_EXSTYLE) | WS_EX_NOACTIVATE);
}   

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

[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);

这需要在Loaded事件中发生,该事件发生在Activated事件之前。如果您在OnActivated中执行它,则窗口已经被激活,为时已晚...它只能防止从那一点开始的焦点。 - Nick
最好使用SourceInitialized。 - Ievgen
最好使用SetWindowLongPtr和GetWindowLongPtr。请参阅pinvoke.net网站上的定义。 - Der_Meister

15
自从.NET 3.5 SP1,WPF表单就有了ShowActivated属性。将其设置为false,这样标记的表单就不会再窃取焦点了。

我已经尝试过了,但它只会导致我的程序崩溃。 - Lunyx

7

您可以通过添加自定义的 WndProc 并处理 WM_MOUSEACTIVATE 来防止 WPF 窗口在鼠标点击时激活:

protected override void OnSourceInitialized(EventArgs e)
{
    base.OnSourceInitialized(e);
    var source = PresentationSource.FromVisual(this) as HwndSource;
    source.AddHook(WndProc);
}
private IntPtr WndProc(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
{
    if (msg == WM_MOUSEACTIVATE)
    {
        handled = true;
        return new IntPtr(MA_NOACTIVATE);
    }
    else return IntPtr.Zero;
}
private const int WM_MOUSEACTIVATE = 0x0021;
private const int MA_NOACTIVATE = 0x0003;

参考文献:


我从未收到过WM_MOUSEACTIVATE消息。 - tofutim
哇!我已经寻找这个问题的解决方案两天了,而这是唯一一个对我有用的!非常感谢您提供这个绝妙的代码片段,并让我学到了很多 :) - mgarant

5

防止WPF中某些顶级窗口被激活:

我尝试了这里给出的Win32解决方案,但它对我没有用。虽然它似乎可以防止窗口“激活”,但是Focus之后就会留在不确定状态,没有恢复到应用程序中的另一个合格窗口。相反,以下方法适用于我:

首先,请确保所有非主窗口都将其Owner属性设置为主Window。我在子窗口的构造函数中执行此操作,在这种情况下,必须采取一些步骤(此处未讨论)以确保主Window首先加载。

public MySubWindow()
{
    if ((base.Owner = Application.Current.MainWindow) == null)
        throw new Exception();

    InitializeComponent();
}

设置 Owner 属性还应确保子窗口位于主窗口顶部。对于子窗口,按照以下属性设置(XAML 或代码):

ShowActivated="False"
Focusable="False"
ShowInTaskbar="False"
IsEnabled="False"
FocusManager.IsFocusScope="False"

最后,在被阻塞的窗口中添加一个 OnActivated 处理程序。由于调用基方法会触发 Activated 事件,因此我不会调用基方法。(请注意,不要从 Visual Studio 设计器切换激活,否则窗口将变为不可见状态)。

protected override void OnActivated(EventArgs e)
{
    if (DesignerProperties.GetIsInDesignMode(this))
        return;

    base.Owner.Activate();
}

我真的很喜欢它。没有C调用,甚至还聚焦主窗口。这种行为对于特定的应用程序来说甚至更好。构造函数不应抛出错误。base.Owner<-不需要"base",使用简单的Owner就足够了。 - Audrius Gailius

0
也许你想要的是 PopupWindow 而不是 Window?它有一个 Focusable 属性,你可以将其设置为 false(我认为默认情况下可能已经是 false 了)。

你可以在这里看到:http://msdn.microsoft.com/en-us/library/system.windows.window.aspx,Window也有一个Focusable属性。我已经尝试将所有内容设置为Focusable = false,但它仍然无法解决我的问题。 - Lunyx

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接