visual studio 2008 express edition
C#3.5
を使用しています。

visual studioのソリューションエクスプローラーのような配置の仕方を真似したいと思っています。
どうやらSplitContainer(境界線を移動できるので)を使ってそうなんですが、
ソリューションエクスプローラー自体は1つのウィンドウのようで
ダブルクリックでSplitContainerの左側に装着したり、離れたりする仕組みのようです。
でもプログラムでウィンドウをSplitContainerに装着するやり方が分かりません・・・

this.splitContainer1.Panel1.Controls.Add(/* ここに new SolutionForm() としたら当然ですがエラーになりました */);

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

ソリューションエクスプローラーが離れる
http://ranobe.com/up/src/up360865.jpg
SplitContainerの左側に装着に装着
http://ranobe.com/up/src/up360864.jpg

A 回答 (4件)

 こんばんは。

すんません、当方の勉強不足でした。
 その後、調査をしたのですが、スプリッタコンテナのパネルにフォームを追加する事が出来るとの事実を突き止めました。
 修正をした事で、win32apiは綺麗さっぱりとなくなりました。

public partial class Form1 : Form
{
private Form2 toolWindow;

public Form1()
{
InitializeComponent();
}

private void Form1_Load(object sender, EventArgs e)
{
this.toolWindow = new Form2();
this.toolWindow.Owner = this;
this.toolWindow.Tag = this;
this.toolWindow.Location = new Point(0, 0);
this.toolWindow.Size = this.splitContainer1.Panel1.Size;
this.toolWindow.Show();
}

//スプリッタコンテナのリサイズ枠
private void splitContainer1_SplitterMoved(object sender, SplitterEventArgs e)
{
//ウィンドウが着地しているのでリサイズをさせる
int index = this.splitContainer1.Panel1.Controls.IndexOf(this.toolWindow);
if (index != -1)
this.toolWindow.Size = this.splitContainer1.Panel1.ClientSize;
}

public void PostDocking(bool bDocking)
{
this.toolWindow.SuspendLayout();
this.toolWindow.Hide();
//着地させる
if (bDocking)
{
//以下をfalseにしないと例外が飛ぶ
this.toolWindow.TopLevel = false;
this.splitContainer1.Panel1.Controls.Add(this.toolWindow);
this.toolWindow.Location = this.splitContainer1.Panel1.Bounds.Location;
this.toolWindow.Size = this.splitContainer1.Panel1.ClientSize;
this.toolWindow.FormBorderStyle = FormBorderStyle.FixedToolWindow;
}
//宙に浮かせる
else
{
this.toolWindow.FormBorderStyle = FormBorderStyle.SizableToolWindow;
this.toolWindow.Location = this.PointToScreen(this.splitContainer1.Panel1.Location);
this.splitContainer1.Panel1.Controls.Remove(this.toolWindow);
this.toolWindow.TopLevel = true;
}
this.toolWindow.Show();
this.toolWindow.ResumeLayout(true);
}
}

public partial class Form2 : Form
{
public const int WM_NCMOUSEMOVE = 0xA0;
public const int WM_NCLBUTTONDOWN = 0xA1;
public const int WM_NCLBUTTONUP = 0xA2;
public const int WM_NCLBUTTONDBLCLK = 0xA3;
public const int WM_MOUSEMOVE = 0x200;
public const int WM_LBUTTONUP = 0x202;

public const int ST_DOCKING = 0;//着地
public const int ST_NORMAL = 1;//宙刷り
public const int ST_DRAGGING = 2;//ドラッグしている(着地の状態からでないとこのフラグにはならない)

private Point ptDrag;
private int state;

public Form2()
{
InitializeComponent();
}

private void Form2_Load(object sender, EventArgs e)
{
this.state = ST_NORMAL;
}

//ウィンドウプロシージャのオーバーライド
protected override void WndProc(ref Message m)
{
switch (m.Msg)
{
case WM_MOUSEMOVE:
//ドラッグしていないのでこれ以上進めない
if (this.state != ST_DRAGGING)
break;

//マウスに合わせてフォームを動かす
this.Location = new Point(Cursor.Position.X - (this.Width / 2), Cursor.Position.Y);

return;

case WM_LBUTTONUP:
//ドラッグ移動から復帰
if (this.state == ST_DRAGGING)
{
//キャプチャを外す
this.Capture = false;

//宙に浮いている
this.state = ST_NORMAL;
}
break;

case WM_NCMOUSEMOVE:
//ドラッグしていないので、処理出来ない
if (this.state != ST_DRAGGING)
break;

//さっき摘んだ座標から、現在位置X,Yの何れが5ピクセル以上動いた
if (Math.Abs(this.ptDrag.X - Cursor.Position.X) > 4 ||
Math.Abs(this.ptDrag.Y - Cursor.Position.Y) > 4)
{

//フォームの宙刷りを実際に処理をする
Form1 form = (Form1)this.Tag;
form.PostDocking(false);

//キャプチャをかける
this.Capture = true;

return;
}
break;

case WM_NCLBUTTONDOWN:
//着地していないのでドラッグ出来ない
if(this.state != ST_DOCKING)
break;

//現在のマウス位置を記憶
this.ptDrag = Cursor.Position;

//ドラッグ開始
this.state = ST_DRAGGING;
return;

case WM_NCLBUTTONUP:
//ドラッグ移動が成立しないでボタンが離された
if (this.state == ST_DRAGGING)
//着地になる
this.state = ST_DOCKING;

break;

case WM_NCLBUTTONDBLCLK:
{
//着地していれば宙に浮く
if (this.state == ST_DOCKING)
this.state = ST_NORMAL;
//宙に浮いているので着地する
else
this.state = ST_DOCKING;

//フォームの離脱着を実際に処理をする
Form1 form1 = (Form1)this.Tag;
form1.PostDocking(this.state == ST_DOCKING);
}
return;
}
//システムに処理させる
base.WndProc(ref m);
}
}
    • good
    • 0
この回答へのお礼

ご返答ありがとうございます。
なるほど、TopLevelを使えば良かったんですね。
ちょっと自分なりにまだ分からないところがあって調べてたんですが、
visual studioの方に完璧に似せる(その必要があるかは疑問ですけど、とりあえず興味深いので)には
まだ疑問なところがいくつかあります。
例えば、上下左右真ん中に現れるアイコンもその一つです
http://ranobe.com/up/src/up361075.jpg
ですが、本題から離れてしまうのとキリがないのと作るのに時間がかかりそうなので
とりあえず締め切りたいと思います。
ここまで作り上げたmachongolaさんに感謝したいのと
まだ自分としては完成させたいので、もしよろしければ次回質問の時にアドバイス頂ければと思います。
ありがとうございました。

お礼日時:2009/05/23 02:45

 こんにちは。

御礼頂きました。

・this.splitContainer1.Panel1にForm2を追加するやり方がわからなくて先に進めません・・・
 Formを追加する事は出来ないと思います。その他、FormのOwnerにコントロールを指定する事も出来ない筈です。
 当方もC++/CLIの方で、其処に行き詰まりました。結局の所、Win32APIのSetParent()を使用するしかありませんでした。
 そう言った理由で、Win32APIを呼び出しています(正攻法では上手くいかないとは、この事です)。
 SetParent()で、スプリッタコンテナのパネル1を宙刷りウィンドウの親に設定しています。こんな感じでしょうか(これ以外は思い浮かびません・・・)。参考資料程度で。

public partial class Form1 : Form
{
public const int SWP_NOSIZE = 1;
public const int SWP_NOZORDER = 4;

[DllImport("user32.dll")]
public static extern int SetWindowPos(IntPtr hWnd, IntPtr hWndInsertAfter, int X, int Y, int cx, int cy, UInt32 uFlags);

[DllImport("user32.dll")]
public static extern IntPtr SetParent(IntPtr hWndChild, IntPtr hWndParent);

[DllImport("user32.dll")]
public static extern IntPtr SetCapture(IntPtr hWndCapture);

[DllImport("user32.dll")]
public static extern int ReleaseCapture();

private Form2 toolWindow;

public Form1()
{
InitializeComponent();
}

private void Form1_Load(object sender, EventArgs e)
{
this.toolWindow = new Form2();
this.toolWindow.Owner = this;
this.toolWindow.Tag = this;
this.toolWindow.Location = new Point(0, 0);
this.toolWindow.Size = this.splitContainer1.Panel1.Size;
this.toolWindow.Show();
}

//スプリッタコンテナのリサイズ枠が伸縮したら
private void splitContainer1_SplitterMoved(object sender, SplitterEventArgs e)
{
//ウィンドウが着地しているのでリサイズをさせる
if (this.toolWindow.IsDocking)
this.toolWindow.Size = this.splitContainer1.Panel1.ClientSize;
}

public void PostDocking(bool bDocking)
{
this.toolWindow.SuspendLayout();
//着地させる
if (bDocking == true)
{
this.toolWindow.Hide();
SetParent(this.toolWindow.Handle, this.splitContainer1.Panel1.Handle);
this.toolWindow.FormBorderStyle = FormBorderStyle.FixedToolWindow;
this.toolWindow.Location = this.splitContainer1.Panel1.Bounds.Location;
this.toolWindow.Size = this.splitContainer1.Panel1.ClientSize;
this.toolWindow.Show();
}
//宙に浮かせる
else
{
this.toolWindow.Hide();
SetParent(this.toolWindow.Handle, IntPtr.Zero);
this.toolWindow.FormBorderStyle = FormBorderStyle.SizableToolWindow;
//スプリッタのスクリーン座標を取る
this.toolWindow.Location = this.PointToScreen(this.splitContainer1.Panel1.Location);
this.toolWindow.Show();
}
this.toolWindow.ResumeLayout(true);
}
}

public partial class Form2 : Form
{
public const int WM_NCMOUSEMOVE = 0xA0;
public const int WM_NCLBUTTONDOWN = 0xA1;
public const int WM_NCLBUTTONUP = 0xA2;
public const int WM_NCLBUTTONDBLCLK = 0xA3;
public const int WM_MOUSEMOVE = 0x200;
public const int WM_LBUTTONUP = 0x202;

public const int ST_DOCKING = 0;//着地
public const int ST_NORMAL = 1;//宙刷り
public const int ST_DRAGGING = 2;//ドラッグしている(着地の状態からでないとこのフラグにはならない)

private Point ptDrag;
private int state;

public Form2()
{
InitializeComponent();
}

private void Form2_Load(object sender, EventArgs e)
{
this.state = ST_NORMAL;
}

//ウィンドウプロシージャのオーバーライド
protected override void WndProc(ref Message m)
{
switch (m.Msg)
{
case WM_MOUSEMOVE:
//ドラッグしていないのでこれ以上進めない
if (this.state != ST_DRAGGING)
break;

//マウスに合わせてフォームを動かす
Form1.SetWindowPos(this.Handle, IntPtr.Zero,
Cursor.Position.X - (this.Width / 2), Cursor.Position.Y, 0, 0,
Form1.SWP_NOZORDER | Form1.SWP_NOSIZE);
return;

case WM_LBUTTONUP:
//ドラッグ移動から復帰
if (this.state == ST_DRAGGING)
{
//キャプチャを外す
this.Capture = false;

//宙に浮いている
this.state = ST_NORMAL;
}
break;

case WM_NCMOUSEMOVE:
//ドラッグしていないので、処理出来ない
if (this.state != ST_DRAGGING)
break;

//さっき摘んだ座標から、現在位置X,Yの何れが5ピクセル以上動いた
if (Math.Abs(this.ptDrag.X - Cursor.Position.X) > 4 ||
Math.Abs(this.ptDrag.Y - Cursor.Position.Y) > 4)
{

//フォームの宙刷りを実際に処理をする
Form1 form = (Form1)this.Tag;
form.PostDocking(false);

//キャプチャをかける
this.Capture = true;

return;
}
break;

case WM_NCLBUTTONDOWN:
//着地していないのでドラッグ出来ない
if(this.state != ST_DOCKING)
break;

//現在のマウス位置を記憶
this.ptDrag = Cursor.Position;

//ドラッグ開始
this.state = ST_DRAGGING;
return;

case WM_NCLBUTTONUP:
//ドラッグ移動が成立しないでボタンが離された
if (this.state == ST_DRAGGING)
//着地になる
this.state = ST_DOCKING;

break;

case WM_NCLBUTTONDBLCLK:
{
//着地していれば宙に浮く
if (this.state == ST_DOCKING)
this.state = ST_NORMAL;
//宙に浮いているので着地する
else
this.state = ST_DOCKING;

//フォームの離脱着を実際に処理をする
Form1 form1 = (Form1)this.Tag;
form1.PostDocking(this.state == ST_DOCKING);
}
return;
}
//システムに処理させる
base.WndProc(ref m);
}

//着地しているかどうかを示すプロパティ
public bool IsDocking
{
get { return this.state != ST_NORMAL; }
}
}
    • good
    • 0

 こんばんは。

御礼頂きました。
・visual studioの方は移動させようとする瞬間に親ウィンドウからソリューションエクスプローラーを切り離しているのでしょうか?
 VisualStudioをじっくりと観察してみましたが、着地している状態から、キャプションバーを摘んで一定量動かした瞬間、宙に浮かせ、更にキャプチャをかけて、マウスの動きに合わせて移動している見たいですので、其の通りだと思います。
 まあ、フローティング系のウィンドウは兎に角、面倒で、ややこしく成りましたが、一応そんな感じになりました。
 ダブルクリックで、着地⇔宙刷り
 着地状態から、マウスで摘んで、一定量動くと、宙刷りになってマウスと一緒に移動します。
 参考資料程度で。

//メインウィンドウ
public partial class Form1 : Form
{
public const int SWP_NOSIZE = 1;
public const int SWP_NOZORDER = 4;

[DllImport("user32.dll")]
public static extern int SetWindowPos(IntPtr hWnd, IntPtr hWndInsertAfter, int X, int Y, int cx, int cy, UInt32 uFlags);

[DllImport("user32.dll")]
public static extern IntPtr SetParent(IntPtr hWndChild, IntPtr hWndParent);

[DllImport("user32.dll")]
public static extern IntPtr SetCapture(IntPtr hWndCapture);

[DllImport("user32.dll")]
public static extern int ReleaseCapture();

private Form2 toolWindow;

public Form1()
{
InitializeComponent();
}

private void Form1_Load(object sender, EventArgs e)
{
this.toolWindow = new Form2();
this.toolWindow.Owner = this;
this.toolWindow.Tag = this;
this.toolWindow.Location = new Point(0, 0);
this.toolWindow.Size = this.splitter1.Size;
this.toolWindow.Show();
}

public void PostDocking(bool bDocking)
{
this.toolWindow.SuspendLayout();
//着地させる
if (bDocking == true)
{
this.toolWindow.Hide();
SetParent(this.toolWindow.Handle, this.splitter1.Handle);
this.toolWindow.FormBorderStyle = FormBorderStyle.FixedToolWindow;
SetWindowPos(this.toolWindow.Handle, IntPtr.Zero, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOZORDER);
this.toolWindow.Show();
}
//宙に浮かせる
else
{
this.toolWindow.Hide();
SetParent(this.toolWindow.Handle, IntPtr.Zero);
this.toolWindow.FormBorderStyle = FormBorderStyle.SizableToolWindow;
//スプリッタのスクリーン座標を取る
Point ptScreen = this.PointToScreen(this.splitter1.Location);
SetWindowPos(this.toolWindow.Handle, IntPtr.Zero, ptScreen.X, ptScreen.Y, 0, 0, SWP_NOSIZE | SWP_NOZORDER);
this.toolWindow.Show();
}
this.toolWindow.ResumeLayout(true);
}
}

//宙刷り又は着地のウィンドウ
public partial class Form2 : Form
{
public const int WM_NCMOUSEMOVE = 0xA0;
public const int WM_NCLBUTTONDOWN = 0xA1;
public const int WM_NCLBUTTONUP = 0xA2;
public const int WM_NCLBUTTONDBLCLK = 0xA3;
public const int WM_MOUSEMOVE = 0x200;
public const int WM_LBUTTONUP = 0x202;

public const int ST_DOCKING = 0;//着地
public const int ST_NORMAL = 1;//宙刷り
public const int ST_DRAGGING = 2;//ドラッグしている(着地の状態からでないとこのフラグにはならない)

private Point ptDrag;
private int state;

public Form2()
{
InitializeComponent();
}

private void Form2_Load(object sender, EventArgs e)
{
this.state = ST_NORMAL;
}

//ウィンドウプロシージャのオーバーライド
protected override void WndProc(ref Message m)
{
switch (m.Msg)
{
case WM_MOUSEMOVE:
//ドラッグしていないのでこれ以上進めない
if (this.state != ST_DRAGGING)
break;

//マウスに合わせてフォームを動かす
Form1.SetWindowPos(this.Handle, IntPtr.Zero,
Cursor.Position.X - (this.Width / 2), Cursor.Position.Y, 0, 0,
Form1.SWP_NOZORDER | Form1.SWP_NOSIZE);
return;

case WM_LBUTTONUP:
//ドラッグ移動から復帰
if (this.state == ST_DRAGGING)
{
//キャプチャを外す
this.Capture = false;

//宙に浮いている
this.state = ST_NORMAL;
}
break;

case WM_NCMOUSEMOVE:
//ドラッグしていないので、処理出来ない
if (this.state != ST_DRAGGING)
break;

//さっき摘んだ座標から、現在位置X,Yの何れが5ピクセル以上動いた
if (Math.Abs(this.ptDrag.X - Cursor.Position.X) > 4 ||
Math.Abs(this.ptDrag.Y - Cursor.Position.Y) > 4)
{

//フォームの宙刷りを実際に処理をする
Form1 form = (Form1)this.Tag;
form.PostDocking(false);

//キャプチャをかける
this.Capture = true;

return;
}
break;

case WM_NCLBUTTONDOWN:
//着地していないのでドラッグ出来ない
if(this.state != ST_DOCKING)
break;

//現在のマウス位置を記憶
this.ptDrag = Cursor.Position;

//ドラッグ開始
this.state = ST_DRAGGING;
return;

case WM_NCLBUTTONUP:
//ドラッグ移動が成立しないでボタンが離された
if (this.state == ST_DRAGGING)
//着地になる
this.state = ST_DOCKING;

break;

case WM_NCLBUTTONDBLCLK:
{
//着地していれば宙に浮く
if (this.state == ST_DOCKING)
this.state = ST_NORMAL;
//宙に浮いているので着地する
else
this.state = ST_DOCKING;

//フォームの離脱着を実際に処理をする
Form1 form1 = (Form1)this.Tag;
form1.PostDocking(this.state == ST_DOCKING);
}
return;
}
//システムに処理させる
base.WndProc(ref m);
}
}
    • good
    • 0
この回答へのお礼

ご返答ありがとうございます。
試してみましたが、ほぼ理想的な動作になったのですが、
visual studioの方と比較すると、
Form2はsplitter1のサイズに合わせるというより
splitter1に横のスクロールバーが現れてるようです。
境界線を広げてForm2の横幅を超えると、
Form2自体が横に大きくなっているのではないかと考えられます。

(クリックしたまま境界線を右に移動してる最中です)
http://ranobe.com/up/src/up361300.jpg

そこでsplitter1で実行時に境界線を動かすことができなかったので、
splitContainer1に書き換えて試そうとしたのですが、
初めの疑問に戻って申し訳ないのですが、
this.splitContainer1.Panel1にForm2を追加するやり方がわからなくて先に進めません・・・
一応
this.splitContainer1.Panel1.Controls.Add(this.toolWindow);
これで実行はできるのですが、ダブルクリックして装着しようとすると
「トップレベルのコントロールをコントロールに追加できません。」
というエラーになります。
AutoScroll属性をtrueにして、境界線がForm2の幅を越したら
Form2を横に伸ばすというプログラムになると思うのですが、
machongolaさんのsplitContainer1を使ったやり方ももしよろしかったら見せて頂けないでしょうか?

お礼日時:2009/05/22 02:55

 こんばんは。

用はフローティングツールウィンドウの事でしょうか。
 此れは、正攻法では無理でしょう・・・。
 過去何回か実装した事がありますが、本当の所、VisualStudioがどの様に実現しているのかは分かりません。
 一応以下で、同じ動きはしますが、座標の計算などアバウトに済ませています。
 その他にも、着地した時は移動出来ない様にしなければならない、たまに消えてしまう等(SetParent()の後にSetWindowLong()でスタイルをWS_CHILDに設定していないからかもしれないです)課題は山積していますが、取り敢えずは参考資料程度で。

//メインのウィンドウ
public partial class Form1 : Form
{
[DllImport("user32.dll")]
static extern IntPtr SetParent(IntPtr hWndChild, IntPtr hWndParent);

private bool bDocking;ドッキングしているかどうか
private Form toolWindow;//宙を浮くForm2の事

public Form1()
{
InitializeComponent();
}

private void Form1_Load(object sender, EventArgs e)
{
this.toolWindow = new Form2();
this.toolWindow.Owner = this;
this.toolWindow.Tag = this;
this.toolWindow.Location = new Point(0, 0);
this.toolWindow.Size = this.splitter1.Size;
this.toolWindow.Show();
}

public void PostDocking()
{
//スプリットの上にいれば、宙に浮かす
if (this.bDocking)
{
this.toolWindow.Hide();
this.toolWindow.Location = new Point(0, 0);
this.toolWindow.Size = this.splitter1.Size;

SetParent(this.toolWindow.Handle, IntPtr.Zero);
this.toolWindow.WindowState = FormWindowState.Normal;

this.toolWindow.Show();
this.bDocking = false;
}
//宙に浮いていれば、スプリットの上につける
else
{
this.toolWindow.Hide();
SetParent(this.toolWindow.Handle, this.splitter1.Handle);
this.toolWindow.WindowState = FormWindowState.Maximized;

this.toolWindow.Location = new Point(0, 0);
this.toolWindow.Size = this.splitter1.Size;
this.toolWindow.Show();
this.bDocking = true;
}
}
}

//此方が宙に浮いたりするウィンドウ
public partial class Form2 : Form
{
private const int WM_NCLBUTTONDBLCLK = 0xA3;

public Form2()
{
InitializeComponent();
}

private void Form2_Load(object sender, EventArgs e)
{

}

//ウィンドウプロシージャのオーバーライド
protected override void WndProc(ref Message m)
{
//キャプションのダブルクリック
if (m.Msg == WM_NCLBUTTONDBLCLK)
{
Form1 form = (Form1)this.Tag;
form.PostDocking();
return;
}
//システムに処理させる
base.WndProc(ref m);
}
}
    • good
    • 0
この回答へのお礼

ご返答ありがとうございます。
なるほど、一般的なやり方として知られているわけじゃなかったんですね。
machongolaさんのコードで試してみましたが、たしかにそれっぽいかんじになりました。
visual studioの方との違いとして、ソリューションエクスプローラー(Form2に相当)は
着地した時は移動出来ないというより移動させようとすると下の図のようになり、

(マウスカーソルは写っていませんが、ソリューションエクスプローラーの頭をクリックしたままの状態です)
http://ranobe.com/up/src/up361075.jpg
ソリューションエクスプローラーがSpliterから飛び出して、完全に独立しているようです。

machongolaさんの方は、Spliterの中に入り込んでる状態です。
http://ranobe.com/up/src/up361076.jpg

visual studioの方は移動させようとする瞬間に親ウィンドウからソリューションエクスプローラーを切り離しているのでしょうか?
つまり
>this.toolWindow.Owner = this;
の部分を変更しているのかなと思うのですが

お礼日時:2009/05/21 01:20

お探しのQ&Aが見つからない時は、教えて!gooで質問しましょう!


人気Q&Aランキング