【问题标题】:Cannot force my window to be on top无法强制我的窗口位于顶部
【发布时间】:2015-07-21 09:48:17
【问题描述】:

我需要我的窗口位于另一个窗口的顶部。该“其他”窗口(应用程序)来自不同的开发人员。我没有它的源代码。我只能使用 Spy++ 来获取有关它的信息。

我使用的是 Windows 7。

我尝试了几件事,但都没有成功。

这是我迄今为止尝试过的:

1) 定时器+BringWindowToTop

2) 我更改了窗口的所有者

        IntPtr handle = User32.FindWindow("Vega Prime", "Vega Prime");

        NativeWindow win = new NativeWindow();
        win.AssignHandle(handle);

        ChildForm form = new ChildForm();
        form.Show(win);

当我说它不起作用时,我的意思是:

1) 起初一切看起来都很好:我的窗口在顶部

2) 然后我点击下方的窗口(Vega Prime)

3) 我的窗口消失了

4) 我点击了我的窗口应该在的地方,它又出现了 (!!!!!!)

那是什么?这怎么可能?

更新:

我花了一些时间试图找到解决问题的方法。 这是我发现的:

TopMost window going behind non-TopMost fullscreen window sometimes

https://social.msdn.microsoft.com/Forums/vstudio/en-US/92e66584-6cb8-4976-9531-eab3b9a129e3/mfc-window-with-wsextopmost-hidden-by-full-screen-window?forum=vcgeneral

我很确定我的问题与 Windows 7 上的“全屏问题”有关(有时,当不是最顶部的窗口变为全屏时,它会强制最顶部的窗口隐藏)。这就解释了上述奇怪的行为,对吧?

【问题讨论】:

  • this.TopMost = true;

标签: c# winforms


【解决方案1】:

我意识到这个例子似乎可以解决问题:

public partial class Form1 : Form
{
    IntPtr hWndToStayOver = User32.FindWindow("Vega Prime", "Vega Prime");
    private System.Windows.Forms.Timer timer1 = new System.Windows.Forms.Timer();

    public Form1()
    {
        InitializeComponent();
        this.Load += new EventHandler(this.Form1_Load);
        this.FormClosing += new FormClosingEventHandler(Form1_FormClosing);
        this.timer1.Tick += new System.EventHandler(this.timer1_Tick);
    }

    private void Form1_Load(object sender, EventArgs e)
    {
        timer1.Start(); // Routine starts now that I'm sure my Form exists
    }

    void Form1_FormClosing(object sender, FormClosingEventArgs e)
    {
        timer1.Stop(); // Stop routine (my Form doesn't exist anymore)
    }

    private bool AmIAboveOtherWindow()
    {
        IntPtr tmpHwnd = User32.GetNextWindow(hWndToStayOver, User32.GetNextWindowCmd.GW_HWNDPREV);
        while (tmpHwnd != (IntPtr)0)
        {
            if (tmpHwnd == this.Handle)
                return true;

            tmpHwnd = User32.GetNextWindow(tmpHwnd, User32.GetNextWindowCmd.GW_HWNDPREV);
        }
        return false;
    }

    private void timer1_Tick(object sender, EventArgs e)
    {
        timer1.Stop();
        if (!AmIAboveOtherWindow()) // Check if I am behind the target window
        {
            User32.SetWindowPos(this.Handle, hWndToStayOver, 0, 0, 0, 0, // Move my Form behind the target
                User32.SetWindowPosFlags.SWP_NOMOVE |
                User32.SetWindowPosFlags.SWP_NOSIZE |
                User32.SetWindowPosFlags.SWP_SHOWWINDOW |
                User32.SetWindowPosFlags.SWP_NOACTIVATE |
                User32.SetWindowPosFlags.SWP_ASYNCWINDOWPOS);

            User32.SetWindowPos(hWndToStayOver, this.Handle, 0, 0, 0, 0, // Move target behind my Form
                User32.SetWindowPosFlags.SWP_NOMOVE |
                User32.SetWindowPosFlags.SWP_NOSIZE |
                User32.SetWindowPosFlags.SWP_SHOWWINDOW |
                User32.SetWindowPosFlags.SWP_NOACTIVATE |
                User32.SetWindowPosFlags.SWP_ASYNCWINDOWPOS);
        }
        timer1.Start();
    }
}

User32 类

public class User32
{
    [Flags]
    public enum SetWindowPosFlags : uint
    {
        SWP_ASYNCWINDOWPOS = 0x4000,
        SWP_DEFERERASE = 0x2000,
        SWP_DRAWFRAME = 0x0020,
        SWP_FRAMECHANGED = 0x0020,
        SWP_HIDEWINDOW = 0x0080,
        SWP_NOACTIVATE = 0x0010,
        SWP_NOCOPYBITS = 0x0100,
        SWP_NOMOVE = 0x0002,
        SWP_NOOWNERZORDER = 0x0200,
        SWP_NOREDRAW = 0x0008,
        SWP_NOREPOSITION = 0x0200,
        SWP_NOSENDCHANGING = 0x0400,
        SWP_NOSIZE = 0x0001,
        SWP_NOZORDER = 0x0004,
        SWP_SHOWWINDOW = 0x0040,
    }

    public enum GetNextWindowCmd : uint
    {
        GW_HWNDNEXT = 2,
        GW_HWNDPREV = 3,
    }

    [DllImport("user32.dll", SetLastError = true)]
    public static extern bool SetWindowPos(IntPtr hWnd, IntPtr hWndInsertAfter, int X, int Y, int cx, int cy, SetWindowPosFlags uFlags);
    [DllImport("user32.dll", SetLastError = true)]
    public static extern IntPtr FindWindow(string lpClassName, string lpWindowName);
    [DllImport("user32.dll", SetLastError = true, EntryPoint = "GetWindow")]
    public static extern IntPtr GetNextWindow(IntPtr hWnd, GetNextWindowCmd uCmd);
}

显然,您必须在此代码中添加对目标句柄的一些检查:此示例仅在加载表单时目标窗口存在并且直到结束才关闭时才有效

【讨论】:

  • 谢谢 - 我会试试看
  • @walruz:这绝对不是要走的路。解决方案是使用单个窗口 api 设置所有权 b/w 2 个窗口:SetWindowLong 或 SetWindowLongPtr。
  • @walruz 根据您的更新,要求是窗口停留在全屏窗口之上。我认为我的解决方案在这种情况下不会有帮助
  • @codroipo:你觉得我应该怎么做?
【解决方案2】:

确保将窗口的所有者设置为另一个窗口。 这将确保您的窗口始终位于所有者之上。 Here的如何导入appr。原生函数,只需在任何地方将“Get”更改为“Set”即可。

然后你可以这样调用它:

SetWindowLong(handle_of_owned_window, -8, handle_of_owner_window);

提示 #1:通过 Form.Owner 属性设置所有权 b/w 2 Form 实例很容易,但是您无权访问其中一个 Forms。

提示 #2:为了访问窗口句柄,它需要首先显示至少一次。

【讨论】:

  • 如果我错了,请纠正我,但您不希望您的窗口位于所有内容之上(例如记事本)。您只想位于通过 FindWindow api 获得其句柄的特定窗口的顶部...
  • 对我来说没关系。我一开始就将我的窗口设为 TopMost。我的窗户仍然从视野中消失了。您如何看待 Windows 7 的“全屏问题”???
  • 我的意见是你应该去掉设置 TopMost 属性的代码。创建一个影响所有其他正在运行的具有 UI 的应用程序的应用程序通常不是一个好主意。当然,除非它是必需的。很少有应用程序使用 TopMost,例如任务管理器/进程资源管理器,但它们都可以启用/禁用此行为。
  • 请看一下我用 NativeWindow 做的“技巧”。和你的建议不一样吗?
  • 要验证您的“技巧”不起作用,请使用 Spy++,找到您的窗口--> 右键单击​​它--> 属性--> Windows 选项卡--> 检查所有者窗口组.那里有有效的句柄还是(无)?
【解决方案3】:

事实证明,“其他”应用程序(我试图与我的应用程序合并)正在使用全屏独占模式。这就解释了为什么我最上面的窗口从视图中消失并且再也没有出现(除非我通过鼠标单击关闭全屏模式)。

关于全屏独占模式的详细信息在这里:

https://docs.oracle.com/javase/tutorial/extra/fullscreen/exclusivemode.html

基本思想是全屏独占模式“允许程序员暂停窗口系统,以便可以直接在屏幕上进行绘图”。我相信这意味着在全屏独占模式(一些专家称之为“真正的全屏”)期间,操作系统会忽略不同的窗口(以节省资源)。

在这种情况下,唯一的解决方案是配置“其他”应用程序以禁用全屏模式。

这对我有帮助 - 我研究了文档并在配置文件中找到了将全屏模式设置为 false 的位置。

【讨论】:

    猜你喜欢
    • 2010-12-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-08-23
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多