- あぶぽん
- 大ベテラン
- 会議室デビュー日: 2005/10/20
- 投稿数: 205
|
投稿日時: 2005-10-27 10:20
皆さま、いつもお世話になっております。
件名の通り、MDIインターフェイスのアプリケーションで、
子フォームを複数開いているとき、(具体的にはクローズボタンを
押したタイミングで)最前面の子フォームを最背面に移動させたい
と考えております。
MDI親フォームのToolBarを切り替えるには?
http://www.atmarkit.co.jp/bbs/phpBB/viewtopic.php?topic=25340&forum=7&4
のスレを読んで頂いた方には分かるかと思いますが、
MDI子フォームのHide()にバグがありますので(VS2003)、
その代わりに最背面に移動するという仕様となったのです。
まずは単純にForm#Closingイベントで、Control#SendToBack()を実行しました。
子フォームは最背面に移動しましたが、
次の画面がActivatedになりません。
特にフォームを最大化している場合、薄っすらと透けたように、
後ろに隠れていたウィンドウが標準化状態で表示されます。
# これはその中から好きなウィンドウを選べるようにという仕様なの
# でしょうか???
また、明示的にControl#Focus()しようと思っても、
Zオーダーが取れないので次にどのフォームをFocus()すれば良いのか
分かりません。
因みにそのときのForm#ActiveMdiChildの値は最背面に移動したフォーム
のままです。
コーディングでZオーダーを管理すれば簡単に解決できそうですが、
本筋ではなさそうです。
.NETの流儀というか標準ではどのような方法をとるべきなのでしょうか?
# 私が根本的にやり方を間違えているのかも知れません。
コード: |
|
private void FormA_Closing(object sender,
System.ComponentModel.CancelEventArgs e)
{
e.Cancel = true;
this.SendToBack();
}
|
|
- まどか
- ぬし
- 会議室デビュー日: 2005/09/06
- 投稿数: 372
- お住まい・勤務地: ますのすし管区
|
投稿日時: 2005-10-27 14:05
まどかです。
引用: |
|
また、明示的にControl#Focus()しようと思っても、
Zオーダーが取れないので次にどのフォームをFocus()すれば良いのか
分かりません。
|
API関数GetNextWindowが使えないでしょうか?
引用: |
|
.NETの流儀というか標準ではどのような方法をとるべきなのでしょうか?
コード: |
|
private void FormA_Closing(object sender,
System.ComponentModel.CancelEventArgs e)
{
e.Cancel = true;
this.SendToBack();
}
|
|
#言われることが全般的なことか、文中でやろうとしてることについてなのかで変わりますが。。。
個人的には、「閉じようとしている」「最中」にフォーカスうんぬんというのは違和感があります。
極論を言えば、MDIのGUIとしての性質からHideにすること自体好ましくないかもしれません。
#子フォームはユーザーが自由に操作できるべき
#ということは、子フォームが自身で処理が閉じていて、そこからHideする仕様は生まれないはず
Hideによりしようとしてるのは値の保持でしょうか?他のフォームとの連携ですか?
|
- あぶぽん
- 大ベテラン
- 会議室デビュー日: 2005/10/20
- 投稿数: 205
|
投稿日時: 2005-10-28 09:55
まどかさん、ありがとうございます。
引用: |
|
引用: |
|
また、明示的にControl#Focus()しようと思っても、
Zオーダーが取れないので次にどのフォームをFocus()すれば良いのか
分かりません。
|
API関数GetNextWindowが使えないでしょうか?
|
調べてみたので報告します。
GetNextWindowですがGetWindowをGW_HWNDNEXTかGW_HWNDPREV
で呼び出すのと等価なようです。
P/Invokeという手法らしいですね。
コード: |
|
public class FormA
{
enum GetWindow_Cmd {
GW_HWNDFIRST = 0,
GW_HWNDLAST = 1,
GW_HWNDNEXT = 2,
GW_HWNDPREV = 3,
GW_OWNER = 4,
GW_CHILD = 5,
GW_ENABLEDPOPUP = 6
}
[DllImport("user32.dll")]
static extern IntPtr GetWindow(IntPtr hWnd, uint uCmd);
private void FormA_Closing(object sender,
System.ComponentModel.CancelEventArgs e)
{
// (省略)
IntPtr hwnd =GetWindow(this.Handle, GetWindow_Cmd.GW_HWNDNEXT);
// (省略)
}
}
|
引用: |
|
引用: |
|
.NETの流儀というか標準ではどのような方法をとるべきなのでしょうか?
|
#言われることが全般的なことか、文中でやろうとしてることについてなのかで変わりますが。。。
|
例えば、Hide()のバグがあるのは仕方ないとして、それを回避するために
どういった作法でアプローチするのか、
そういうインターフェイス(裏口のようなもの)が用意されているのか否か、
もうひとつはControl#SendToBack()というメソッドがあるのだから、最背面に
移動したフォームの代わりに次のフォームにフォーカスをあてるということが、
当然、考えられる訳で、そのための方法(こちらはバグではないので、できること
ならWin32APIを使用せずに)があるのかどうか、
# しかし、Control#SendToBack()をMDI子フォームを最背面に移動するために
# 使用するのが正当な方法とはどこにも書いていませんし、おそらく、
# 間違っているのだろうと思いますが。
そういうことを言いたかったのです。
引用: |
|
個人的には、「閉じようとしている」「最中」にフォーカスうんぬんというのは違和感があります。
極論を言えば、MDIのGUIとしての性質からHideにすること自体好ましくないかもしれません。
#子フォームはユーザーが自由に操作できるべき
#ということは、子フォームが自身で処理が閉じていて、そこからHideする仕様は生まれないはず
Hideによりしようとしてるのは値の保持でしょうか?他のフォームとの連携ですか?
|
Hide()にする理由は、「値の保持」と「表示の高速化」、「リソースの再利用」、
「メモリ管理をガーベジコレクタに任せたくない」、「既存システムがそういう設計なので」、
です。
また、.NETの流儀ではどうか分からないですが、少なくともJavaやVC++などでは、
ウィンドウクローズの変わりに非表示(Hide)にするのは定石だと思います。
.NETでも2005では直っているということは、
「MDI子フォームを非表示(Hide)することは推奨しない」という態度では
ないように思われますよ。
|
- じゃんぬねっと
- ぬし
- 会議室デビュー日: 2004/12/22
- 投稿数: 7811
- お住まい・勤務地: 愛知県名古屋市
|
投稿日時: 2005-10-28 10:05
引用: |
|
あぶぽんさんの書き込み (2005-10-28 09:55) より:
.NETでも2005では直っているということは、
「MDI子フォームを非表示(Hide)することは推奨しない」という態度では
ないように思われますよ。
|
もともと、値の保持は別で任せるべきだと思うんですが。
2003 よりももっと前から、分離して考える手法は取られています。
表題の件ですが、SendToBack してから何をしようと Active にはならないっぽいです。
別の MDI の話であったように、最初の MDI 子フォームを Active だと勘違いしてるっぽいです。
_________________ C# と VB.NET の入門サイト
じゃんぬねっと日誌
|
- まどか
- ぬし
- 会議室デビュー日: 2005/09/06
- 投稿数: 372
- お住まい・勤務地: ますのすし管区
|
投稿日時: 2005-10-28 12:15
で、うまくいったのでしょうか?(^^;
引用: |
|
Hide()にする理由は、「値の保持」と「表示の高速化」、「リソースの再利用」、
「メモリ管理をガーベジコレクタに任せたくない」、「既存システムがそういう設計なので」、
です。
また、.NETの流儀ではどうか分からないですが、少なくともJavaやVC++などでは、
ウィンドウクローズの変わりに非表示(Hide)にするのは定石だと思います。
|
個人的にはMDIではおっしゃる「表示の高速化」くらいしか理由が思いつきません。
で、現代のスペックではよっぽど重たい初期処理が無い限り必要性も感じません。
「そうなんだ」と言われればおしまいですが。
#Hideするなと言ってるわけではありません。
ただ、親は表示するだけ、子は自分の中で閉じた処理をするということからすれば
最初の投稿でも書いたように、
・親と子で同一コントロールインスタンスを共有する
・子にPublic変数だけれど他の子をShowする記述がある
というのはお勧めしません。
|
- あぶぽん
- 大ベテラン
- 会議室デビュー日: 2005/10/20
- 投稿数: 205
|
投稿日時: 2005-10-28 13:20
まどかさん、じゃんぬねっとさん、有り難うございます。
いくのではと思ってますが、
今回のプロジェクトではWin32APIを使うことが却下されたので、
試していません。
個人的には試してみたいと思っているのですけど。。。
引用: |
|
個人的にはMDIではおっしゃる「表示の高速化」くらいしか理由が思いつきません。
|
正直なところ、
じゃんぬねっとさんもおっしゃるように、
引用: |
|
もともと、値の保持は別で任せるべきだと思うんですが。
2003 よりももっと前から、分離して考える手法は取られています。
|
という意見に賛成なのですが、
「既存システムがそういう設計なので」という拘束が一番大きいです。
いつものことながらジレンマですね。
以下もその通りだと思います。
引用: |
|
ただ、親は表示するだけ、子は自分の中で閉じた処理をするということからすれば
最初の投稿でも書いたように、
・親と子で同一コントロールインスタンスを共有する
・子にPublic変数だけれど他の子をShowする記述がある
というのはお勧めしません。
|
それで、現在は他の方法を考えています。
とにかく、子フォームを本当にはクローズせず、ユーザには
閉じたかのように見せればいいわけなのですが。。。
|
- まどか
- ぬし
- 会議室デビュー日: 2005/09/06
- 投稿数: 372
- お住まい・勤務地: ますのすし管区
|
投稿日時: 2005-10-28 13:40
引用: |
|
とにかく、子フォームを本当にはクローズせず、ユーザには
閉じたかのように見せればいいわけなのですが。。。
|
Hideを使わないことが前提と思われるので、
MDI配下から除外して、極端な座標へ移動するとか。
#試さずに書いてます。
ただ、アプリケーション内でのフォーカス移動を殺す必要があったりしますが。
ただしVisible=Trueの場合、どのような場合もウィンドウリストやウィンドウの列挙に注意が必要ですね。
#ベストはバグのため不可能であることを納得させることですかね。。。
|
- ジブ
- 大ベテラン
- 会議室デビュー日: 2005/09/22
- 投稿数: 135
|
投稿日時: 2005-10-28 19:17
引用: |
|
今回のプロジェクトではWin32APIを使うことが却下されたので、
|
こういうことならできるかも
コード: |
|
Private WithEvents formA As formA
Private WithEvents formB As formA
Private WithEvents formC As formA
Private _MdiChildren As New System.Collections.ArrayList
Private _MdiZorder As New System.Collections.ArrayList
Private Sub Form1_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles MyBase.Load
formA = New formA
formA.MdiParent = Me
formA.Text = "A"
formA.Show()
_MdiChildren.Add(formA)
formB = New formA
formB.MdiParent = Me
formB.Text = "B"
formB.Show()
_MdiChildren.Add(formB)
formC = New formA
formC.MdiParent = Me
formC.Text = "C"
formC.Show()
_MdiChildren.Add(formC)
End Sub
Private Sub Form1_MdiChildActivate(ByVal sender As Object, ByVal e As System.EventArgs) Handles MyBase.MdiChildActivate
'自前でZorderの変更
Try
_MdiZorder.Remove(sender)
Catch ex As Exception
End Try
_MdiZorder.Insert(0, sender)
End Sub
Dim _forceClose As Boolean = False
Private Sub MdiChild_Closing(ByVal sender As Object, ByVal e As System.ComponentModel.CancelEventArgs) Handles formA.Closing, formB.Closing, formC.Closing
If _forceClose Then Exit Sub
Dim form As Form = sender
form.SendToBack()
Try
_MdiZorder.Remove(form)
Catch ex As Exception
End Try
_MdiZorder.Add(form)
_MdiZorder(0).focus()
e.Cancel = True
End Sub
Private Sub Form1_Closing(ByVal sender As Object, ByVal e As System.ComponentModel.CancelEventArgs) Handles MyBase.Closing
_forceClose = True
For Each child As Form In _MdiChildren
child.Close()
Next
e.Cancel = False
End Sub
|
|