关于C#:像Photoshop CS一样管理窗口Z顺序

Managing Window Z-Order Like Photoshop CS

因此,我有一个应用程序,其窗口行为我想更像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命令都将被覆盖。

现在我有一个基本的实现准工作方式,当应用程序被激活时,它使工具窗口位于最顶层,但当窗口未激活时,则使工具窗口变为非顶层,但这非常古怪(有时有时会在其他工具之上Windows就像任务管理器一样,而Photoshop CS却不这样做,所以我认为Photoshop会以某种方式做不同的事情),而且似乎会有一种更直观的方式来做到这一点。

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


我没有发现关于Photoshop CS的任何引人注目的东西,这些东西要求接近这种黑客攻击水平的东西,而不能仅仅通过在创建窗口时指定正确的所有者窗口关系来完成。也就是说,必须在其他窗口上方显示的任何窗口在创建时都将其指定为该窗口的所有者-如果您有多个文档窗口,则每个窗口都有其自己的一组子窗口,您可以随着文档窗口的增加而动态地显示和隐藏这些子窗口。并失去激活。


您可以尝试处理WM_WINDOWPOSCHANGING事件,以防止其他窗口重叠(带有伪最高标记)。因此,您避免了设置/清除TopMost标志的所有问题。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
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的行为与往常一样,但是将始终在当前级别为1的Windows下以及当前级别为1的Windows下。


对Photoshop CS不熟悉,很难确切地知道您要实现的外观。

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

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


您是否尝试过在主窗口获得焦点时将工具窗口置于最高位置,而在失去焦点时使工具窗口置于非最高位置?听起来您好像已经开始研究这种解决方案了……但是要更加详细。

作为一个注释,似乎很有据可查,当涉及到z排序时,工具窗口表现出意外的行为。我尚未在MSDN上找到任何要确认的内容,但可能是Windows专门对其进行了管理。


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

目前,我已经将其与最重要的功能配合使用,但这充其量只是一个技巧。我确实感到安慰,因为GIMP尝试使用2.6版来模仿Photoshop,但是即使是其实现有时也会表现出古怪,这使我相信它们的实现也是一种hack。


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

您应该使用图像作为父级来创建工具窗口,以便Windows管理zorder。无需设置WS_POPUP或WS_EX_TOOLWINDOW。这些标志仅控制窗口的呈现。

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


我想他们会因为他们没有使用.NET而在存在的多年中滚动自己的窗口代码,而现在,就像Amazon最初的OBIDOS一样,如此定制他们的产品就可以了-shelf(又名.NET的MDI支持)不会很接近。

我不喜欢没有真实答案的答案,但是如果像Photoshop那样的东西确实是您的目标,那么您可能必须花费大量的时间和精力来获得类似的东西。值得您花时间吗?请记住,多年来有许多程序员和各种版本聚在一起,以使Photoshop的简单化窗口行为能够正确工作并让您感到自然。

看来您已经必须深入研究Win32 API函数和值才能瞥见"解决方案",这应该是您的第一个危险信号。最终有可能吗?大概。但是取决于您的需求和时间以及您只能决定的许多其他因素,这可能不切实际。