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

C++ 像Photoshop CS一样管理窗口Z顺序,c++,winapi,C++,Winapi,所以我有一个应用程序,我希望它的窗口行为更像Photoshop CS。在Photoshop CS中,文档窗口始终位于工具窗口之后,但仍然是顶级窗口。对于MDI子窗口,由于文档窗口实际上是一个子窗口,因此不能将其移出主窗口。不过,在CS中,您可以将图像移动到不同的监视器精细位置,这与Visual Studio等停靠应用程序以及常规MDI应用程序相比具有很大优势 不管怎样,这是我到目前为止的研究。我曾尝试截获WM_MOUSEACTIVATE消息,并使用DeferWindowPos命令对窗口进行我自己

所以我有一个应用程序,我希望它的窗口行为更像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_POSCHANGED、WM_POSCHANGED和repaint消息) 3.用WM_NCACTIVATE和WM_ACTIVATE消息通知新激活的窗口

看起来最干净的方法是截取第一条WM_激活消息,然后以某种方式通知Windows您将覆盖其执行z排序的方式,然后使用DEFEREWindowPOS命令,但我不知道如何这样做。似乎一旦Windows发送WM_ACTIVATE消息,它就会按照自己的方式重新排序,因此我使用的任何WindowPos命令都会被覆盖

现在我已经有了一个基本的实现,当应用程序被激活时,它会使工具窗口成为最顶层,但当它没有激活时,它会使工具窗口成为非顶层,但这很奇怪(它有时会位于其他窗口的顶部,比如任务管理器,而Photoshop CS不会这样做,所以我认为Photoshop的做法有所不同)看起来会有一种更直观的方法来做


不管怎么说,有人知道Photoshop CS是如何做到这一点的吗?或者有一种比使用topmost更好的方法吗?

我想,由于他们不使用.NET,他们已经在多年的时间里推出了自己的窗口代码,现在,它与Amazon的原创产品一样,可以定制现成的产品(又称.NET的MDI支持)只是我们不会接近

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


看起来您已经必须深入研究Win32 API函数和值,才能看到“解决方案”,这应该是您的第一个危险信号。最终有可能吗?可能但是,根据您的需要和时间以及许多只有您才能决定的其他因素,这可能是不实际的。

由于Photoshop CS不熟悉,因此很难确切了解您想要实现的外观和感觉

但是我认为如果您在工具窗口时创建了一个无模式对话框窗口,并且确保它具有WS_弹出式样式,那么生成的工具窗口将不会被剪切到主父窗口,并且窗口将自动管理z顺序确保工具窗口保持在父窗口的顶部

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

您应该创建以图像为父对象的toolwindow,以便windows管理zorder。不需要设置WS_弹出窗口或WS_EX_工具窗口。这些标志仅控制窗口的渲染


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

我没有看到Photoshop CS有什么值得注意的地方,需要接近这种级别的黑客攻击,而这不能通过在创建窗口时指定正确的所有者窗口关系来实现。i、 e.任何必须显示在其他窗口上方的窗口在创建时指定该窗口为其所有者-如果您有多个文档窗口,则每个窗口都有自己的子窗口集,您可以在文档窗口获得和释放激活时动态显示和隐藏这些子窗口。

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


现在,我已经让它与最顶级的功能一起工作,但它充其量只是一个黑客。我确实得到了一些安慰,因为GIMP已经尝试用他们的版本2.6来模仿Photoshop,但即使是他们的实现偶尔也会表现出古怪的行为,这让我相信他们的实现也是一种黑客行为

当主窗口接收到焦点时,您是否尝试将工具窗口设置为最上面的窗口,而当它失去焦点时,您是否尝试将工具窗口设置为非最上面的窗口?听起来你已经开始考虑这种解决方案了。。。但要复杂得多

值得注意的是,工具windows exh
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; }
    }
}