像Photoshop CS一样管理窗口Z顺序

时间:2008-09-30 15:53:52

标签: c++ winapi

所以我有一个应用程序,其窗口行为我想表现得更像Photoshop CS。在Photoshop CS中,文档窗口始终位于工具窗口后面,但仍然是顶级窗口。对于MDI子窗口,由于文档窗口实际上是子窗口,因此无法将其移出主窗口。但是,在CS中,您可以将图像移动到不同的显示器,这比与Visual Studio等对接应用程序以及常规MDI应用程序相比具有很大的优势。

无论如何,到目前为止,这是我的研究。我试图拦截WM_MOUSEACTIVATE消息,并使用DeferWindowPos命令来执行我自己的窗口排序,然后返回MA_NOACTIVATEANDEAT,但这会导致窗口无法正常激活,我相信还有其他命令可以“激活” “一个没有调用WM_MOUSEACTIVATE的窗口(比如我认为的SetFocus()),所以这个方法可能无论如何都不会起作用。

我相信Windows“激活”窗口的程序是 1.使用WM_NCACTIVATE和WM_ACTIVATE消息通知未激活的窗口 2.将窗口移动到z顺序的顶部(发送WM_POSCHANGING,WM_POSCHANGED和重绘消息) 3.使用WM_NCACTIVATE和WM_ACTIVATE消息通知新激活的窗口。

似乎最干净的方法是拦截第一个WM_ACTIVATE消息,并以某种方式通知Windows你要覆盖他们执行z排序的方式,然后使用DeferWindowPos命令,但我可以弄清楚如何这样做。似乎一旦Windows发送了WM_ACTIVATE消息,它就已经按照自己的方式重新排序了,所以我使用的任何DeferWindowPos命令都被覆盖了。

现在我有一个基本的实现quasy-working,当应用程序被激活时,工具窗口最顶层,但是当它不是时,它会使它们不是最顶层,但它非常古怪(它有时会在其他顶部像任务管理器的窗口,而Photoshop CS不这样做,所以我认为Photoshop在某种程度上做了不同的方式),似乎有一种更直观的方式来做它。

无论如何,有没有人知道Photoshop CS是如何做到的,或者比使用topmost更好的方式?

7 个答案:

答案 0 :(得分:3)

我没有看到任何关于Photoshop CS的任何重要信息都要求接近这种级别的黑客攻击,而不能简单地通过在创建窗口时指定正确的所有者窗口关系来完成。即任何必须在其他窗口上方显示的窗口在创建时指定该窗口作为其所有者 - 如果您有多个文档窗口,每个窗口都有自己的一组拥有子窗口,您可以在文档窗口获得时动态显示和隐藏并失去激活。

答案 1 :(得分:2)

您可以尝试处理WM_WINDOWPOSCHANGING事件以防止覆盖其他窗口(使用伪最顶层标志)。因此,您可以避免设置/清除TopMost标志的所有问题。

public class BaseForm : Form
{
    public virtual int TopMostLevel
    {
        get { return 0; }
    }

    [DllImport("user32.dll")]
    [return: MarshalAs(UnmanagedType.Bool)]
    static extern bool EnumThreadWindows(uint dwThreadId, Win32Callback lpEnumFunc, IntPtr lParam);

    /// <summary>
    /// Get process window handles sorted by z order from top to bottom.
    /// </summary>
    public static IEnumerable<IntPtr> GetWindowsSortedByZOrder()
    {
        List<IntPtr> handles = new List<IntPtr>();
        EnumThreadWindows(GetCurrentThreadId(),
                          (hWnd, lparam) =>
                              {
                                  handles.Add(hWnd);
                                  return true;
                              }, IntPtr.Zero);
        return handles;
    }


    protected override void WndProc(ref Message m)
    {
            if (m.Msg == (int)WindowsMessages.WM_WINDOWPOSCHANGING)
            {
                //Looking for Window at the bottom of Z-order, but with TopMostLevel > this.TopMostLevel
                foreach (IntPtr handle in GetWindowsSortedByZOrder().Reverse())
                {
                    var window = FromHandle(handle) as BaseForm;
                    if (window != null && this.TopMostLevel < window.TopMostLevel)
                    {
                        //changing hwndInsertAfter field in WindowPos structure
                        if (IntPtr.Size == 4)
                        {
                            Marshal.WriteInt32(m.LParam, IntPtr.Size, window.Handle.ToInt32());
                        }
                        else if (IntPtr.Size == 8)
                        {
                            Marshal.WriteInt64(m.LParam, IntPtr.Size, window.Handle.ToInt64());
                        }
                        break;
                    }
                }
            }

        base.WndProc(ref m);
    }
}

public class FormWithLevel1 : BaseForm
{
    public override int TopMostLevel
    {
        get { return 1; }
    }
}

因此,FormWithLevel1将始终覆盖任何BaseForm。您可以添加任意数量的Z顺序级别。同一级别的Windows表现与往常一样,但总是在Windows下,级别为Current + 1,而Windows级别为Current-1。

答案 2 :(得分:1)

不熟悉Photoshop CS,很难确切地知道你想要达到的外观和感觉。

但我想如果您在工具窗口中创建了一个无模式对话框窗口,并确保它具有 WS_POPUP 样式,那么生成的工具窗口不会被剪切到主父窗口, Windows 会自动管理 z-Order ,确保工具窗口保持不变在父窗口的顶部。

由于工具窗口对话框是无模式的,因此不会干扰主窗口。

答案 3 :(得分:0)

Managing Window Z-Order Like Photoshop CS

您应该创建工具窗口,并将图像作为父级,以便Windows管理zorder。无需设置WS_POPUP或WS_EX_TOOLWINDOW。这些标志只控制窗口的渲染。

使用图像窗口的hwnd作为父窗口调用CreateWindowEx。

答案 4 :(得分:0)

对Chris和Emmanuel的回复,使用所有者窗口功能的问题是窗口只能由另一个窗口拥有,并且您无法更改谁拥有窗口。因此,如果工具窗口A和B总是需要位于文档窗口C和D的顶部,那么当文档窗口C处于活动状态时,我希望它拥有窗口A和B,以便A和B始终位于其顶部。但是当我激活文档窗口D时,我必须将工具窗口A和B的所有权更改为D,否则它们将在窗口D后面(因为它们由窗口C拥有)。但是,Windows不允许您更改窗口的所有权,因此该选项不可用。

现在我已经使用了最顶级的功能,但它最好是一个黑客。我确实得到了一些安慰,因为GIMP已经尝试用他们的2.6版本来模仿Photoshop,但即使他们的实现偶尔也会变得古怪,这让我相信他们的实现也是一个黑客。

答案 5 :(得分:0)

您是否尝试在主窗口获得焦点时使工具窗口位于最顶层,而在失去焦点时非最顶层?听起来你已经开始考虑这种解决方案......但更为精细。

作为一个注释,似乎有很好的记录,工具窗口在z排序时表现出意想不到的行为。我没有在MSDN上找到任何确认信息,但可能是Windows专门管理它们。

答案 6 :(得分:-1)

我想他们已经,因为他们没有使用.NET,在其存在的多年中推出了他们自己的窗口代码,现在,就像亚马逊的原始OBIDOS一样,对他们的产品进行定制现成的(即.NET的MDI支持)就不会接近了。

我不喜欢在没有真正答案的情况下回答,但很可能你需要花费大量的时间和精力来获得类似的东西如果像Photoshop一样真的是你的目标。值得你花时间吗?只需记住许多程序员许多年,并且版本已经结合在一起,使Photoshop的简单看似窗口行为正常工作并让您感觉自然。

看起来你已经不得不深入研究Win32 API函数和值,甚至可以瞥见“解决方案”,这应该是你的第一个红旗。它最终可能吗?大概。但是,根据您的需求和时间以及许多其他因素,您只能决定,这可能不切实际。