DOBON.NET プログラミング道: .NET Framework, VB.NET, C#, Visual Basic, Visual Studio, インストーラ, ...

DOBON.NET

PictureBoxのImageプロパティに関するよくある勘違い

PictureBoxに画像を表示する方法として、PictureBoxのImageプロパティを使う方法と、GraphicsのDrawImageメソッドを使う方法を紹介しましたが、掲示板に寄せられる質問を拝見しますと、この2つの方法を混同し、正しく理解していない方が多いようです。例えば、次に列挙するような状況が該当します。

  1. PictureBoxのGraphicsオブジェクトを使ってPictureBoxに画像や図形を描画し、そのイメージを保存するためにPictureBoxのImageプロパティのSaveメソッドを呼び出すと、エラーが出る(もしくはうまく行かない)。
  2. PictureBoxのGraphicsオブジェクトを使ってPictureBoxに描画し、そのイメージをPictureBoxのImageプロパティで取得しようとすると、エラーが出る(もしくはうまく行かない)。
  3. PictureBoxのImageプロパティで画像を表示し、さらにPictureBoxのGraphicsオブジェクトを使ってPictureBoxに図形を描画すると、他のウィンドウに隠れた後などで、描画した図形が消えてしまう。

上記のようなトラブルは、PictureBoxのGraphicsオブジェクトを使って描画した画像や図形が、そのPictureBoxのImageプロパティに反映されると勘違いしたことに起因します。しかし実際には、PictureBoxのGraphicsオブジェクトに何を描画しようが、それがPictureBoxのImageプロパティに反映されることはありません。この2つは全く別の方法です。

つまり、このようなトラブルを解決する方法としては、以下のどちらかになるでしょう。

  1. PictureBoxのImageプロパティを一切使用しない。
  2. PictureBoxのGraphicsオブジェクト(PictureBox.PaintイベントとPictureBox.CreateGraphicsメソッド)を一切使用しない。

PictureBoxのImageプロパティを一切使用しない方法

まずは1番目の方法です。ここでは、GraphicsのDrawImageメソッドを使う方法を使ってピクチャボックスに画像や図形を描画している場合に、ピクチャボックスに表示されているイメージをどのようにしたら保存できるかという問題に絞って説明します。

これにはいろいろな方法が考えられますが、その一つは、ピクチャボックスに表示する画像をBitmapオブジェクトに描画し、PaintイベントではそのBitmapを描画するというものがあるでしょう。

以下に具体例を示します。フォームにピクチャボックスPictureBox1とボタンButton1、Button2があるものとし、Button1をクリックすることによりピクチャボックスに図形を描画し、Button2をクリックすることにより画像を保存しています。

[VB.NET]
'PictureBox1に表示する画像
Private _bmp As Bitmap = Nothing

'フォームのLoadイベントハンドラ
Private Sub Form1_Load(ByVal sender As Object, _
    ByVal e As System.EventArgs) Handles MyBase.Load
    '300x300の大きさのイメージを_bmpに設定する
    _bmp = New Bitmap(300, 300)
End Sub

'Button1のClickイベントハンドラ
Private Sub Button1_Click(ByVal sender As Object, _
    ByVal e As System.EventArgs) Handles Button1.Click
    '_bmpに設定されているイメージのGraphicsオブジェクトを取得
    Dim g As Graphics = Graphics.FromImage(_bmp)
    'イメージに描画する
    g.FillPie(Brushes.Red, 50, 50, 200, 200, 0, 360)
    'Graphicsを破棄する
    g.Dispose()

    'PictureBoxを再描画する
    PictureBox1.Invalidate()
End Sub

'Button2のClickイベントハンドラ
Private Sub Button2_Click(ByVal sender As Object, _
    ByVal e As System.EventArgs) Handles Button2.Click
    'ピクチャボックスに表示されている画像を保存
    If Not (_bmp Is Nothing) Then
        _bmp.Save("C:\test.png")
    End If
End Sub

'PictureBox1のPaintイベントハンドラ
Private Sub PictureBox1_Paint(ByVal sender As Object, _
    ByVal e As System.Windows.Forms.PaintEventArgs) _
    Handles PictureBox1.Paint
    '画像を表示する
    If Not (_bmp Is Nothing) Then
        e.Graphics.DrawImage(_bmp, 0, 0, _bmp.Width, _bmp.Height)
    End If
End Sub
[C#]
//PictureBox1に表示する画像
private Bitmap _bmp = null;

//フォームのLoadイベントハンドラ
private void Form1_Load(object sender, System.EventArgs e)
{
    //300x300の大きさのイメージを_bmpに設定する
    _bmp = new Bitmap(300, 300);
}

//Button1のClickイベントハンドラ
private void Button1_Click(object sender, System.EventArgs e)
{
    //_bmpに設定されているイメージのGraphicsオブジェクトを取得
    Graphics g = Graphics.FromImage(_bmp);
    //イメージに描画する
    g.FillPie(Brushes.Red, 50, 50, 200, 200, 0, 360);
    //Graphicsを破棄する
    g.Dispose();

    //PictureBoxを再描画する
    PictureBox1.Invalidate();
}

//Button2のClickイベントハンドラ
private void Button2_Click(object sender, System.EventArgs e)
{
    //ピクチャボックスに表示されている画像を保存
    if (_bmp != null)
    {
        _bmp.Save("C:\\test.png");
    }
}

//PictureBox1のPaintイベントハンドラ
private void PictureBox1_Paint(object sender,
    System.Windows.Forms.PaintEventArgs e)
{
    //画像を表示する
    if (_bmp != null)
    {
        e.Graphics.DrawImage(_bmp, 0, 0, _bmp.Width, _bmp.Height);
    }
}

補足:状況によっては、「ピクチャボックスに表示されている画像を取得する」も参考になるでしょう。

PictureBoxのGraphicsオブジェクトを一切使用しない方法

この場合は、PictureBoxのImageプロパティに指定されたイメージに直接描画すればよいということになります。つまり、次のようになります。

  1. PictureBoxのImageプロパティに画像(イメージ)が設定されていない時は、「画像を動的に作成する」で紹介したように新しくイメージを作成し、Imageプロパティに設定しておく。
  2. 画像を動的に作成する」で紹介したように、Graphics.FromImageメソッドにより、Imageプロパティに設定したイメージのGraphicsオブジェクトを取得し、これを使って描画する。
  3. PictureBoxのInvalidateメソッドにより、再描画する。

この方法により、ピクチャボックスに図形を描画するサンプルを以下に示します。ここではフォームにピクチャボックスPictureBox1とボタンButton1があるものとし、Button1をクリックすると、PictureBox1に赤い丸が表示されます。

[VB.NET]
'フォームのLoadイベントハンドラ
Private Sub Form1_Load(ByVal sender As Object, _
        ByVal e As EventArgs) Handles MyBase.Load
    '300x300の大きさのイメージをImageプロパティに設定する
    PictureBox1.Image = New Bitmap(300, 300)
End Sub

'Button1のClickイベントハンドラ
Private Sub Button1_Click(ByVal sender As System.Object, _
        ByVal e As System.EventArgs) Handles Button1.Click
    'Imageプロパティに設定されているイメージのGraphicsオブジェクトを取得
    Dim g As Graphics = Graphics.FromImage(PictureBox1.Image)
    'イメージに描画する
    g.FillPie(Brushes.Red, 50, 50, 200, 200, 0, 360)
    'Graphicsを破棄する
    g.Dispose()

    'PictureBoxを再描画する
    PictureBox1.Invalidate()
End Sub
[C#]
//フォームのLoadイベントハンドラ
private void Form1_Load(object sender, System.EventArgs e)
{
    //300x300の大きさのイメージをImageプロパティに設定する
    PictureBox1.Image = new Bitmap(300, 300);
}

//Button1のClickイベントハンドラ
private void Button1_Click(object sender, System.EventArgs e)
{
    //Imageプロパティに設定されているイメージのGraphicsオブジェクトを取得
    Graphics g = Graphics.FromImage(PictureBox1.Image);
    //イメージに描画する
    g.FillPie(Brushes.Red, 50, 50, 200, 200, 0, 360);
    //Graphicsを破棄する
    g.Dispose();

    //PictureBoxを再描画する
    PictureBox1.Invalidate();
}

「PictureBoxのImageプロパティを一切使用しない方法」と同じようなコードになりますが、この方法のほうがより簡単な分、融通は利きません。

注意:この記事では、基本的な事柄の説明が省略されているかもしれません。初心者の方は、特に以下の点にご注意ください。

  • このサイトで紹介されているコードの多くは、例外処理が省略されています。例外処理については、こちらをご覧ください。
  • イベントハンドラの意味が分からない、C#のコードをそのまま書いても動かないという方は、こちらをご覧ください。