最終更新日 :
VB Tips

ドッキングウィンドウをVBで作る

今回は「ドッキングウィンドウをVBで作る」本編です。
前回はガイダンス的なことで終わってしまいましたが、本編ではそれを踏まえてさらに深く押し進めていきます。

構文
' GetParent()/SetParent()関数
Declare Function SetParent Lib "user32" (ByVal hWndChild As Long, ByVal hWndNewParent As Long) As Long
Declare Function GetParent Lib "user32" (ByVal hwnd As Long) As Long
' ウィンドウのスタイルを設定する関数とその定数
Public Const GWL_EXSTYLE = (-20)
Public Const GWL_STYLE = (-16)
Public Const WS_BORDER = &H800000
Public Const WS_CAPTION = &HC00000 ' WS_BORDER Or WS_DLGFRAME
Public Const WS_CHILD = &H40000000
Public Const WS_DLGFRAME = &H400000
Public Const WS_THICKFRAME = &H40000
Public Const WS_VSCROLL = &H200000
Public Const WS_HSCROLL = &H100000
Public Const WS_EX_TOOLWINDOW = &H80
Public Const WS_EX_WINDOWEDGE = &H100
Public Const WS_EX_CLIENTEDGE = &H200

Declare Function GetWindowLong Lib "user32" Alias "GetWindowLongA" (ByVal hwnd As Long, ByVal nIndex As Long) As Long
Declare Function SetWindowLong Lib "user32" Alias "SetWindowLongA" (ByVal hwnd As Long, ByVal nIndex As Long, ByVal dwNewLong As Long) As Long

ドッキングウィンドウを作ってみよう
1.用意するネタ
完成予定図 Form1に、ツールバーコントロール、テキストボックスなど、それと、(実行時は非表示ですが)フレームコントロール(またはピクチャーボックス)を貼り付けます。
そして、Form2には何も貼り付けず、ウィンドウスタイル(BorderStyle)を"4 - ツールボックス固定"にします。
ツールバーのAlignプロパティは"1"にしておいても大丈夫です。成功すると、右のような状態にすることができます。「ダブルクリックすると...?」と表示されているラベルコントロールは、Alignプロパティを"1"にしたピクチャーボックスの中に貼り付けています。
この図では、ツールバーコントロールのみに対象を絞っていますが、応用すれば、VBのドッキングウィンドウのように、テキストボックスやツリービューコントロール、それらの複合体(ピクチャーボックスをコンテナにすることによってできます)をドッキングしたり切り離したりすることができます。
ただ、MFCにあるような移動中に枠を描いたり、かっこいい(?)動作をさせようとするには、残念ながらもっと修行が必要です。(きっとメッセージフックなどを使えばできるでしょう。)
2.親子関係を変える
Dim OldParent_hwnd As Long

OldParent_hwnd = SetParent(Toolbar1.hwnd, Form2.hwnd)


最初、単純にこれだけでいいと思ってました。多分、掲示板に刺激されて試された方がいれば、この段階でおかしなことに気付いたはずです。
この時、他にAlignプロパティが"1"のコントロール(上の図ではピクチャーボックス)があれば、どうなるでしょう? ピクチャーボックスの位置が変わりません。ツールバーはForm2に移ってしまっているのに、Form1ではツールバーの高さ分だけ、透明なコントロールが占有しているのです。これでは使い物になりませんね。理由は、Alignプロパティを実現する機能がWindowsに備わっていないからです。この処理をVBが独自で行っているからだと思われます。
ではむりやりAlignを解除して、親フォームを変えてみましょう。SetParent()の前に、Toolbar1.Alignを"0"に設定するだけです。どうでしょう? 「透明コントロール」は消えました。しかし、ちょっとツールバーがおかしいみたいです。Alignが0だからです。と言うことは、サイズ調整はForm2_Resize()で手動で行わないといけません。
ここでフレームコントロールの出番です。

Set Toolbar1.Container = Frame1

とすることによって、ツールバーのコンテナがフォームからフレームに移ります。つまり、Alignプロパティは一旦解除(=0)されます。しかし、この状態でSetParent()を実行すると、不思議なことに、上の図のようになるではないですか。
戻す時も簡単ですね。逆をすればいいのです。コンテナをフレームからフォームに移し、SetParent()を実行するのです。
3.注意すること
この動作をさせる時、注意しておかなければならないのは「フォーカスの移動」です。上の図で実験中に分かったことですが、ツールバー上に配置されたコントロールがアクティブの時、Form1をクリックしても、Form1はアクティブになってくれません。
Form1をクリックすることによって、Form1がアクティブになります。しかし、Form1でフォーカスを持っているのは見かけ上Form2にあるツールバーの中のコントロールです。Form1がアクティブになると、そのコントロールがアクティブになり、同時に、その親であるForm2が結果的に再びアクティブになるのです。サンプルプロジェクトで確かめてください。
Form1_Active()において、ツールバー分離時の対策について、何らかのコードを組む必要があります。上の図では、例えばText1.SetFocusとすることでこの現象は防ぐことができます。ただ、ツールバー上のコントロールがアクティブでなく、「ツールバーコントロール」がアクティブの時、この現象は起こりませんので、ツールバーにコントロールを乗せない時はこの措置は必要ありません。
4.ウィンドウのスタイルを変える
ここではツールバーに限って書いているので直接は関係ありませんが、先に書いた「フォーカスの移動」の問題を解決後(現時点で自分でも解決法が見つかりません)、複合体を分離した時にVBのドッキングウィンドウのようにタイトルバーを付けたい、とお思いの方に。
SetWindowLong()を使用します。GWL_STYLE, GWL_EXSTYLEの場合、まず、GetWindowLong()で元からあるスタイルを取得したあと、Or や Xor でスタイルを追加・削除する方法が基本です。

Dim Style As Long

Style = GetWindowLong(Form1.hwnd, GWL_STYLE)
Style = Style Xor WS_CAPTION ' タイトルバーを消す
SetWindowLong Form1.hwnd, GWL_STYLE, Style


この関数はスタイルの変更に関してあまり危険ということはないので、上の定数を使っていろいろと試してみてください。複合体のコンテナ(ピクチャーボックス)にタイトルバーを付けた後、それを親フォームの中で最大化します。そうするとVBのドッキングウィンドウのようになります。
5.メッセージフックでもっときれいに
タイトルバーを付けました。しかし、動いてしまいます。ここでメッセージフックです(Movableプロパティはないので)。
サブクラス化し、WM_NCMOUSEDOWNメッセージを横取りし、そのメッセージのパラメータにあるヒットテストコード(どこにマウスポインタがあるか)にことごとく「別の場所」の値を代入すればいいのです。
自分でもあまりやっておらず、詳しく書けませんので各個人の責任で探求していってください。その結果新発見があれば、掲示板・メールでお知らせください。 つづ・・・く?

※ この事に関しては研究した後、また別の機会に続編Tipsを載せるつもりでいます。が、載せないかもしれません。

サンプル : wdparent.lzh(VB5)
今回実験に使用したサンプルプロジェクトです。(作成 : SP3)

Copyright (C) 1999 Satoshi Tadaフィードバックはこちら
Home