.NETプログラミング研究 第53号

お知らせ

「.NETプログラミング研究」が「まぐまぐ!」殿堂入り

皆様のおかげでこのメールマガジン「.NETプログラミング研究」が「まぐまぐ!」で殿堂入りしました。これからも「.NETプログラミング研究」をよろしくお願いいたします。

.NET質問箱

「.NET質問箱」では、「どぼん!のプログラミング掲示板」に書き込まれた.NETプログラミングに関する投稿を基に、さらに考察を加え、Q&A形式にまとめて紹介します。

Buttonコントロールで矢印キーが押されたことを知るには?

注意

この記事の最新版は「コントロールで矢印、Tab、Enter、Escキーが押されたことを知る」で公開しています。

【質問】

System.Windows.Forms.ButtonコントロールのKeyDownイベントで矢印キーの押下を捕捉しようとしたのですが、できませんでした。Buttonコントロールで矢印キーが押されたことを知る方法はありますか?

【回答】

System.Windows.Forms.ButtonクラスのProcessDialogKeyメソッド(あるいはProcessCmdKeyメソッド等)をオーバーライドすれば、矢印キーやTab、Enter、Escキーが押されたことを知ることができます。

まず次のようなSystem.Windows.Forms.Buttonクラスの派生クラスを作成し、ProcessDialogKeyメソッドをオーバーライドします。ここでは、左キーが押された時に、メッセージボックスを表示するようにしています。

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
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
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
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);
    }
}

この様にして作成したMyButtonクラスをSystem.Windows.Forms.Buttonクラスの代わりに使うようにします。具体的には、"System.Windows.Forms.Button"を"MyButton"に文字列置換すれば大抵の場合大丈夫でしょう。

○この記事の基になった掲示板のスレッド

○この記事の基になった掲示板のスレッド

Buttonコントロールにフォーカスがあるとフォームで矢印キーのキーイベントが発生しない

注意

この記事の最新版は「Buttonコントロールにフォーカスがあるとフォームで矢印キーのキーイベントが発生しない」で公開しています。

【質問】

フォームのKeyPreviewプロパティをtrueにして、KeyDownやKeyUpなどのキーイベントをフォームで受け取るようにしています。しかしButtonコントロールにフォーカスがあると矢印キーやタブキーの押下でキーイベントが発生しません。何か良い解決法はありませんか?

【回答】

矢印キーが押されたかをフォームで知る場合は、先の「Buttonコントロールで矢印キーが押されたことを知るには?」と同様に、フォームクラスのProcessDialogKeyメソッドをオーバーライドします。この場合はフォームデザイナで作成されたコードを変更する必要が無いため、先の方法より簡単です。

具体的には、次のようなコードをフォームクラスに追加します。この例では、左キーとタブキーが押された時に、メッセージボックスを表示するようにしています。

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
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
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
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);
}

なおタブキーが押されたかを調べる場合は、ButtonのあるフォームのProcessTabKeyメソッドをオーバーライドする方法もあります。フォームクラスに次のようなコードを追加することにより、Tabキーが押された時にメッセージボックスを表示し、次のコントロールがアクティブにならないようにすることができます。

  1
  2
  3
  4
  5
Protected Overrides Function ProcessTabKey( _
        ByVal forward As Boolean) As Boolean
    MessageBox.Show("Tabキーが押されました。")
    Return True
End Function
  1
  2
  3
  4
  5
protected override bool ProcessTabKey(bool forward)
{
    MessageBox.Show("Tabキーが押されました。");
    return true;
}

○この記事の基になった掲示板のスレッド

画面をキャプチャするには?

注意

この記事の最新版は「画面をキャプチャする」で公開しています。

【質問】

画面をキャプチャ(ハードコピー)し、イメージをBitmapで取得したいのですが、どのような方法がありますか?

【回答】

最も簡単な方法は、Print ScreenキーストロークをSendKeys.SendWait(またはSend)メソッドで送信し、クリップボードにコピーされたイメージを取得する方法です。Ctrl+PrintScreenキーストローク("^{PRTSC}")で画面全体がキャプチャされ、Alt+PrintScreenキーストローク("%{PRTSC}")でアクティブなウィンドウがキャプチャされます(PrintScreenキーストロークのみでもアクティブなウィンドウがキャプチャされるようです)。

ただしこの方法では確実に画面のイメージを取得できる保障はありません(実際に失敗することも多いようです)。

補足:MSDNには、「{PRTSC} (今後使用するために予約されている)」と書かれています。

この方法により画面全体のイメージを取得し、PictureBox(PictureBox1)に表示するサンプルを以下に示します。

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
'画面全体のイメージをクリップボードにコピー
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
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
//画面全体のイメージをクリップボードにコピー
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());
}

ちゃんとした画面キャプチャを行うには、Win32 APIを使うことになります。この方法関しては、ウェブ上で優れたサンプルがいくつも公開されています。その一部を紹介します。

以下はC#のサンプルです。

以下はVB.NETのサンプルです。

以下に画面全体をキャプチャするメソッド(CaptureScreen)と、アクティブなウィンドウをキャプチャするメソッド(CaptureActiveWindow)のごく簡単なサンプルを紹介します。

  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
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
'Imports System.Runtime.InteropServices
 
Const SRCCOPY As Integer = 13369376
 
<DllImport("user32.dll")> _
Private Shared Function GetDC(ByVal hwnd As IntPtr) As IntPtr
End Function
 
<DllImport("gdi32.dll")> _
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
 
<DllImport("user32.dll")> _
Private Shared Function ReleaseDC(ByVal hwnd As IntPtr, _
    ByVal hdc As IntPtr) As IntPtr
End Function
 
''' <summary>
''' プライマリスクリーンの画像を取得する
''' </summary>
''' <returns>プライマリスクリーンの画像</returns>
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
 
<StructLayout(LayoutKind.Sequential)> _
Private Structure RECT
    Public left As Integer
    Public top As Integer
    Public right As Integer
    Public bottom As Integer
End Structure
 
<DllImport("user32.dll")> _
Private Shared Function GetWindowDC(ByVal hwnd As IntPtr) As IntPtr
End Function
 
<DllImport("user32.dll")> _
Private Shared Function GetForegroundWindow() As IntPtr
End Function
 
<DllImport("user32.dll")> _
Private Shared Function GetWindowRect(ByVal hwnd As IntPtr, _
    ByRef lpRect As RECT) As Integer
End Function
 
''' <summary>
''' アクティブなウィンドウの画像を取得する
''' </summary>
''' <returns>アクティブなウィンドウの画像</returns>
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
  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
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
//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);
 
/// <summary>
/// プライマリスクリーンの画像を取得する
/// </summary>
/// <returns>プライマリスクリーンの画像</returns>
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);
 
/// <summary>
/// アクティブなウィンドウの画像を取得する
/// </summary>
/// <returns>アクティブなウィンドウの画像</returns>
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;
}

○この記事の基になった掲示板のスレッド

複数のRTFファイルを連結してRichTextBoxに表示するには?

注意

この記事の最新版は「複数の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)に表示するコードを示します。

  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
'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
 
''' <summary>
''' RichTextBoxの末尾にRTFを追加
''' </summary>
''' <param name="rtb">RTFを追加するRichTextBox</param>
''' <param name="rtfText">追加するRTF文字列</param>
Public Shared Sub AppendRtfToRichTextBox( _
    ByVal rtb As RichTextBox, ByVal rtfText As String)
    rtb.SelectionStart = rtb.TextLength
    rtb.SelectedRtf = rtfText
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
//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);
    }
}
 
/// <summary>
/// RichTextBoxの末尾にRTFを追加
/// </summary>
/// <param name="rtb">RTFを追加するRichTextBox</param>
/// <param name="rtfText">追加するRTF文字列</param>
public static void AppendRtfToRichTextBox(
    RichTextBox rtb, string rtfText)
{
    rtb.SelectionStart = rtb.TextLength;
    rtb.SelectedRtf = rtfText;
}

参考:

○この記事の基になった掲示板のスレッド

コメント



ページ情報
  • カテゴリ : .NET
  • 作成日 : 2005-03-18 (金) 06:00:00
  • 作成者 : DOBON!
  • 最終編集日 : 2010-03-22 (月) 02:06:26
  • 最終編集者 : DOBON!
[ トップ ]   [ 編集 | 凍結 | 差分 | バックアップ | 添付 | 複製 | 名前変更 | リロード ]   [ 新規 | 子ページ作成 | 一覧 | 単語検索 | 最終更新 | ヘルプ ]