如何保持我的Window Forms应用程序窗口的Z顺序?

时间:2019-04-23 01:12:34

标签: .net vb.net winforms

我有一个运行时打开8个窗口的应用,每个窗口都是图形。
如何保持8个窗口相对于彼此的Z顺序?

我的同事们使用此应用程序在窗户上伸展和移动。
当他们将一个窗口放在另一个窗口上时,我想在启动时恢复此Z顺序。

保持位置,尺寸(w&h)和WindowState的效果很好。

我尝试了GetChildIndex(),但是在构建时我得到了:

  

未声明GetChildIndex()”

1 个答案:

答案 0 :(得分:3)

一种利用GetWindow函数确定应用程序(打开的)窗体的Z顺序,然后在重新启动应用程序时恢复相同顺序的方法。

(为简单起见,表单的订单被保存到应用程序路径中的文件中。您需要使其适应应用程序要求(当前存储格式))

当应用程序的主窗体关闭时(Form.FormClosing事件),打开的窗体的当前Z顺序将存储到文件中。
注意:我假设应用程序的关机模式设置为启动表单关闭时

将要显示“主表单”(Form.Shown事件)时,将恢复先前的表单Z顺序。创建上次关闭应用程序时打开的每个Forms的实例,并显示每个Form,并保留Z顺序。

使用Activator.CreateInstance方法创建每个实例,并传递从存储中检索到的表单的名称(此处为文件)。

注意:截至目前,表单的位置尚未保存。您说您已经有这项工作了。如果需要,此处提供的代码可以轻松调整为也存储这些度量。

Imports System.IO
Imports System.Reflection

Private Sub Form1_FormClosing(sender As Object, e As FormClosingEventArgs) Handles MyBase.FormClosing
    Dim zOrder As Dictionary(Of Integer, String) = GetWindowZOrder(Me.Handle, False)
    If zOrder Is Nothing Then return
    Using sw As New StreamWriter("FormsOrder.txt")
        For Each form In zOrder
            sw.WriteLine($"{form.Key},{form.Value}")
        Next
    End Using
End Sub

Private Sub Form1_Shown(sender As Object, e As EventArgs) Handles MyBase.Shown
    If File.Exists("FormsOrder.txt") Then
        Dim orderList As String() = File.ReadAllLines("FormsOrder.txt").Reverse().ToArray()
        Dim appNameSpace = Assembly.GetExecutingAssembly().GetName().Name
        For Each formOrder As String In orderList
            Dim formName As String = formOrder.Split(","c)(1)
            Dim form = CType(Activator.CreateInstance(Type.GetType($"{appNameSpace}.{formName}")), Form)
            form.Show()
        Next
    End If
End Sub

声明和GetWindow帮助方法( GetWindowZOrder ):

hwndFirst方法的 GetWindowZOrder 参数是开始枚举的Window的句柄。它可以是您的主表单(开始表单),也可以是您接下来打开的表单之一。
如果hwndFirst是您的MainForm,则要将其从枚举中排除,将False作为 includeFirst 参数(1) 。如果hwndFirst child 形式之一,则相反。

(1)如果从主窗体调用了此方法,我们不想再次打开它。

然后,GetWindowZOrder方法调用GetWindow以获取当前Z顺序中的下一个Window句柄,验证该句柄是否属于应用程序,如果存在,则将其添加到Dictionary中。
然后,它返回一个Dictionary(Of Integer, String),其中Key代表顺序,Value代表表单的名称。

此代码可以是模块的一部分。

Imports System.Runtime.InteropServices

<DllImport("user32.dll", SetLastError:=True)>
Private Function GetWindow(hWnd As IntPtr, uCmd As GetWindowRel) As IntPtr
End Function

Private Enum GetWindowRel As UInteger
    GW_HWNDFIRST = 0
    GW_HWNDLAST = 1
    GW_HWNDNEXT = 2
    GW_HWNDPREV = 3
    GW_OWNER = 4
    GW_CHILD = 5
    GW_ENABLEDPOPUP = 6
End Enum

Public Function GetWindowZOrder(hwndFirst As IntPtr, includeFirst As Boolean) As Dictionary(Of Integer, String)
    Dim zOrder As New Dictionary(Of Integer, String)()
    Dim form As Form = GetOpenedForm(hwndFirst)
    If form Is Nothing Then Return Nothing

    Dim order As Integer = 0
    If includeFirst Then zOrder.Add(order, form.Name)

    Dim hwndNext As IntPtr = hwndFirst
    While True
        hwndNext = GetWindow(hwndNext, GetWindowRel.GW_HWNDNEXT)
        If hwndNext = IntPtr.Zero Then Exit While
        form = GetOpenedForm(hwndNext)
        If Not (form Is Nothing) Then
            order += 1
            zOrder.Add(order, form.Name)
        End If
    End While
    Return zOrder
End Function

Private Function GetOpenedForm(hwnd As IntPtr) As Form
    Return Application.OpenForms.OfType(Of Form)().FirstOrDefault(Function(f) f.Handle = hwnd)
End Function