DOBON.NET DOBON.NETプログラミング掲示板過去ログ

画面の最前面にメッセージを表示させる方法

環境/言語:[Windows98, 2000,XP VB.NET(VS2002)]
分類:[.NET]

こんにちは 平ちゃんです。
いつも有難うございます。すみませんがまたアドバイスをお願いします。

画面の最前面にメッセージを表示させる方法です。

現状はメッセージボックスでユーザーに注意を促しています。
この場合、メッセージボックスが最前面に表示されなければ、操作不可能になり色々と面倒です。

アプリのイメージとして
@ 起動すると基本フォームを表示
A 基本フォームからユーザが必要ならヘルプ画面を表示できる。ヘルプ画面は常に最前面 (TopMost = True) 表示。
  (ヘルプ画面を見ながら実際の操作が出来るように作っています)
B 基本フォームから別の操作フォームを新たに起動する。基本フォームのボタンを操作する必要があるので、モーダレスとし、
  基本フォームに所有させて、基本フォームの前面に重ねて表示します。
C ヘルプ画面が表示されている場合に操作フォームを起動してもヘルプ画面は最前面表示。


このような場合に入力ミスなどでメッセージボックスを表示した場合、ヘルプ画面の背後になると、操作不可能となります。

これを防ぐためにメッセージボックスを
MessageBox.Show メソッド (String, String, MessageBoxButtons, MessageBoxIcon, MessageBoxDefaultButton, MessageBoxOptions)
で MessageBoxOptions = DefaultDesktopOnly で使っています。

ところがテストしてみると
xp, 2000 では問題ないのですが、98 の場合 ヘルプ画面の背後になり不具合です。


そこで教えて頂きたいのは
ヘルプ画面、メッセージボックスをどの様にしたらよいかという案です。

私が考えている案は、メッセージボックスをメッセージ用のフォーム(TopMost = True)に置き換えるという方法です。

MessageBox.Show メソッド (IWin32Window, String) でもいいのですが、常にIWin32Window を管理する必要があり面倒です。

(なおタスクバーには操作フォームは表示しないようにしています)
こんにちは、じゃんぬねっと です。

■No9544に返信(平ちゃんさんの記事)
> 基本フォームから別の操作フォームを新たに起動する。

この「別の操作フォーム」は単一ですか、複数ありえますか?
それによって、ちょっと実装が変わります。

それと、TopMost はあまりお勧めしません。
そのアプリケーション外のすべてのウィンドウより最善面に表示されてしまうことが問題です。
特に今回のような「ヘルプ画面」だと、他のアプリケーションに "ずっと" 干渉してしまう状況にありますので、宜しくないです。
2005/03/04(Fri) 00:05:37 編集(投稿者)

■No9546に返信(じゃんぬねっとさんの記事)


早速のご返信有難うございます。

> この「別の操作フォーム」は単一ですか、複数ありえますか?
> それによって、ちょっと実装が変わります。

多少長くなりますが、宜しくお願いします。(申し訳ありません)

操作画面は3画面(A、B、C)で
Aはラジオボックスだけで、操作する内容を選択し、次へボタンでAを非表示にし、子操作画面(実質的な操作画面)を起動します。
  子操作画面は6種類ですが、同時には表示しません。これから先の画面もありません。
  子操作画面から基本フォームのボタンを操作します。従って子操作画面はモーダレスです。
  子操作画面から戻る時はAを表示し、別のラジオボックスを選び次へで別の子操作画面が起動されます。(以下同様)

Bから1画面呼ばれます。Bから呼ばれる子操作画面はモーダル表示です。これから先への表示はありません。
  Bは基本フォームのボタンを操作します。

Cは簡単なモーダル画面でこれから基本フォームのボタンの操作はありません。(4種類)

A、B、C、Aの子操作画面、Bの子操作画面からメッセージボックスを出しています。

ヘルプ画面はこれら全ての画面の最前面になります。
ヘルプ画面は画面全体の約1/9位で見ながら操作でき、邪魔なら移動でき、最小化もできます。


>
> それと、TopMost はあまりお勧めしません。

そうなんです。実は始めは操作画面等も TopMost でやっていたのですが、嫌になりやめました。
ヘルプ画面 だけは諸事情で操作画面以前に起動しますので仕方なくTopMost を使っています。

またこのヘルプ画面以外に通常の画面一杯のヘルプ画面もありこちらは、プロセスで起動しています。
(当然ですがここからメッセージボックスはでません)


> そのアプリケーション外のすべてのウィンドウより最善面に表示されてしまうことが問題です。
> 特に今回のような「ヘルプ画面」だと、他のアプリケーションに "ずっと" 干渉してしまう状況にありますので、宜しくないです。

この問題のためタスクバーにヘルプ画面を表示し最小化できるようにしました。

#申し訳ありませんが何卒良い知恵をお貸し下さい。
■No9548に返信(平ちゃんさんの記事)

お早うございます。
自己レスです。

じゃんぬねっとさんのご指摘にありましたように ヘルプ画面の TopMost をやめる方向も 案の1つとさせて頂きます。
確かに これが 悪さをしています。

参考にしたいので皆様方のメッセージの方法を是非教えて下さい。
おはようございます、じゃんぬねっと です。

■No9554に返信(平ちゃんさんの記事)
案その 1 (下策)

オーナーのオーナーを利用する。
1 方のフォームが閉じる時、開く時にオーナーをすり替える方法。
サブフォームが、単一であるのならば、そこそこ使えますが難点が多々あります。

フォームが増えると似たような実装が必要となる。
フォームの型を毎回判断せねばならないため、拡張性には長けていない。

FormMain:------------------------------------------------

Friend Class FormMain
    Inherits System.Windows.Forms.Form
' _______________________
'| Windows フォーム デザイナで生成されたコード |
'  ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄
    '/ プライベート メンバ
    Private hFormSub  As FormSub
    Private hFormHelp As FormHelp

    '/ サブ フォームを開く
    Private Sub Button1_Click(...) Handles Button1.Click
        hFormSub = New FormSub()
        Me.AddOwnedForm(hFormSub)
        hFormSub.Show()
    End Sub

    '/ ヘルプ フォームを開く
    Private Sub Button2_Click(...) Handles Button2.Click
        hFormHelp = New FormHelp()

        If hFormSub Is Nothing OrElse hFormSub.IsDisposed Then
            Me.AddOwnedForm(hFormHelp)
        Else
            hFormSub.AddOwnedForm(hFormHelp)
        End if

        hFormHelp.Show()
    End Sub

    '/ メッセージ ボックスの表示
    Private Sub Button3_Click(...) Handles Button3.Click
        MessageBox.Show("メッセージ ボックスですが、何か?")
    End Sub

End Class

FormSub:-------------------------------------------------

Friend Class FormSub
    Inherits System.Windows.Forms.Form
' _______________________
'| Windows フォーム デザイナで生成されたコード |
'  ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄
    '/ Load 時に FormHelp の存在している場合は所有をすり替え
    Private Sub FormSub_Load(...) Handles MyBase.Load
        If TypeOf Me.Owner Is FormMain Then
            For Each hForm As Form In Me.Owner.OwnedForms
                If TypeOf hForm Is FormHelp Then
                    Me.AddOwnedForm(hForm)
                End If
            Next hForm
        End If
    End Sub

    '/ Close 時に HelpForm が存在している場合は所有をすり替え
    Private Sub FormSub_Closed(...) Handles MyBase.Closed
        For Each hForm As Form In Me.OwnedForms
            If TypeOf hForm Is FormHelp Then
                If TypeOf Me.Owner Is FormMain Then
                    Dim hOwnerForm As Form = Me.Owner
                    Me.Owner = Nothing
                    hForm.Owner = hOwnerForm
                End If
            End If
        Next hForm
    End Sub
End Class
2005/03/04(Fri) 10:57:55 編集(投稿者)

■No9556に返信(じゃんぬねっとさんの記事)


じゃんぬねっとさん 早速のご回答有難うございます。

じゃんぬねっとさん が言われるとおり ヘルプ画面のTopMostを止める案が最有力候補です。



コードまで書いて頂いて恐縮です。

> ■No9554に返信(平ちゃんさんの記事)
> 案その 1 (下策)
>
> オーナーのオーナーを利用する。
> 1 方のフォームが閉じる時、開く時にオーナーをすり替える方法。

ヘルプ画面のTopMostを止める為にこの案で検討していました。


> サブフォームが、単一であるのならば、そこそこ使えますが難点が多々あります。
>
> フォームが増えると似たような実装が必要となる。
> フォームの型を毎回判断せねばならないため、拡張性には長けていない。
>

これをなるべく楽にする為に次のように考えています。
ヘルプ画面表示中フラグをFriend Shared で定義する。
Friend Shared スタック変数 を定義し これに起動したサブフォームをPush し サブフォーム終了時に Pop する。
これらを利用してオーナーをすり替える。

上記内容を Friend Shared メソッド として全てのサブフォームLoad時及びClose時に呼ぶ。

#初めての試みなのでこれが可能かどうかは今からコードを書いてみます。

#参考コード流用させて下さい。
#下策があれば上策がありますね。?

申し訳ありません、引き続き宜しくお願いします。
こんにちは、じゃんぬねっと です。

■No9567に返信(平ちゃんさんの記事)
> じゃんぬねっとさんが言われるとおり、ヘルプ画面のTopMostを止める案が最有力候補です。

これは、今回のメッセージ ボックスの件を抜きに考えてください。
他のアプリケーションに干渉することは、宜しくないですからね。

> コードまで書いて頂いて恐縮です。

いえいえ、テストしていないソースなので心配でしたが大丈夫だったようですね... (^-^;)

> これをなるべく楽にする為に次のように考えています。
> ヘルプ画面表示中フラグをFriend Shared で定義する。
> Friend Shared スタック変数 を定義し これに起動したサブフォームをPush し サブフォーム終了時に Pop する。
> これらを利用してオーナーをすり替える。

楽にはなるかもしれませんが、拡張性は乏しいですよね。
お互いのフォームがお互いを干渉しちゃってます。
本来、オーナという考えからすると双方向ではなく片方向にすべきですよね?

> #参考コード流用させて下さい。
> #下策があれば上策がありますね。?

可変のオーナーを管理したコンテナ クラスを書くと良いでしょう。
プロパティに、フォーム間の ZOrder レベルを持たせてあげるとか。
■No9568に返信(じゃんぬねっとさんの記事)


じゃんぬねっとさん お世話様です。

次のように書きまして、xp、2000、98 でも大丈夫です。


Friend Shared Mestack As New Stack() '現在の最前面フォーム
'使い方フォームの表示
Friend WithEvents FormUse As FmUsing 'ヘルプ画面のインスタンス変数


'使い方フォームのオーナー使用のすり替え
'MeStack のフォームにオーナーを変更
'このメソッドをフォームが開かれる時呼び出すこと(Loadイベント)
Friend Sub HelpForm_SubFormLoad(ByVal fm As Form)
'現在の最前面フォームの収得
Mestack.Push(fm)
If Not (FormUse Is Nothing) Then
'使い方ヘルプが起動しているのでオーナーのすり替えをする
'以前のオーナーを無効にする
Dim oldownew As Form = FormUse.Owner
oldownew.RemoveOwnedForm(FormUse)
'新しいオーナーに設定
fm.AddOwnedForm(FormUse)
End If
End Sub


'このメソッドをフォームが閉じられる時呼び出すこと(Closeイベント)
Friend Sub HelpForm_SubFormClose()
'このフォームのオーナーを呼び出す
Mestack.Pop()
Dim fm As Form = DirectCast(Mestack.Peek, Form)
If Not (FormUse Is Nothing) Then
'使い方ヘルプが起動しているのでオーナーのすり替えをする
'以前のオーナーを無効にする
Dim oldownew As Form = FormUse.Owner
oldownew.RemoveOwnedForm(FormUse)
'新しいオーナーに設定
fm.AddOwnedForm(FormUse)
End If
End Sub

#何分未熟者ゆえFreind 変数を使いまくっています。
#せっかくじゃんぬねっとさんがprivate で書いてくれてるんですが ここだけ頑張ってもという事で、次の課題とします。

沢山お聞きしたいことがありますが、最終に回すとしてとりあえず1点お願いします。

じゃんぬねっとさんのコードで
hForm.Owner = hOwnerForm とした場合
変更前のオーナーの所有リストから削除されているのですか?

良く解らなかったので取り合えず
oldownew.RemoveOwnedForm(FormUse)  ' <−−−
'新しいオーナーに設定
fm.AddOwnedForm(FormUse)
のように対としています。
こんにちは、じゃんぬねっと です。

■No9588に返信(平ちゃんさんの記事)
> じゃんぬねっとさんのコードで
> hForm.Owner = hOwnerForm とした場合
> 変更前のオーナーの所有リストから削除されているのですか?

RemoveOwnedForm([Form]) と、[Form].Owner = Nothing は同じですよね。
面倒くさがらずに統一するべきでしたね。(^^)
■No9594に返信(じゃんぬねっとさんの記事)

こんばんは 平ちゃんです。

じゃんぬねっとさん お世話様です。
お蔭様で修正する箇所も少なくてすみ、且つ不具合も解消されました。


> お互いのフォームがお互いを干渉しちゃってます。
> 本来、オーナという考えからすると双方向ではなく片方向にすべきですよね?
>
> 可変のオーナーを管理したコンテナ クラスを書くと良いでしょう。
> プロパティに、フォーム間の ZOrder レベルを持たせてあげるとか。

この辺りをお聞きしたかったのですが、内容がまとまらないので、私の今後の課題とします。

ありがとうございました。
今後とも宜しくお願いします。
解決済み!

DOBON.NET | プログラミング道 | プログラミング掲示板