┏第13号━━━━━━━━━━━━━━━━━━━━━━━━━━┓ ┃         .NETプログラミング研究         ┃ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ 〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜メニュー ■.NET Tips ・フォームの形を変える ■.NET質問箱 ・メールのサブジェクトをデコードするには? ・フォームが閉じられる時その原因を知るには? 〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜 ─────────────────────────────── ■.NET Tips ─────────────────────────────── ●フォームの形を変える フォームの形を四角(矩形)以外の形に変える方法としてここでは、 Control.Regionプロパティを使う方法と、Form.TransparencyKeyプロ パティを使う方法の2つを紹介します。まずはControl.Regionプロパテ ィを使った方法から。 形を変えたいフォームのRegionプロパティに、その形状を示した Regionオブジェクトを指定することにより、フォームの形を変えるこ とが出来ます。 次の例はフォームをドーナッツ型にするコードです。ここではフォー ムのLoadイベントハンドラ内にコードを書いていますが、コンストラ クタ内の適当な位置などに記述してもかまいません。 '[VB.NET]・・・・・・・・・・・・・・・・・・・・・・・・・・ 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) 'GraphicsPathオブジェクトの作成 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)) 'Regionプロパティの設定 Me.Region = New Region(myGraphicsPath) End Sub '・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・ //[C#]・・・・・・・・・・・・・・・・・・・・・・・・・・・・ private void Form1_Load(object sender, System.EventArgs e) { //フォームの大きさを適当に変更 this.SetBounds(this.Left, this.Top, 301, 301, BoundsSpecified.Size); //GraphicsPathオブジェクトの作成 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)); //Regionプロパティの設定 this.Region = new Region(myGraphicsPath); } //・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・ 次の例はフォームの形を文字(この例では"DOBON!")にするものです。 '[VB.NET]・・・・・・・・・・・・・・・・・・・・・・・・・・ 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() '文字のサイズは50 myGraphicsPath.AddString("DOBON!", New FontFamily("Arial"), _ FontStyle.Bold, 50, New Point(0, 0), StringFormat.GenericDefault) Me.Region = New Region(myGraphicsPath) End Sub '・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・ //[C#]・・・・・・・・・・・・・・・・・・・・・・・・・・・・ private void Form1_Load(object sender, System.EventArgs e) { System.Drawing.Drawing2D.GraphicsPath myGraphicsPath = new System.Drawing.Drawing2D.GraphicsPath(); //文字のサイズは50 myGraphicsPath.AddString("DOBON!", new FontFamily("Arial"), (int) FontStyle.Bold, 50, new Point(0, 0), StringFormat.GenericDefault); this.Region = new Region(myGraphicsPath); } //・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・ そのほかの形に変える例につきましては、下のURLをご覧ください。 ・フォームやコントロールの形を変える http://dobon.net/vb/dotnet/form/formregion.html 次に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の形を作っています。ここでは白を透明色 としています。 '[VB.NET]・・・・・・・・・・・・・・・・・・・・・・・・・・ Dim _formBitmap As Bitmap Private Sub Form1_Load(ByVal sender As Object, _ ByVal e As System.EventArgs) Handles MyBase.Load 'TransparencyKeyプロパティを指定する前に画像を読み込む 'フォームの形の画像を読み込む _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 '・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・ //[C#]・・・・・・・・・・・・・・・・・・・・・・・・・・・・ Bitmap _formBitmap; private void Form1_Load(object sender, System.EventArgs e) { //TransparencyKeyプロパティを指定する前に画像を読み込む //フォームの形の画像を読み込む _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); } //・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・ ・フォームウィンドウの特定の色を透明にする http://dobon.net/vb/dotnet/form/transparencykey.html ─────────────────────────────── ■.NET質問箱 ─────────────────────────────── ●メールのサブジェクトをデコードするには? 質問: 受信したメールのSubjectをデコードしたいのですが、どのようにすれ ばよいのでしょうか? 例「=?ISO-2022-JP?B?GyRCJCIkMSReJDckRiQqJGEkRyRIJCYhKhsoSg==?=」 答え: ちゃんとしたものを作るにはRFC2047の知識が必要になると思いますが、 「とりあえず」的なもの(Base64形式のみ対応)であれば、次のよう な簡単なコード(メソッド)で実現できます。 ・RFC2047 http://www.ietf.org/rfc/rfc2047.txt '[VB.NET]・・・・・・・・・・・・・・・・・・・・・・・・・・ '/ '/ メールのサブジェクトをデコードする '/ '/ デコードするメールサブジェクト '/ デコードされた文字列 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 'Base64形式の時 b = System.Convert.FromBase64String(s(3)) Else 'Base64形式のみ対応 Throw New Exception("未対応のエンコード形式です。") End If 's(1)をEncoding名として、デコードする Return System.Text.Encoding.GetEncoding(s(1)).GetString(b) End Function '・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・ //[C#]・・・・・・・・・・・・・・・・・・・・・・・・・・・・ /// /// メールのサブジェクトをデコードする /// /// デコードするメールサブジェクト /// デコードされた文字列 private static string DecodeMailSubject(string subject) { //要素を分解する string[] s = subject.Split('?'); byte[] b; if (s[2] == "B") { //Base64形式の時 b = System.Convert.FromBase64String(s[3]); } else { //Base64形式のみ対応 throw new Exception("未対応のエンコード形式です。"); } //s[1]をEncoding名として、デコードする return System.Text.Encoding.GetEncoding(s[1]).GetString(b); } //・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・ ・メールのサブジェクトのデコード http://www.sky-j.com/viewlist.php?arg_forum_id=38&arg_thread_id=675&arg_message_id=675 ─────────────────────────────── ●フォームが閉じられる時その原因を知るには? 質問: VB6のQueryUnloadイベントにおけるUnloadModeのように、フォームが 閉じられる時にどうしてフォームが閉じられようとしているのか(ウ ィンドウのCloseボタンのクリックにより閉じられようとしているのか、 コードのCloseメソッドにより閉じられようとしているのか等)知るに はどのようにすればよいのでしょうか? 答え: これにはいくつかの方法があるようです。 一つ目は、フォームのWndProcメソッドをオーバーライドし、送られて くるメッセージを調べるという方法です。次の例では、 WM_ENDSESSION、WM_SYSCOMMAND、WM_CLOSEが送られてきたか調べ、フ ォームが閉じられる原因がOSのシャットダウンによるか、Xボタンやコ ントロールメニューによるか、コードによるか判断しています。 '[VB.NET]・・・・・・・・・・・・・・・・・・・・・・・・・・ 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 'OSのシャットダウンなどで閉じられようとしている Console.WriteLine("WM_ENDSESSION") Case WM_SYSCOMMAND If m.WParam.ToInt32() = SC_CLOSE Then 'Xボタン、コントロールメニューの「閉じる」、 'コントロールボックスのダブルクリック、 'Atl+F4などにより閉じられようとしている Console.WriteLine("SC_CLOSE") End If Case WM_CLOSE 'Application.Exit以外などで閉じられようとしている Console.WriteLine("WM_CLOSE") End Select MyBase.WndProc(m) End Sub '・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・ //[C#]・・・・・・・・・・・・・・・・・・・・・・・・・・・・ 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: //OSのシャットダウンなどで閉じられようとしている Console.WriteLine("WM_ENDSESSION"); break; case WM_SYSCOMMAND: if (m.WParam.ToInt32() == SC_CLOSE) //Xボタン、コントロールメニューの「閉じる」、 //コントロールボックスのダブルクリック、 //Atl+F4などにより閉じられようとしている Console.WriteLine("SC_CLOSE"); break; case WM_CLOSE: //Application.Exit以外などで閉じられようとしている Console.WriteLine("WM_CLOSE"); break; } base.WndProc (ref m); } //・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・ 二番目の方法は、StackFrameを使うというちょっと変わった方法で、 これは GotDotNet Message Boards で紹介されています。 ・GotDotNet Message Boards - Form.Closing... http://www.gotdotnet.com/Community/MessageBoard/Thread.aspx?id=40651 ここで紹介されているYeahIGotDotNetさん、MikeWill34さんの書いた コード及び、CodeProjectで紹介されているEvilDoctorSmithさんのコー ドを参考にさせていただき、次のようなコードを書いてみました。詳 しくは上記URLをご覧ください。 ・The Code Project - Find out what's closing your application http://www.codeproject.com/useritems/FormClosing.asp '[VB.NET]・・・・・・・・・・・・・・・・・・・・・・・・・・ 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 '・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・ //[C#]・・・・・・・・・・・・・・・・・・・・・・・・・・・・ 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で紹 介されていますので、興味のある方はご覧ください。 ・Visual Studio Magazine - Determine a Form's UnloadMode http://www.fawcette.com/Archives/premier/mgznarch/vbpj/2001/11nov01/qa0111/qa0111.asp ・フォームのXボタンの制御 http://www.sky-j.com/viewlist.php?arg_forum_id=38&arg_thread_id=772&arg_message_id=772 =============================== ■このマガジンの購読、購読中止、バックナンバー、説明に関しては  次のページをご覧ください。  http://www.mag2.com/m/0000104516.htm ■発行人・編集人:どぼん!  http://dobon.net  dobon@bigfoot.com ■ご質問等はメールではなく、掲示板へお願いいたします。  http://dobon.net/bbs ■上記メールアドレスへのメールは確実に読まれる保障はありません  (スパム、ウィルス対策です)。メールは下記URLのフォームメール  から送信してください。  http://dobon.net/mail.html Copyright (c) 2003 DOBON! All rights reserved. ===============================