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

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

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

MDI子ウインドウを最前面にする方法

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

■92263 / inTopicNo.1)  MDI子ウインドウを最前面にする方法
  
□投稿者/ むん (1回)-(2019/09/06(Fri) 09:17:59)

分類:[.NET 全般] 

VB.NETを使っております。


MDIアプリケーションを作成しており、

・ボタンを押すことで、既に表示されている子ウインドウの一つを最前面表示にする方法
・既に表示されている子ウインドウの一つを常に最前面表示にする方法


の二つを探しております。

検索すると、
https://social.msdn.microsoft.com/Forums/ja-JP/78b55c72-6a21-4483-a0dd-488466004486/mdi233761250112457125401251212434263682106938754123953492031034?forum=vbgeneralja

このページなどそれらしいページは見つかるのですが、
どのコードを使えば良いか分かりません。


どなたか、どのコードを使えば良いか提示していただけないでしょうか?


引用返信 編集キー/
■92264 / inTopicNo.2)  Re[1]: MDI子ウインドウを最前面にする方法
□投稿者/ とっちゃん (631回)-(2019/09/06(Fri) 11:40:14)
No92263 (むん さん) に返信

> ・ボタンを押すことで、既に表示されている子ウインドウの一つを最前面表示にする方法

前面に持ってくる方法は、Form.ActivateMdiChild メソッド(基本的にはIsMdiContainer = True にしたフォームのメンバーを呼び出す)で
パラメータにアクティブにしたいフォームを渡してやればうまくいくと思います。


> ・既に表示されている子ウインドウの一つを常に最前面表示にする方法

子ウィンドウ内で、最前面に常に配置は、最前面に持ってきたいフォームの TopMost プロパティを True にすれば行けるかもしれません。
こちらは試したことがないのでちょっとわからないです。

引用返信 編集キー/
■92265 / inTopicNo.3)  Re[2]: MDI子ウインドウを最前面にする方法
□投稿者/ むん (2回)-(2019/09/06(Fri) 11:53:31)
ありがとうございます。

ActivateMdiChildを試してみましたがうまくいきませんでした。
https://dobon.net/vb/dotnet/form/mdiapplication.html
このページには
親フォームから指定した子フォームをアクティブにするためのメソッドとして、Form.ActivateMdiChildメソッドがありますが、これはprotectedメンバであり、ヘルプによると、「このメンバは、.NET Framework インフラストラクチャのサポートを目的としています。
独自に作成したコード内で直接使用することはできません。」とのことです。
と書かれてあるので使えないのではないでしょうか?

topmostをTrueにする方法も既に試していますが

https://www.papy.in/bbs/vb1/200507/05070056.html

使えないと書かれてあるページが見つかりました。

どのようにすれば良いでしょうか?

引用返信 編集キー/
■92266 / inTopicNo.4)  Re[3]: MDI子ウインドウを最前面にする方法
□投稿者/ とっちゃん (632回)-(2019/09/06(Fri) 13:08:14)
No92265 (むん さん) に返信
> ありがとうございます。
>
> ActivateMdiChildを試してみましたがうまくいきませんでした。
> https://dobon.net/vb/dotnet/form/mdiapplication.html
> このページには
> 親フォームから指定した子フォームをアクティブにするためのメソッドとして、Form.ActivateMdiChildメソッドがありますが、これはprotectedメンバであり、ヘルプによると、「このメンバは、.NET Framework インフラストラクチャのサポートを目的としています。
> 独自に作成したコード内で直接使用することはできません。」とのことです。
> と書かれてあるので使えないのではないでしょうか?
>
ActivateMdiChild() メソッドがうまくいかないとすると、アクティブにしたい Mdi子フォームの、Activate() メソッドを呼び出す形でもうまくいかない気がします。

ボタンを押したら何をするのでしょう?
もしかしたらそのあたりの段取りに問題があるのかもしれません。



> topmostをTrueにする方法も既に試していますが
>
> https://www.papy.in/bbs/vb1/200507/05070056.html
>
> 使えないと書かれてあるページが見つかりました。
>
子ウィンドウは駄目なのか。残念ですがこちらは対処方法はないです。
Windows という OS の仕組みに起因する問題なので、少なくとも Windows を使っている限り対処方法はないと思います。

それでも、どうしても手前に配置したいのなら、モードレスダイアログとして表示させるくらいしかないと思います。

引用返信 編集キー/
■92269 / inTopicNo.5)  Re[4]: MDI子ウインドウを最前面にする方法
□投稿者/ 魔界の仮面弁士 (2360回)-(2019/09/06(Fri) 14:00:08)
No92266 (とっちゃん さん) に返信
>>ActivateMdiChildを試してみましたがうまくいきませんでした。
> ActivateMdiChild() メソッドがうまくいかないとすると、アクティブにしたい Mdi子フォームの、
> Activate() メソッドを呼び出す形でもうまくいかない気がします。

下記で試した限りでは、Activate の方が良いみたいです。
ActivateMdiChild だと、フォーカス遷移が期待動作しないことがあるようで。


'コントロールを何も貼ってない空の Form1
Partial Public Class Form1
  Inherits Form
  Private Child1, Child2, Child3 As Form
  Private WithEvents Button1, Button2 As Button
  Public Sub New()
    ' InitializeComponent() '←今回は実験コードなので、デザイナコードの有無はあまり関係ない

    'アクティブ化させるためのボタンを配置
    Dim pnl As New Panel() With {.Dock = DockStyle.Top}
    Button1 = New Button() With {.Text = "1", .Dock = DockStyle.Left}
    Button2 = New Button() With {.Text = "2", .Dock = DockStyle.Left}
    pnl.Controls.Add(Button2)
    pnl.Controls.Add(Button1)
    Controls.Add(pnl)

    'MDI の準備
    Me.IsMdiContainer = True
    Child1 = New Form() With {.MdiParent = Me, .Text = "Child1"}
    Child2 = New Form() With {.MdiParent = Me, .Text = "Child2"}
    Child3 = New Form() With {.MdiParent = Me, .Text = "Child3"}

    'MDI 子フォームを表示
    Child1.Show()
    Child2.Show()
    Child3.Show()
  End Sub

  Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
    ' ActivateMdiChild(Child1)
    Child1.Activate()
  End Sub

  Private Sub Button2_Click(sender As Object, e As EventArgs) Handles Button2.Click
    'ActivateMdiChild(Child2)
    Child2.Activate()
  End Sub
End Class
引用返信 編集キー/
■92271 / inTopicNo.6)  Re[5]: MDI子ウインドウを最前面にする方法
□投稿者/ とっちゃん (633回)-(2019/09/06(Fri) 14:44:23)
No92269 (魔界の仮面弁士 さん) に返信
> ■No92266 (とっちゃん さん) に返信
> >>ActivateMdiChildを試してみましたがうまくいきませんでした。
>>ActivateMdiChild() メソッドがうまくいかないとすると、アクティブにしたい Mdi子フォームの、
>>Activate() メソッドを呼び出す形でもうまくいかない気がします。
>
> 下記で試した限りでは、Activate の方が良いみたいです。
> ActivateMdiChild だと、フォーカス遷移が期待動作しないことがあるようで。
>
>
フォローありがとうございます。
なるほど。。。
Activate が内部的に呼びだしてるのかもしれませんね。
それだと、状況次第ではうまくいかないなどがありそうです。

引用返信 編集キー/
■92276 / inTopicNo.7)  Re[6]: MDI子ウインドウを最前面にする方法
□投稿者/ 魔界の仮面弁士 (2363回)-(2019/09/06(Fri) 15:39:28)
No92271 (とっちゃん さん) に返信
> なるほど。。。
> Activate が内部的に呼びだしてるのかもしれませんね。
> それだと、状況次第ではうまくいかないなどがありそうです。

Child1 = New Form() With {.MdiParent = Me, .Text = "Child1"}
Child2 = New Form() With {.MdiParent = Me, .Text = "Child2"}
Child3 = New Form() With {.MdiParent = Me, .Text = "Child3"}

のところの順番を入れ替えて

Child3 = New Form() With {.MdiParent = Me, .Text = "Child3"}
Child1 = New Form() With {.MdiParent = Me, .Text = "Child1"}
Child2 = New Form() With {.MdiParent = Me, .Text = "Child2"}

にした場合は、ActivateMdiChild でも 1 と 2 の切り替えが正しく行われました。
奇妙奇天烈摩訶不思議…



No92265 (むん さん) に返信
> 「このメンバは、.NET Framework インフラストラクチャのサポートを目的としています。
> 独自に作成したコード内で直接使用することはできません。」とのことです。

.NET Framework 2.0 以降のリファレンスには見当たらないですね。
.NET Framework 1.x 当時の記述だったかな…?
https://docs.microsoft.com/ja-jp/dotnet/api/system.windows.forms.form.activatemdichild

…と思ったら .NET のソースコードに、<internalonly/> が付与されていますね。
https://referencesource.microsoft.com/#System.Windows.Forms/winforms/Managed/System/WinForms/Form.cs,3096



> topmostをTrueにする方法も既に試していますが
> https://www.papy.in/bbs/vb1/200507/05070056.html
同ログのオリジナルはここ。
http://madia.world.coocan.jp/cgi-bin/VBBBS2/wwwlng.cgi?print+200507/05070056.txt


> どのようにすれば良いでしょうか?

MDI 子フォームとせず、親フォーム上に直接配置するという手法があります。

ただし MDI 管理からは外れるため、MdiChildren プロパティや
LayoutMdi メソッドとは連動しなくなりますし、
子フォームを最大化した場合の動作も異なることになります。

Partial Public Class Form1
  Private Child1, Child2, Child3 As Form

  Public Sub New()
    InitializeComponent()

    IsMdiContainer = True
    Child1 = New Form() With {.MdiParent = Me, .Text = "Child1"}
    Child2 = New Form() With {.MdiParent = Me, .Text = "Child2"}
    Child3 = New Form() With {.TopLevel = False, .Text = "TopMost"}
    Controls.Add(Child3)
    Child1.Show()
    Child2.Show()
    Child3.Show()

    ' Controls.OfType(Of MdiClient)().FirstOrDefault()?.SendToBack()
  End Sub
End Class
引用返信 編集キー/
■92289 / inTopicNo.8)  Re[7]: MDI子ウインドウを最前面にする方法
□投稿者/ むん (3回)-(2019/09/08(Sun) 11:19:58)
ありがとうございます。
Formをボタンで最前面化する方法はActivateでうまくいきました。

常に最前面化する方法ですが、
提示してくださった方法でいけるかと思ったのですが、
ウインドウを最小化するとどこかに
消えてしまってプログラムを再起動しないとアクセスできなくなってしまうのですが・・・
なぜでしょうか?


引用返信 編集キー/
■92293 / inTopicNo.9)  Re[8]: MDI子ウインドウを最前面にする方法
□投稿者/ 魔界の仮面弁士 (2364回)-(2019/09/08(Sun) 22:08:07)
No92289 (むん さん) に返信
> ウインドウを最小化するとどこかに
MinimizeBox と MaximizeBox を False にして運用していただくのが手っ取り早いのですけれどね…。


> 消えてしまってプログラムを再起動しないとアクセスできなくなってしまうのですが・・・

消えたというか、MdiClient コントロールの背面に居る状態なのでしょう。
先の No92276 のコントロール階層は、下記の構成になっています。

Form(Form1)
├MdiClient(IsMdiContainer による自動生成)
│├Form(Child1)
│└Form(Child2)
└Form(Child3)

MdiClient コントロールというのは、MDI 親フォームのクライアント領域に自動配置される灰色のパネルです。

MdiClient は通常、Dock プロパティが Fill の状態であるため、
Form1 のクライアント領域全てを覆っている状態となります。

今回の場合、MdiClient も Child3 も、Form1 の子コントロールとなっているため、
重なり合っている場合は、より手前にある方が見えた状態となります。



★案1:最小化された瞬間、MDIClient を最背面に移動しなおす。

Partial Public Class Form1
  Private Child1, Child2, Child3 As Form

  Public Sub New()
    InitializeComponent()

    IsMdiContainer = True
    Child1 = New Form() With {.MdiParent = Me, .Text = "Child1"}
    Child2 = New Form() With {.MdiParent = Me, .Text = "Child2"}

    Child3 = New Form() With {.TopLevel = False, .Text = "TopMost"}
    AddHandler Child3.SizeChanged, AddressOf TopMostChildWindowStateChanged
    Controls.Add(Child3)

    Child1.Show()
    Child2.Show()
    Child3.Show()
  End Sub

  Private Sub TopMostChildWindowStateChanged(sender As Object, e As EventArgs)
    Static IsBusy As Boolean = False
    If Not IsBusy Then
      IsBusy = True
      If DirectCast(sender, Form).WindowState = FormWindowState.Minimized Then
        Me.Controls.OfType(Of MdiClient)().FirstOrDefault()?.SendToBack()
      End If
      IsBusy = False
    End If
  End Sub
End Class

欠点:
 Child3 は「MDI 子フォーム」では無く「子コントロール」であるため、Chlld1/Child2 を最小化したときに
 アイコンが重なってしまう。また、最大化時の振る舞いが通常の MDI 子フォームとは異なる。



★案2:通常時は「子コントロール」、最小化時は「MDI 子フォーム」として振舞うようにする。

Partial Public Class Form1
  Private Child1, Child2, Child3 As Form

  Public Sub New()
    InitializeComponent()

    IsMdiContainer = True
    Child1 = New Form() With {.MdiParent = Me, .Text = "Child1"}
    Child2 = New Form() With {.MdiParent = Me, .Text = "Child2"}

    Child3 = New Form() With {.TopLevel = False, .Text = "TopMost"}
    AddHandler Child3.SizeChanged, AddressOf TopMostChildWindowStateChanged
    Controls.Add(Child3)

    Child1.Show()
    Child2.Show()
    Child3.Show()
  End Sub

  Private Sub TopMostChildWindowStateChanged(sender As Object, e As EventArgs)
    Static IsBusy As Boolean = False
    If Not IsBusy Then
      IsBusy = True
      Dim f = DirectCast(sender, Form)

      'If f.WindowState <> FormWindowState.Minimized Then
      If f.WindowState = FormWindowState.Normal Then
        If f.MdiParent IsNot Nothing Then
          f.MdiParent = Nothing
          f.TopLevel = False
          f.Parent = Me
        End If
      Else
        f.Visible = False
        f.MdiParent = Me
        f.Visible = True
      End If
      IsBusy = False
    End If
  End Sub
End Class

欠点:
 「子コントロール」と「MDI 子フォーム」の間で切り替えるたびに、ウィンドウハンドルが再生成される。
 また、最大化」時の連携が不完全。
引用返信 編集キー/

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


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

このトピックに書きこむ