#title(.NETプログラミング研究 第13号) #navi(.NET プログラミング研究) #contents *.NETプログラミング研究 第13号 [#jb1098d4] **.NET Tips [#d0567963] ***フォームの形を変える [#fd7b9ead] #column(注意){{ この記事の最新版は「[[フォームやコントロールの形を変える>http://dobon.net/vb/dotnet/form/formregion.html]]」及び「[[フォームウィンドウの特定の色を透明にする>http://dobon.net/vb/dotnet/form/transparencykey.html]]」で公開しています。 }} フォームの形を四角(矩形)以外の形に変える方法としてここでは、Control.Regionプロパティを使う方法と、Form.TransparencyKeyプロパティを使う方法の2つを紹介します。まずはControl.Regionプロパティを使った方法から。 形を変えたいフォームのRegionプロパティに、その形状を示したRegionオブジェクトを指定することにより、フォームの形を変えることが出来ます。 次の例はフォームをドーナッツ型にするコードです。ここではフォームのLoadイベントハンドラ内にコードを書いていますが、コンストラクタ内の適当な位置などに記述してもかまいません。 #code(vbnet){{ 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 }} #code(csharp){{ 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!")にするものです。 #code(vbnet){{ 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 }} #code(csharp){{ 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の形を作っています。ここでは白を透明色としています。 #code(vbnet){{ 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 }} #code(csharp){{ 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); } }} **.NET質問箱 [#h0617ae4] ***メールのサブジェクトをデコードするには? [#y1303da9] #column(注意){{ この記事の最新版は「[[メールのサブジェクトをデコードする>http://dobon.net/vb/dotnet/internet/decodemailsubject.html]]」で公開しています。 }} ''質問:'' 受信したメールのSubjectをデコードしたいのですが、どのようにすればよいのでしょうか? 例「=?ISO-2022-JP?B?GyRCJCIkMSReJDckRiQqJGEkRyRIJCYhKhsoSg==?=」 ''答え:'' ちゃんとしたものを作るにはRFC2047の知識が必要になると思いますが、「とりあえず」的なもの(Base64形式のみ対応)であれば、次のような簡単なコード(メソッド)で実現できます。 -[[RFC2047>http://www.ietf.org/rfc/rfc2047.txt]] #code(vbnet){{ '/ <summary> '/ メールのサブジェクトをデコードする '/ </summary> '/ <param name="subject">デコードするメールサブジェクト</param> '/ <returns>デコードされた文字列</returns> 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 }} #code(csharp){{ /// <summary> /// メールのサブジェクトをデコードする /// </summary> /// <param name="subject">デコードするメールサブジェクト</param> /// <returns>デコードされた文字列</returns> 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]] ***フォームが閉じられる時その原因を知るには? [#b29109d6] #column(注意){{ この記事の最新版は「[[フォームが閉じられる時その原因を知る>http://dobon.net/vb/dotnet/form/unloadmode.html]]」で公開しています。 }} ''質問:'' VB6のQueryUnloadイベントにおけるUnloadModeのように、フォームが閉じられる時にどうしてフォームが閉じられようとしているのか(ウィンドウのCloseボタンのクリックにより閉じられようとしているのか、コードのCloseメソッドにより閉じられようとしているのか等)知るにはどのようにすればよいのでしょうか? ''答え:'' これにはいくつかの方法があるようです。 一つ目は、フォームのWndProcメソッドをオーバーライドし、送られてくるメッセージを調べるという方法です。次の例では、WM_ENDSESSION、WM_SYSCOMMAND、WM_CLOSEが送られてきたか調べ、フォームが閉じられる原因がOSのシャットダウンによるか、Xボタンやコントロールメニューによるか、コードによるか判断しています。 #code(vbnet){{ 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 }} #code(csharp){{ 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]] #code(vbnet){{ 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 }} #code(csharp){{ 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]] **コメント [#u1e2d7df] #comment //これより下は編集しないでください #pageinfo([[:Category/.NET]],2003-07-29 (火) 06:00:00,DOBON!,2010-03-21 (日) 00:31:27,DOBON!) |