.NETプログラミング研究 第13号 †
.NET Tips †
フォームの形を変える †
フォームの形を四角(矩形)以外の形に変える方法としてここでは、Control.Regionプロパティを使う方法と、Form.TransparencyKeyプロパティを使う方法の2つを紹介します。まずはControl.Regionプロパティを使った方法から。
形を変えたいフォームのRegionプロパティに、その形状を示したRegionオブジェクトを指定することにより、フォームの形を変えることが出来ます。
次の例はフォームをドーナッツ型にするコードです。ここではフォームのLoadイベントハンドラ内にコードを書いていますが、コンストラクタ内の適当な位置などに記述してもかまいません。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
| | Private Sub Form1_Load(ByVal sender As System.Object, _
ByVal e As System.EventArgs) Handles MyBase.Load
Me.SetBounds(Me.Left, Me.Top, 301, 301, BoundsSpecified.Size)
Dim myGraphicsPath As System.Drawing.Drawing2D.GraphicsPath = _
New System.Drawing.Drawing2D.GraphicsPath()
myGraphicsPath.AddEllipse(New Rectangle(0, 0, 300, 300))
myGraphicsPath.AddEllipse(New Rectangle(100, 100, 100, 100))
Me.Region = New Region(myGraphicsPath)
End Sub
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
| | private void Form1_Load(object sender, System.EventArgs e)
{
this.SetBounds(this.Left, this.Top, 301, 301, BoundsSpecified.Size);
System.Drawing.Drawing2D.GraphicsPath myGraphicsPath =
new System.Drawing.Drawing2D.GraphicsPath();
myGraphicsPath.AddEllipse(new Rectangle(0, 0, 300, 300));
myGraphicsPath.AddEllipse(new Rectangle(100, 100, 100, 100));
this.Region = new Region(myGraphicsPath);
}
|
次の例はフォームの形を文字(この例では"DOBON!")にするものです。
1
2
3
4
5
6
7
8
| | Private Sub Form1_Load(ByVal sender As System.Object, _
ByVal e As System.EventArgs) Handles MyBase.Load
Dim myGraphicsPath As New System.Drawing.Drawing2D.GraphicsPath()
myGraphicsPath.AddString("DOBON!", New FontFamily("Arial"), _
FontStyle.Bold, 50, New Point(0, 0), StringFormat.GenericDefault)
Me.Region = New Region(myGraphicsPath)
End Sub
|
1
2
3
4
5
6
7
8
9
10
| | private void Form1_Load(object sender, System.EventArgs e)
{
System.Drawing.Drawing2D.GraphicsPath myGraphicsPath =
new System.Drawing.Drawing2D.GraphicsPath();
myGraphicsPath.AddString("DOBON!", new FontFamily("Arial"),
(int) FontStyle.Bold, 50, new Point(0, 0),
StringFormat.GenericDefault);
this.Region = new Region(myGraphicsPath);
}
|
そのほかの形に変える例につきましては、下のURLをご覧ください。
次にForm.TransparencyKeyプロパティを使ってフォームの形を変える方法を紹介します。
Windows2000以降のOSであれば、Form.TransparencyKeyプロパティにより、フォームで透明にしたい色を指定できます。このTransparencyKeyを使うことにより、自由な形のフォームを簡単に作れそうです。つまり、フォームの形としたい形が描かれた画像ファイルを用意し、フォームにその画像を描画し、画像の背景色をフォームのTransparencyKeyに指定する訳です。
しかし残念なことに実際にはそう簡単にはいきません。私の試したところでは、Windowsの設定で「画面のプロパティ」の「画面の色」が「High Color(16ビット)」以下になっている時はうまく行きますが、「True Color(32ビット)」の時はTransparencyKeyに指定した色でも透明になりませんでした。(環境により違いがあるかもしれません。)
いろいろ試してみたところ、少なくとも私の環境では次のような方法により、True Colorの時でも成功するようになりました。まずフォームの背景色を透明にしたい色にし、フォームのTransparencyKeyにもその色を指定します。フォームの形に使う画像ファイルはBitmapオブジェクトとして読み込み、MakeTransparentメソッドにより、透明にしたい色(背景色)を透明色にします。(透明色を指定して保存したGif画像を使うという手もあります。)
画像をフォームに描画するには、BackgroundImageプロパティを使わずに、フォームのPaintイベントハンドラで描画するようにします。(この方法がすべての環境でうまく行くか分かりませんので、情報をいただければ助かります。)
次の例ではフォームの形を"form.bmp"という画像ファイルに保存し、これを使ってフォームForm1の形を作っています。ここでは白を透明色としています。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
| | Dim _formBitmap As Bitmap
Private Sub Form1_Load(ByVal sender As Object, _
ByVal e As System.EventArgs) Handles MyBase.Load
_formBitmap = New Bitmap("form.bmp")
_formBitmap.MakeTransparent(Color.White)
Me.FormBorderStyle = FormBorderStyle.None
Me.Size = New Size(100, 100)
Me.TransparencyKey = Color.White
Me.BackColor = Color.White
End Sub
Private Sub Form1_Paint(ByVal sender As Object, _
ByVal e As System.Windows.Forms.PaintEventArgs) _
Handles MyBase.Paint
e.Graphics.DrawImage(_formBitmap, 0, 0)
End Sub
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
| | Bitmap _formBitmap;
private void Form1_Load(object sender, System.EventArgs e)
{
_formBitmap = new Bitmap(@"form.bmp");
_formBitmap.MakeTransparent(Color.White);
this.FormBorderStyle = FormBorderStyle.None;
this.Size = new Size(100, 100);
this.TransparencyKey = Color.White;
this.BackColor = Color.White;
}
private void Form1_Paint(object sender,
System.Windows.Forms.PaintEventArgs e)
{
e.Graphics.DrawImage(_formBitmap, 0, 0);
}
|
.NET質問箱 †
メールのサブジェクトをデコードするには? †
質問:
受信したメールのSubjectをデコードしたいのですが、どのようにすればよいのでしょうか?
例「=?ISO-2022-JP?B?GyRCJCIkMSReJDckRiQqJGEkRyRIJCYhKhsoSg==?=」
答え:
ちゃんとしたものを作るにはRFC2047の知識が必要になると思いますが、「とりあえず」的なもの(Base64形式のみ対応)であれば、次のような簡単なコード(メソッド)で実現できます。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
| | Private Shared Function DecodeMailSubject(ByVal subject As String) As String
Dim s As String() = subject.Split("?"c)
Dim b() As Byte
If s(2) = "B" Then
b = System.Convert.FromBase64String(s(3))
Else
Throw New Exception("未対応のエンコード形式です。")
End If
Return System.Text.Encoding.GetEncoding(s(1)).GetString(b)
End Function
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
| | private static string DecodeMailSubject(string subject)
{
string[] s = subject.Split('?');
byte[] b;
if (s[2] == "B")
{
b = System.Convert.FromBase64String(s[3]);
}
else
{
throw new Exception("未対応のエンコード形式です。");
}
return System.Text.Encoding.GetEncoding(s[1]).GetString(b);
}
|
フォームが閉じられる時その原因を知るには? †
質問:
VB6のQueryUnloadイベントにおけるUnloadModeのように、フォームが閉じられる時にどうしてフォームが閉じられようとしているのか(ウィンドウのCloseボタンのクリックにより閉じられようとしているのか、コードのCloseメソッドにより閉じられようとしているのか等)知るにはどのようにすればよいのでしょうか?
答え:
これにはいくつかの方法があるようです。
一つ目は、フォームのWndProcメソッドをオーバーライドし、送られてくるメッセージを調べるという方法です。次の例では、WM_ENDSESSION、WM_SYSCOMMAND、WM_CLOSEが送られてきたか調べ、フォームが閉じられる原因がOSのシャットダウンによるか、Xボタンやコントロールメニューによるか、コードによるか判断しています。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
| | Protected Overrides Sub WndProc(ByRef m As System.Windows.Forms.Message)
Const WM_CLOSE As Integer = &H10
Const WM_ENDSESSION As Integer = &H16
Const WM_SYSCOMMAND As Integer = &H112
Const SC_CLOSE As Integer = &HF060
Select Case m.Msg
Case WM_ENDSESSION
Console.WriteLine("WM_ENDSESSION")
Case WM_SYSCOMMAND
If m.WParam.ToInt32() = SC_CLOSE Then
Console.WriteLine("SC_CLOSE")
End If
Case WM_CLOSE
Console.WriteLine("WM_CLOSE")
End Select
MyBase.WndProc(m)
End Sub
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
| | protected override void WndProc(ref Message m)
{
const int WM_CLOSE = 0x0010;
const int WM_ENDSESSION = 0x16;
const int WM_SYSCOMMAND = 0x112;
const int SC_CLOSE = 0xF060;
switch (m.Msg)
{
case WM_ENDSESSION:
Console.WriteLine("WM_ENDSESSION");
break;
case WM_SYSCOMMAND:
if (m.WParam.ToInt32() == SC_CLOSE)
Console.WriteLine("SC_CLOSE");
break;
case WM_CLOSE:
Console.WriteLine("WM_CLOSE");
break;
}
base.WndProc (ref m);
}
|
二番目の方法は、StackFrameを使うというちょっと変わった方法で、これは GotDotNet Message Boards で紹介されています。
ここで紹介されているYeahIGotDotNetさん、MikeWill34さんの書いたコード及び、CodeProjectで紹介されているEvilDoctorSmithさんのコードを参考にさせていただき、次のようなコードを書いてみました。詳しくは上記URLをご覧ください。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
| | Private Sub Form1_Closing(ByVal sender As Object, _
ByVal e As System.ComponentModel.CancelEventArgs) _
Handles MyBase.Closing
Dim stack As New System.Diagnostics.StackTrace(True)
Dim frame7 As System.Diagnostics.StackFrame = stack.GetFrame(7)
Select Case frame7.GetMethod().Name
Case "DispatchMessageW"
Console.WriteLine("タスクマネージャーによる")
Case "SendMessage"
Console.WriteLine("コードによる")
Case "CallWindowProc"
If stack.FrameCount > 14 Then
Dim frame14 As System.Diagnostics.StackFrame = _
stack.GetFrame(14)
If frame14.GetMethod().Name = "WmSysCommand" Then
Console.WriteLine( _
"Xボタンまたはコントロールメニューによる")
Else
If frame14.GetMethod().Name = "WndProc" Then
Console.WriteLine("OSのシャットダウンによる")
End If
End If
End If
Case "DefMDIChildProc"
Console.WriteLine( _
"MDI子フォームのXボタンまたはコントロールメニューによる")
Case "DefFrameProc"
Console.WriteLine("MDI親フォームが閉じられたことによる")
Case "ShowDialog"
Console.WriteLine("モーダルダイアログが閉じられたことによる")
Case Else
Console.WriteLine("原因不明")
End Select
End Sub
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
| | private void Form1_Closing(object sender,
System.ComponentModel.CancelEventArgs e)
{
System.Diagnostics.StackTrace stack =
new System.Diagnostics.StackTrace(true);
System.Diagnostics.StackFrame frame7 = stack.GetFrame(7);
switch (frame7.GetMethod().Name)
{
case "DispatchMessageW":
Console.WriteLine("タスクマネージャーによる");
break;
case "SendMessage":
Console.WriteLine("コードによる");
break;
case "CallWindowProc":
if (stack.FrameCount > 14)
{
System.Diagnostics.StackFrame frame14 =
stack.GetFrame(14);
if (frame14.GetMethod().Name == "WmSysCommand")
Console.WriteLine(
"Xボタンまたはコントロールメニューによる");
else if (frame14.GetMethod().Name == "WndProc")
Console.WriteLine("OSのシャットダウンによる");
}
break;
case "DefMDIChildProc":
Console.WriteLine(
"MDI子フォームのXボタンまたはコントロールメニューによる");
break;
case "DefFrameProc":
Console.WriteLine("MDI親フォームが閉じられたことによる");
break;
case "ShowDialog":
Console.WriteLine("モーダルダイアログが閉じられたことによる");
break;
default:
Console.WriteLine("原因不明");
break;
}
}
|
最後に紹介するのは、hidden windowを使った方法です(一番目の方法を拡張したような感じです)。この方法につきましては、下記URLで紹介されていますので、興味のある方はご覧ください。
コメント †