C# と VB.NET の質問掲示板

ASP.NET、C++/CLI、Java 何でもどうぞ

C# と VB.NET の入門サイト

BringToFrontを最後に更新する方法

[トピック内 17 記事 (1 - 17 表示)]  << 0 >>

■96106 / inTopicNo.1)  BringToFrontを最後に更新する方法
  
□投稿者/ 雀 (8回)-(2020/10/21(Wed) 18:07:17)

分類:[.NET 全般] 

http://bbs.wankuma.com/index.cgi?mode=al2&namber=96047

このページでウインドウをZオーダーの設定の仕方を教えていただきました。

複数のウインドウのZオーダーを設定するには
ウインドウを順番に .BringToFront()を設定していく必要があります。
ウインドウの数が多いと、画面がちかちかして時間がかかってしまいます。

SuspendLayout()
ResumeLayout()

のようなコマンドを使って,最後に一度の画面の更新を行いたいのですが
どのようにすれば良いですか?


引用返信 編集キー/
■96116 / inTopicNo.2)  Re[1]: BringToFrontを最後に更新する方法
□投稿者/ KOZ (140回)-(2020/10/22(Thu) 09:27:53)
No96106 (雀 さん) に返信
> ウインドウを順番に .BringToFront()を設定していく必要があります。
> ウインドウの数が多いと、画面がちかちかして時間がかかってしまいます。

だからこそ、並びの順に下から作成するのがベストだと書いたのですが・・・

一応、こんな感じで抑制できそうです。

<DllImport("user32.dll")>
Private Shared Function LockWindowUpdate(ByVal hWndLock As IntPtr) As Boolean
End Function

Private Enum ShowWindowCommands
    SW_HIDE = 0
    SW_SHOWNORMAL = 1
    SW_SHOWMINIMIZED = 2
    SW_SHOWMAXIMIZED = 3
    SW_MAXIMIZE = 3
    SW_SHOWNOACTIVATE = 4
    SW_SHOW = 5
    SW_MINIMIZE = 6
    SW_SHOWMINNOACTIVE = 7
    SW_SHOWNA = 8
    SW_RESTORE = 9
    SW_SHOWDEFAULT = 10
    SW_FORCEMINIMIZE = 11
End Enum

<DllImport("user32.dll")>
Private Shared Function ShowWindow(hWnd As IntPtr, <MarshalAs(UnmanagedType.I4)> nCmdShow As ShowWindowCommands) As <MarshalAs(UnmanagedType.Bool)> Boolean
End Function

Private Sub ToolStripButton1_Click(sender As Object, e As EventArgs) Handles ToolStripButton1.Click
    If Me.MdiChildren.Count > 0 Then
        Dim mc As MdiClient = Me.MdiChildren(0).Parent
        LockWindowUpdate(mc.Handle)
        ShowWindow(mc.Handle, ShowWindowCommands.SW_HIDE)
        '
        ' 並び替えの処理
        '
        ShowWindow(mc.Handle, ShowWindowCommands.SW_SHOW)
        LockWindowUpdate(IntPtr.Zero)
    End If
End Sub

引用返信 編集キー/
■96119 / inTopicNo.3)  Re[2]: BringToFrontを最後に更新する方法
□投稿者/ 雀 (9回)-(2020/10/22(Thu) 11:20:46)
No96116 (KOZ さん) に返信

ありがとうございます。

まだコードは試していませんが、
> だからこそ、並びの順に下から作成するのがベストだと書いたのですが・

既にフォームを表示した状態で、並び順だけを変更したいのですが、
その場合、ご提示してくださった方法がベストと考えて良いでしょうか?

あるいは、全てのフォームを一度消してから、
再作成した方が良いという意味でしょうか?

引用返信 編集キー/
■96121 / inTopicNo.4)  Re[3]: BringToFrontを最後に更新する方法
□投稿者/ KOZ (141回)-(2020/10/22(Thu) 11:45:16)
No96119 (雀 さん) に返信

やりたいことは

No96047 より抜粋
> 子ウインドウの位置や表示順番(最前面などの設定)を取得して
> 保存しておき、再度プログラムを開いた時に
> 再現できるようにしたいと考えています。

ではないのでしょうか?
これに対するベストな方法を示したつもりです。

描画を抑止して並び替えするのがよいか、ウインドウを削除して再作成するほうがよいかはウインドウの数によると思います。

引用返信 編集キー/
■96129 / inTopicNo.5)  Re[4]: BringToFrontを最後に更新する方法
□投稿者/ 雀 (10回)-(2020/10/22(Thu) 20:24:41)
No96121 (KOZ さん) に返信



ありがとうございます。

試してみたところ、Zオーダーに関してはうまくいきました。

ただ、少し気になったのは、

Zオーダーと同時に、ウインドウサイズや位置、状態も復元したいと考えています。

あるウインドウが最小化した状態で
以下のコードを走らせると、


Private Sub ToolStripButton1_Click(sender As Object, e As EventArgs) Handles ToolStripButton1.Click
    If Me.MdiChildren.Count > 0 Then
        Dim mc As MdiClient = Me.MdiChildren(0).Parent
        LockWindowUpdate(mc.Handle)
        ShowWindow(mc.Handle, ShowWindowCommands.SW_HIDE)
        '
        ' 並び替えと、ウインドウサイズや位置、状態の処理(最小化していたウインドウが通常モードに切り替わる)
        '
        ShowWindow(mc.Handle, ShowWindowCommands.SW_SHOW)
        LockWindowUpdate(IntPtr.Zero)
    End If
End Sub

最小化していたウインドウを通常モードしようとすると、
タイトルバーだけのウインドウになってしまいます。


いろいろと試してみたのですが、
以下のように、SW_FORCEMINIMIZEを使えば、この問題は起きなくなります。


        Dim mc As MdiClient = Me.MdiChildren(0).Parent
        LockWindowUpdate(mc.Handle)
        ShowWindow(mc.Handle, ShowWindowCommands.SW_FORCEMINIMIZE)
        '
        ' 並び替えと、ウインドウサイズや位置、状態の処理
        '
        ShowWindow(mc.Handle, ShowWindowCommands.SW_SHOW)
        LockWindowUpdate(IntPtr.Zero)

  SW_MINIMIZEでもいけるかと思いましたが、
SW_FORCEMINIMIZEでしかうまくいかないようです。

SW_FORCEMINIMIZEについて調べてみると

https://www.tokovalue.jp/function/ShowWindow.htm

Windows 2000:たとえウィンドウを所有するスレッドがハングしていても、ウィンドウを最小化する。

このフラグは、ほかのスレッドのウィンドウを最小化する場合にだけ使用する。

Windows 2000と書かれてあり、何かよく分からない説明が書かれてあります。

Windows10を使った場合で、上記のような使い方は問題ないのでしょうか?

引用返信 編集キー/
■96137 / inTopicNo.6)  Re[5]: BringToFrontを最後に更新する方法
□投稿者/ KOZ (142回)-(2020/10/23(Fri) 09:20:26)
2020/10/23(Fri) 09:40:21 編集(投稿者)

No96129 (雀 さん) に返信

ShowWindow(mc.Handle, ShowWindowCommands.SW_HIDE)

これは、子ウインドウの表示領域となるウィンドウ(MDIClient)を非表示にします。
mc.Visible = False とほぼ同じですが、Visible プロパティを変更すると子ウインドウにも影響を与えるのに対し、この方法だと子ウインドウに影響しません。

> 最小化していたウインドウを通常モードしようとすると、
> タイトルバーだけのウインドウになってしまいます。

タイトルバーだけのウインドウ = 最小化状態です。
MDIClient が非表示になっており、子ウインドウの Visible プロパティは False を返すので WindowState プロパティの変更がききません。

以下は Form.WindowState プロパティの実装部分です。
https://referencesource.microsoft.com/#System.Windows.Forms/winforms/Managed/System/WinForms/Form.cs#2785


子ウインドウの WindowState を変更したら、以下の様に強制的に変更してください。

子ウインドウ.WindowState = FormWindowState.Normal
ShowWindow(子ウインドウ.Handle, ShowWindowCommands.SW_SHOWNOACTIVATE)

引用返信 編集キー/
■96139 / inTopicNo.7)  Re[1]: BringToFrontを最後に更新する方法
□投稿者/ とっちゃん (697回)-(2020/10/23(Fri) 10:43:40)
No96106 (雀 さん) に返信

> ウインドウを順番に .BringToFront()を設定していく必要があります。
> ウインドウの数が多いと、画面がちかちかして時間がかかってしまいます。
>
> SuspendLayout()
> ResumeLayout()
>
> のようなコマンドを使って,最後に一度の画面の更新を行いたいのですが
> どのようにすれば良いですか?
>
>
SuspendLayout() を行った後(SuspendLayoutはMDiContainerに行う必要があるはず)

一番下のMdiChild.ZOrder = 1
以下順に ZOrder を 2, 3 とセットし、
一通り終わったら ResumeLayout() を呼び出して、レイアウトしてもらう
とすれば、でZオーダーの入れ替えを一括して行えそうな気がします。

試していないのでできるかどうかわかりませんが。


引用返信 編集キー/
■96156 / inTopicNo.8)  Re[2]: BringToFrontを最後に更新する方法
□投稿者/ 雀 (11回)-(2020/10/23(Fri) 18:52:34)

KOZ さん

ありがとうございます。

ご提示してくださった方法でもうまくいきました。

ただ、10回ループを回して処理時間を計測したところ、

ShowWindow(mc.Handle, ShowWindowCommands.SW_HIDE)
を使うと2400ミリ秒かかるところが、

ShowWindow(mc.Handle, ShowWindowCommands.SW_FORCEMINIMIZE)
を使うと1400ミリ秒で済みます、
SW_FORCEMINIMIZEの方が1.7倍程度高速なようです。

そのため、できれば、SW_FORCEMINIMIZEの方を使いたいと考えていますが、
こちらを使っても問題ないのでしょうか?

懸念しているのは、SW_FORCEMINIMIZEの説明を見るとWindows2000のことが書かれているので
環境によってはエラーが出る場合もあるのではないかと考えています。
ご提示してくださった方を推奨しますでしょうか?




とっちゃん さん

ありがとうございます。

MDiContainerとMdiChildを取得するにはどのようにしたら良いですか?



引用返信 編集キー/
■96162 / inTopicNo.9)  Re[3]: BringToFrontを最後に更新する方法
□投稿者/ とっちゃん (698回)-(2020/10/23(Fri) 23:07:47)
No96156 (雀 さん) に返信
> とっちゃん さん
>
> ありがとうございます。
>
> MDiContainerとMdiChildを取得するにはどのようにしたら良いですか?
>
MdiChild は、MdiChildren の各要素になるFormオブジェクトです。
MDiContainer は、MdiClient という.NET のウィンドウクラスのオブジェクトです。

通常は、IsMdiContainer == true なFormウィンドウが間接的な使うので
.NET においてはあまり直接的に障ることはない気がしますが、この手の
非標準的な仕組みやOSの機能を直接的に扱うという場合は意識しておくとよい気がする
というオブジェクトの一つです。

引用返信 編集キー/
■96163 / inTopicNo.10)  Re[3]: BringToFrontを最後に更新する方法
□投稿者/ 魔界の仮面弁士 (2890回)-(2020/10/23(Fri) 23:45:46)
No96156 (雀 さん) に返信
> MDiContainerとMdiChildを取得するにはどのようにしたら良いですか?

こんな感じで。

If Me.IsMdiContainer Then
  Dim container As MdiClient = Me.Controls.OfType(Of MdiClient).Single()
  For Each child As Form In container.MdiChildren

  Next
End If



引用返信 編集キー/
■96164 / inTopicNo.11)  Re[4]: BringToFrontを最後に更新する方法
□投稿者/ 雀 (12回)-(2020/10/24(Sat) 08:22:37)
No96163 (魔界の仮面弁士 さん) に返信


ありがとうございます。

                            If frm00.IsMdiContainer Then
                                Dim container As MdiClient = frm00.Controls.OfType(Of MdiClient).Single()
                                For Each child As Form In container.MdiChildren

                                    child.ZOrder = 1

                                Next
                            End If

とやろうとすると、
'ZOrder' は 'Form' のメンバーではありません。

というエラーが出てしまいます。

                                    CType(child, MdiChild).ZOrder = 1

でも駄目なようです。
何かやり方が間違っているのでしょうか?

引用返信 編集キー/
■96165 / inTopicNo.12)  Re[5]: BringToFrontを最後に更新する方法
□投稿者/ 魔界の仮面弁士 (2891回)-(2020/10/24(Sat) 13:39:32)
No96164 (雀 さん) に返信
> 何かやり方が間違っているのでしょうか?

WPF 向けの GetZIndex / SetZIndex というものなら聞いたことがあるのですが、
WinForm 用の ZOrder プロパティなるものの存在は自分も聞いたことが無いですね…。


MDIChild 自体は .TopLevel = False 状態の System.Windows.Forms.Form にしか過ぎないはずで、
ここに Z で始まる名前のメンバーは存在していないはずです。

MdiClient の方も、WM_MDICASCADE / WM_MDITILE / WM_MDIICONARRANGE を SendMessage するための
LayoutMdi() メソッドがあるぐらいで。
引用返信 編集キー/
■96166 / inTopicNo.13)  Re[6]: BringToFrontを最後に更新する方法
□投稿者/ 雀 (13回)-(2020/10/24(Sat) 15:17:05)
No96165 (魔界の仮面弁士 さん) に返信

ありがとうございます。

やはり、KOZさんが教えてくださった方法を使った方が良いと思います。

もし、ご存じでしたら、
ShowWindow(mc.Handle, ShowWindowCommands.SW_HIDE)
ShowWindow(mc.Handle, ShowWindowCommands.SW_FORCEMINIMIZE)

の違いをお教え願えないでしょうか?
SW_FORCEMINIMIZEを使っても問題ないのでしょうか?

引用返信 編集キー/
■96167 / inTopicNo.14)  Re[7]: BringToFrontを最後に更新する方法
□投稿者/ とっちゃん (699回)-(2020/10/24(Sat) 22:26:44)
No96166 (雀 さん) に返信
> ■No96165 (魔界の仮面弁士 さん) に返信
> 
うあ、ごめんなさい。
Windows.Forms ではなくて、Microsoft Forms のメンバーでした。

えっと。。。そうすると、ちらつきなしでとするなら、DeferWindowPos を使う方法か。
Windows API なので、P/Invoke で呼び出しになりますが。


// 以下C#なコード(ただし、P/Invoke 省略)
List<Form> orderList; // Zorder 的に下から順に並べたいウィンドウのリスト

var dwp = BeginDeferWindowPos( orderList.Length );

Form prev = null;
foreach( var form in orderList )
{
  DeferWindowPos( dwp, form, prev, 0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE );
  prev = form;
}
EndDeferWindowPos( dwp );

これで、任意順に並べ替えができるはずです。


引用返信 編集キー/
■96174 / inTopicNo.15)  Re[8]: BringToFrontを最後に更新する方法
□投稿者/ KOZ (143回)-(2020/10/26(Mon) 09:32:31)
No96167 (とっちゃん さん) に返信
おお、こんないい方法があったのですね。知りませんでした。
とっちゃんさん、ありがとうございます。

順番を逆にするサンプルを書いてみました。
childForm.Left, childForm.Top, childForm.Width, childForm.Height の部分は回復する位置・大きさに修正してください。

<DllImport("user32.dll", SetLastError:=True, CharSet:=CharSet.Auto)>
Private Shared Function GetWindow(ByVal hWnd As IntPtr, ByVal uCmd As GetWindowType) As IntPtr
End Function

Private Enum GetWindowType 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

<DllImport("user32.dll", SetLastError:=True)>
Private Shared Function BeginDeferWindowPos(nNumWindows As Integer) As IntPtr
End Function

Private Enum ShowWindowPosition As UInteger
    SWP_NOSIZE = &H1
    SWP_NOMOVE = &H2
    SWP_NOZORDER = &H4
    SWP_NOREDRAW = &H8
    SWP_NOACTIVATE = &H10
    SWP_DRAWFRAME = &H20
    SWP_FRAMECHANGED = &H20
    SWP_SHOWWINDOW = &H40
    SWP_HIDEWINDOW = &H80
    SWP_NOCOPYBITS = &H100
    SWP_NOOWNERZORDER = &H200
    SWP_NOREPOSITION = &H200
    SWP_NOSENDCHANGING = &H400
    SWP_DEFERERASE = &H2000
    SWP_ASYNCWINDOWPOS = &H4000
End Enum

<DllImport("user32.dll", SetLastError:=True)>
Private Shared Function DeferWindowPos(
    hWinPosInfo As IntPtr,
    hWnd As IntPtr,
    hWndInsertAfter As Integer,
    x As Integer, y As Integer,
    cx As Integer, cy As Integer,
    uFlags As ShowWindowPosition) As IntPtr
End Function

<DllImport("user32.dll", SetLastError:=True)>
Private Shared Function EndDeferWindowPos(hWinPosInfo As IntPtr) As <MarshalAs(UnmanagedType.Bool)> Boolean
End Function

Private Sub ToolStripButton2_Click(sender As Object, e As EventArgs) Handles ToolStripButton2.Click
    If Me.MdiChildren.Count > 0 Then
        Dim orderList As New List(Of Form)
        Dim childHwnd As IntPtr = GetWindow(Me.MdiChildren(0).Handle, GetWindowType.GW_HWNDLAST)
        While childHwnd <> IntPtr.Zero
            Dim childForm As Form = Control.FromHandle(childHwnd)
            orderList.Add(childForm)
            childHwnd = GetWindow(childHwnd, GetWindowType.GW_HWNDPREV)
        End While
        Dim dwp As IntPtr = BeginDeferWindowPos(orderList.Count)
        Dim prev As IntPtr = IntPtr.Zero
        For Each childForm As Form In orderList
            childForm.WindowState = FormWindowState.Normal
            DeferWindowPos(dwp, childForm.Handle, prev, childForm.Left, childForm.Top, childForm.Width, childForm.Height, 0)
            prev = childForm.Handle
        Next
        EndDeferWindowPos(dwp)
    End If
End Sub

引用返信 編集キー/
■96176 / inTopicNo.16)  Re[9]: BringToFrontを最後に更新する方法
□投稿者/ とっちゃん (701回)-(2020/10/26(Mon) 14:13:37)
No96174 (KOZ さん) に返信

> 順番を逆にするサンプルを書いてみました。

フォローありがとうございます。
そして、自分んコードをよく見てみたら、FormをAPIに渡そうとしていた…orz
あそこは、form.Handle じゃないとだめだわ><

引用返信 編集キー/
■96214 / inTopicNo.17)  Re[10]: BringToFrontを最後に更新する方法
□投稿者/ 雀 (14回)-(2020/10/31(Sat) 20:03:09)
とっちゃん さん、KOZ さん

どうもありがとうございます。

KOZ さんのDeferWindowPosの方法を使うと
なんと100倍も高速化することができました。
素晴らしいです。

解決済み
引用返信 編集キー/

このトピックをツリーで一括表示


トピック内ページ移動 / << 0 >>

このトピックに書きこむ