┏第53号━━━━━━━━━━━━━━━━━━━━━━━━━━┓ ┃         .NETプログラミング研究         ┃ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ──<メニュー>─────────────────────── ■お知らせ ■.NET質問箱 ・Buttonコントロールで矢印キーが押されたことを知るには? ・Buttonコントロールにフォーカスがあるとフォームで矢印キーのキー  イベントが発生しない ・画面をキャプチャするには? ・複数のRTFファイルを連結してRichTextBoxに表示するには? ─────────────────────────────── ─────────────────────────────── ■お知らせ ─────────────────────────────── ●「.NETプログラミング研究」が「まぐまぐ!」殿堂入り 皆様のおかげでこのメールマガジン「.NETプログラミング研究」が「ま ぐまぐ!」で殿堂入りしました。これからも「.NETプログラミング研 究」をよろしくお願いいたします。 [URL]『まぐまぐ!』メールマガジン http://www.mag2.com/ [URL]『まぐまぐ!』殿堂入りメールマガジン一覧>科学・技術 http://www.mag2.com/j/13/dendo.htm ─────────────────────────────── ■.NET質問箱 ─────────────────────────────── 「.NET質問箱」では、「どぼん!のプログラミング掲示板」に書き込 まれた.NETプログラミングに関する投稿を基に、さらに考察を加え、 Q&A形式にまとめて紹介します。 [URL]どぼん!のプログラミング掲示板 http://dobon.net/vb/bbs.html ─────────────────────────────── ●Buttonコントロールで矢印キーが押されたことを知るには? 【質問】 System.Windows.Forms.ButtonコントロールのKeyDownイベントで矢印 キーの押下を捕捉しようとしたのですが、できませんでした。Button コントロールで矢印キーが押されたことを知る方法はありますか? 【回答】 System.Windows.Forms.ButtonクラスのProcessDialogKeyメソッド(あ るいはProcessCmdKeyメソッド等)をオーバーライドすれば、矢印キー やTab、Enter、Escキーが押されたことを知ることができます。 まず次のようなSystem.Windows.Forms.Buttonクラスの派生クラスを 作成し、ProcessDialogKeyメソッドをオーバーライドします。ここで は、左キーが押された時に、メッセージボックスを表示するようにし ています。 ‥‥▽VB.NET ここから▽‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥ Public Class MyButton Inherits Button Protected Overrides Function ProcessDialogKey( _ ByVal keyData As Keys) As Boolean '左キーが押されているか調べる If (keyData And Keys.KeyCode) = Keys.Left Then MessageBox.Show("左キーが押されました。") '左キーの本来の処理(左側のコントロールにフォーカスを移す)を 'させたくないときは、trueを返す 'return true; End If Return MyBase.ProcessDialogKey(keyData) End Function End Class ‥‥△VB.NET ここまで△‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥ ‥‥▽C# ここから▽‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥ public class MyButton : Button { protected override bool ProcessDialogKey(Keys keyData) { //左キーが押されているか調べる if ((keyData & Keys.KeyCode) == Keys.Left) { MessageBox.Show("左キーが押されました。"); //左キーの本来の処理(左側のコントロールにフォーカスを移す)を //させたくないときは、trueを返す //return true; } return base.ProcessDialogKey(keyData); } } ‥‥△C# ここまで△‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥ この様にして作成したMyButtonクラスをSystem.Windows.Forms. Buttonクラスの代わりに使うようにします。具体的には、"System. Windows.Forms.Button"を"MyButton"に文字列置換すれば大抵の場合 大丈夫でしょう。 ○この記事の基になった掲示板のスレッド [題名] BUTTONのKeyPressイベントについて [投稿者(敬称略)] john, ピラルク [URL] http://dobon.net/vb/bbs/log3-5/2597.html ○この記事の基になった掲示板のスレッド [題名] Tabキーイベントを発生させたい [投稿者(敬称略)] Dai, Codingslave, デフロボ [URL] http://dobon.net/vb/bbs/log3-6/3461.html ─────────────────────────────── ●Buttonコントロールにフォーカスがあるとフォームで矢印キーのキ ーイベントが発生しない 【質問】 フォームのKeyPreviewプロパティをtrueにして、KeyDownやKeyUpなど のキーイベントをフォームで受け取るようにしています。しかし Buttonコントロールにフォーカスがあると矢印キーやタブキーの押下 でキーイベントが発生しません。何か良い解決法はありませんか? 【回答】 矢印キーが押されたかをフォームで知る場合は、先の「Buttonコント ロールで矢印キーが押されたことを知るには?」と同様に、フォーム クラスのProcessDialogKeyメソッドをオーバーライドします。この場 合はフォームデザイナで作成されたコードを変更する必要が無いため、 先の方法より簡単です。 具体的には、次のようなコードをフォームクラスに追加します。この 例では、左キーとタブキーが押された時に、メッセージボックスを表 示するようにしています。 ‥‥▽VB.NET ここから▽‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥ Protected Overrides Function ProcessDialogKey( _ ByVal keyData As Keys) As Boolean If (keyData And Keys.KeyCode) = Keys.Left Then '左キーが押されているか調べる MessageBox.Show("左キーが押されました。") '左キーの本来の処理(左側のコントロールにフォーカスを移す)を 'させたくないときは、trueを返す Return True ElseIf (keyData And Keys.KeyCode) = Keys.Tab Then 'Tabキーが押されているか調べる MessageBox.Show("Tabキーが押されました。") Return True End If Return MyBase.ProcessDialogKey(keyData) End Function ‥‥△VB.NET ここまで△‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥ ‥‥▽C# ここから▽‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥ protected override bool ProcessDialogKey(Keys keyData) { //左キーが押されているか調べる if ((keyData & Keys.KeyCode) == Keys.Left) { MessageBox.Show("左キーが押されました。"); //左キーの本来の処理(左側のコントロールにフォーカスを移す)を //させたくないときは、trueを返す return true; } //Tabキーが押されているか調べる else if ((keyData & Keys.KeyCode) == Keys.Tab) { MessageBox.Show("Tabキーが押されました。"); return true; } return base.ProcessDialogKey(keyData); } ‥‥△C# ここまで△‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥ なおタブキーが押されたかを調べる場合は、Buttonのあるフォームの ProcessTabKeyメソッドをオーバーライドする方法もあります。フォー ムクラスに次のようなコードを追加することにより、Tabキーが押さ れた時にメッセージボックスを表示し、次のコントロールがアクティ ブにならないようにすることができます。 ‥‥▽VB.NET ここから▽‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥ Protected Overrides Function ProcessTabKey( _ ByVal forward As Boolean) As Boolean MessageBox.Show("Tabキーが押されました。") Return True End Function ‥‥△VB.NET ここまで△‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥ ‥‥▽C# ここから▽‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥ protected override bool ProcessTabKey(bool forward) { MessageBox.Show("Tabキーが押されました。"); return true; } ‥‥△C# ここまで△‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥ ○この記事の基になった掲示板のスレッド [題名] フォーム での KeyDownイベント [投稿者(敬称略)] ヒロコ, Codingslave [URL] http://dobon.net/vb/bbs/log3-6/3373.html ─────────────────────────────── ●画面をキャプチャするには? 【質問】 画面をキャプチャ(ハードコピー)し、イメージをBitmapで取得した いのですが、どのような方法がありますか? 【回答】 最も簡単な方法は、Print ScreenキーストロークをSendKeys. SendWait(またはSend)メソッドで送信し、クリップボードにコピー されたイメージを取得する方法です。Ctrl+PrintScreenキーストロー ク("^{PRTSC}")で画面全体がキャプチャされ、Alt+PrintScreenキー ストローク("%{PRTSC}")でアクティブなウィンドウがキャプチャさ れます(PrintScreenキーストロークのみでもアクティブなウィンド ウがキャプチャされるようです)。 ただしこの方法では確実に画面のイメージを取得できる保障はありま せん(実際に失敗することも多いようです)。 補足:MSDNには、「{PRTSC} (今後使用するために予約されている)」 と書かれています。 [URL]SendKeys.Send メソッド http://www.microsoft.com/japan/msdn/library/ja/cpref/html/frlrfSystemWindowsFormsSendKeysClassSendTopic.asp この方法により画面全体のイメージを取得し、PictureBox( PictureBox1)に表示するサンプルを以下に示します。 ‥‥▽VB.NET ここから▽‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥ '画面全体のイメージをクリップボードにコピー SendKeys.SendWait("^{PRTSC}") '次のようにすると、アクティブなウィンドウのイメージをコピー 'SendKeys.SendWait("{PRTSC}") 'SendKeys.SendWait("%{PRTSC}") 'DoEventsを呼び出したほうがよい場合があるらしい 'Application.DoEvents() 'クリップボードにあるデータの取得 Dim d As IDataObject = Clipboard.GetDataObject() 'ビットマップデータ形式に関連付けられているデータを取得 Dim img As Image = CType(d.GetData(DataFormats.Bitmap), Image) If Not (img Is Nothing) Then 'データが取得できたときはPictureBoxに表示する PictureBox1.Image = img '画面のイメージデータは大きいため、 '用がなくなればクリップボードから削除した方がいいかもしれない Clipboard.SetDataObject(New DataObject) End If ‥‥△VB.NET ここまで△‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥ ‥‥▽C# ここから▽‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥ //画面全体のイメージをクリップボードにコピー SendKeys.SendWait("^{PRTSC}"); //次のようにすると、アクティブなウィンドウのイメージをコピー //SendKeys.SendWait("{PRTSC}"); //SendKeys.SendWait("%{PRTSC}"); //DoEventsを呼び出したほうがよい場合があるらしい //Application.DoEvents(); //クリップボードにあるデータの取得 IDataObject d = Clipboard.GetDataObject(); //ビットマップデータ形式に関連付けられているデータを取得 Image img = (Image) d.GetData(DataFormats.Bitmap); if (img != null) { //データが取得できたときはPictureBoxに表示する PictureBox1.Image = img; //画面のイメージデータは大きいため、 //用がなくなればクリップボードから削除した方がいいかもしれない Clipboard.SetDataObject(new DataObject()); } ‥‥△C# ここまで△‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥ ちゃんとした画面キャプチャを行うには、Win32 APIを使うことにな ります。この方法関しては、ウェブ上で優れたサンプルがいくつも公 開されていますので、ここではその一部を紹介します。 以下はC#のサンプルです。 [URL]The Code Project - Screen Captures, Window Captures, and Window Icon Captures with Spy++ style Window Finder! http://www.codeproject.com/useritems/Screen_Capturing.asp [URL]Capture a Screen Shot (.NET) - Developer Fusion http://www.developerfusion.com/show/4630/ 以下はVB.NETのサンプルです。 [URL]Capture Screen http://www.planet-source-code.com/vb/scripts/ShowCode.asp?txtCodeId=309&lngWId=10 以下に画面全体をキャプチャするメソッド(CaptureScreen)と、ア クティブなウィンドウをキャプチャするメソッド( CaptureActiveWindow)のごく簡単なサンプルを紹介します。 ‥‥▽VB.NET ここから▽‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥ 'Imports System.Runtime.InteropServices Const SRCCOPY As Integer = 13369376 _ Private Shared Function GetDC(ByVal hwnd As IntPtr) As IntPtr End Function _ Private Shared Function BitBlt(ByVal hDestDC As IntPtr, _ ByVal x As Integer, ByVal y As Integer, _ ByVal nWidth As Integer, ByVal nHeight As Integer, _ ByVal hSrcDC As IntPtr, _ ByVal xSrc As Integer, ByVal ySrc As Integer, _ ByVal dwRop As Integer) As Integer End Function _ Private Shared Function ReleaseDC(ByVal hwnd As IntPtr, _ ByVal hdc As IntPtr) As IntPtr End Function ''' ''' プライマリスクリーンの画像を取得する ''' ''' プライマリスクリーンの画像 Public Shared Function CaptureScreen() As Bitmap 'プライマリモニタのデバイスコンテキストを取得 Dim disDC As IntPtr = GetDC(IntPtr.Zero) 'Bitmapの作成 Dim bmp As New Bitmap(Screen.PrimaryScreen.Bounds.Width, _ Screen.PrimaryScreen.Bounds.Height) 'Graphicsの作成 Dim g As Graphics = Graphics.FromImage(bmp) 'Graphicsのデバイスコンテキストを取得 Dim hDC As IntPtr = g.GetHdc() 'Bitmapに画像をコピーする BitBlt(hDC, 0, 0, bmp.Width, bmp.Height, disDC, 0, 0, SRCCOPY) '解放 g.ReleaseHdc(hDC) g.Dispose() ReleaseDC(IntPtr.Zero, disDC) Return bmp End Function _ Private Structure RECT Public left As Integer Public top As Integer Public right As Integer Public bottom As Integer End Structure _ Private Shared Function GetWindowDC(ByVal hwnd As IntPtr) As IntPtr End Function _ Private Shared Function GetForegroundWindow() As IntPtr End Function _ Private Shared Function GetWindowRect(ByVal hwnd As IntPtr, _ ByRef lpRect As RECT) As Integer End Function ''' ''' アクティブなウィンドウの画像を取得する ''' ''' アクティブなウィンドウの画像 Public Shared Function CaptureActiveWindow() As Bitmap 'アクティブなウィンドウのデバイスコンテキストを取得 Dim hWnd As IntPtr = GetForegroundWindow() Dim winDC As IntPtr = GetWindowDC(hWnd) 'ウィンドウの大きさを取得 Dim winRect As New RECT GetWindowRect(hWnd, winRect) 'Bitmapの作成 Dim bmp As New Bitmap(winRect.right - winRect.left, _ winRect.bottom - winRect.top) 'Graphicsの作成 Dim g As Graphics = Graphics.FromImage(bmp) 'Graphicsのデバイスコンテキストを取得 Dim hDC As IntPtr = g.GetHdc() 'Bitmapに画像をコピーする BitBlt(hDC, 0, 0, bmp.Width, bmp.Height, winDC, 0, 0, SRCCOPY) '解放 g.ReleaseHdc(hDC) g.Dispose() ReleaseDC(hWnd, winDC) Return bmp End Function ‥‥△VB.NET ここまで△‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥ ‥‥▽C# ここから▽‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥ //using System.Runtime.InteropServices; private const int SRCCOPY = 13369376; [DllImport("user32.dll")] private static extern IntPtr GetDC(IntPtr hwnd); [DllImport("gdi32.dll")] private static extern int BitBlt(IntPtr hDestDC, int x, int y, int nWidth, int nHeight, IntPtr hSrcDC, int xSrc, int ySrc, int dwRop); [DllImport("user32.dll")] private static extern IntPtr ReleaseDC(IntPtr hwnd, IntPtr hdc); /// /// プライマリスクリーンの画像を取得する /// /// プライマリスクリーンの画像 public static Bitmap CaptureScreen() { //プライマリモニタのデバイスコンテキストを取得 IntPtr disDC = GetDC(IntPtr.Zero); //Bitmapの作成 Bitmap bmp = new Bitmap(Screen.PrimaryScreen.Bounds.Width, Screen.PrimaryScreen.Bounds.Height); //Graphicsの作成 Graphics g = Graphics.FromImage(bmp); //Graphicsのデバイスコンテキストを取得 IntPtr hDC = g.GetHdc(); //Bitmapに画像をコピーする BitBlt(hDC, 0, 0, bmp.Width, bmp.Height, disDC, 0, 0, SRCCOPY); //解放 g.ReleaseHdc(hDC); g.Dispose(); ReleaseDC(IntPtr.Zero, disDC); return bmp; } [StructLayout(LayoutKind.Sequential)] private struct RECT { public int left; public int top; public int right; public int bottom; } [DllImport("user32.dll")] private static extern IntPtr GetWindowDC(IntPtr hwnd); [DllImport("user32.dll")] private static extern IntPtr GetForegroundWindow(); [DllImport("user32.dll")] private static extern int GetWindowRect(IntPtr hwnd, ref RECT lpRect); /// /// アクティブなウィンドウの画像を取得する /// /// アクティブなウィンドウの画像 public static Bitmap CaptureActiveWindow() { //アクティブなウィンドウのデバイスコンテキストを取得 IntPtr hWnd = GetForegroundWindow(); IntPtr winDC = GetWindowDC(hWnd); //ウィンドウの大きさを取得 RECT winRect = new RECT(); GetWindowRect(hWnd, ref winRect); //Bitmapの作成 Bitmap bmp = new Bitmap(winRect.right - winRect.left, winRect.bottom - winRect.top); //Graphicsの作成 Graphics g = Graphics.FromImage(bmp); //Graphicsのデバイスコンテキストを取得 IntPtr hDC = g.GetHdc(); //Bitmapに画像をコピーする BitBlt(hDC, 0, 0, bmp.Width, bmp.Height, winDC, 0, 0, SRCCOPY); //解放 g.ReleaseHdc(hDC); g.Dispose(); ReleaseDC(hWnd, winDC); return bmp; } ‥‥△C# ここまで△‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥ ○この記事の基になった掲示板のスレッド [題名] C#での画面ハードコピー [投稿者(敬称略)] Odagirizm, 管理人, よねKEN [URL] http://dobon.net/vb/bbs/log3-1/447.html [題名] 指定範囲の画面のキャプチャについて [投稿者(敬称略)] tos, うに, Odagirizm [URL] http://dobon.net/vb/bbs/log3-3/1004.html [題名] 画面のハードコピーをとりたい [投稿者(敬称略)] Dai, Codingslave, りょう, うほ [URL] http://dobon.net/vb/bbs/log3-6/3449.html ─────────────────────────────── ●複数のRTFファイルを連結してRichTextBoxに表示するには? 【質問】 複数のRTFファイルを連結したくて、RTFファイルをテキストとして読 み込み、文字列を連結し、RichTextBoxのRtfプロパティに代入したの ですが、うまくいきません。複数のRTFファイルを連結して RichTextBoxに表示するにはどのようにすればよいのでしょうか? 【回答】 RichTextBoxにRTFを挿入するには、RichTextBox.SelectedRtfプロパ ティを使用しますが、RTFファイルのテキストをSelectedRtfプロパテ ィに代入することにより、そのフォーマットされた内容が RichTextBoxに挿入されます。つまり複数のRTFファイルを連結して RichTextBoxに表示するには、RTFファイルをテキストとして読み込み、 RichTextBoxのSelectionStartを末尾にし、SelectedRtfにRTFファイ ルのRTFを設定するという作業を繰り返せばよいということになりま す。 次にRTFファイル"C:\1.rtf"、"C:\2.rtf"、"C:\3.rtf"を連結し、 RichTextBox(RichTextBox1)に表示するコードを示します。 ‥‥▽VB.NET ここから▽‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥ 'Button1のClickイベントハンドラ Private Sub Button1_Click(ByVal sender As System.Object, _ ByVal e As System.EventArgs) Handles Button1.Click RichTextBox1.Clear() Dim i As Integer For i = 1 To 3 'RTFファイルを読み込む Dim sr As New System.IO.StreamReader( _ "C:\" + i.ToString() + ".rtf", _ System.Text.Encoding.GetEncoding(932)) Dim rtf As String = sr.ReadToEnd() sr.Close() 'RichTextBox1に追加する AppendRtfToRichTextBox(RichTextBox1, rtf) Next i End Sub ''' ''' RichTextBoxの末尾にRTFを追加 ''' ''' RTFを追加するRichTextBox ''' 追加するRTF文字列 Public Shared Sub AppendRtfToRichTextBox( _ ByVal rtb As RichTextBox, ByVal rtfText As String) rtb.SelectionStart = rtb.TextLength rtb.SelectedRtf = rtfText End Sub ‥‥△VB.NET ここまで△‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥ ‥‥▽C# ここから▽‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥ //Button1のClickイベントハンドラ private void Button1_Click(object sender, System.EventArgs e) { RichTextBox1.Clear(); for (int i = 1; i <= 3; i++) { //RTFファイルを読み込む System.IO.StreamReader sr = new System.IO.StreamReader( "C:\\" + i.ToString() + ".rtf", System.Text.Encoding.GetEncoding(932)); string rtf = sr.ReadToEnd(); sr.Close(); //RichTextBox1に追加する AppendRtfToRichTextBox(RichTextBox1, rtf); } } /// /// RichTextBoxの末尾にRTFを追加 /// /// RTFを追加するRichTextBox /// 追加するRTF文字列 public static void AppendRtfToRichTextBox( RichTextBox rtb, string rtfText) { rtb.SelectionStart = rtb.TextLength; rtb.SelectedRtf = rtfText; } ‥‥△C# ここまで△‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥ 参考: [URL]The Code Project - Insert Plain Text and Images into RichTextBox at Runtime http://www.codeproject.com/cs/miscctrl/csexrichtextbox.asp [題名] リッチテキストの文字列連結 [投稿者(敬称略)] 四季, りょう, Codingslave [URL] http://dobon.net/vb/bbs/log3-6/3472.html =============================== ■ここで示したコードの多くはまずC#で書き、それを「C# to VB.NET Translator」でVB.NETのコードに変換し、修正を加えたものです。 [URL]C# to VB.NET Translator http://authors.aspalliance.com/aldotnet/examples/translate.aspx ■このマガジンの購読、購読中止、バックナンバー、説明に関しては  次のページをご覧ください。  http://www.mag2.com/m/0000104516.htm ■発行人・編集人:どぼん!  (Microsoft MVP for Visual Basic, Oct 2004-Oct 2005)  http://dobon.net  dobon_info@yahoo.co.jp ■ご質問等はメールではなく、掲示板へお願いいたします。  http://dobon.net/vb/bbs.html ■上記メールアドレスへのメールは確実に読まれる保障はありません  (スパム、ウィルス対策です)。メールは下記URLのフォームメール  から送信してください。  http://dobon.net/mail.html Copyright (c) 2003 - 2005 DOBON! All rights reserved. ===============================