質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

88.32%

C# 2つのpictureBoxを一つに重ねるやり方について

解決済

回答 1

投稿

  • 評価
  • クリップ 1
  • VIEW 12K+

yuzusenbei

score 19

前提・実現したいこと

C#,visual studioを使ってお絵かきソフトを作成しています。
直線描画(クリック中は軌跡が表示される)が出来るようにしたいのですが、1本目は問題なく描画できるのですが、2本目を描画しようとすると(マウスムーブ時)にpictureBoxをリフレッシュするため前回描画した線が消えてしまいます。そこで透明なpictureBoxを上に重ねて描画後に合成したいと思っていますがどのようにすればわからずにつまずいております。この方法が可能であればやり方を、もっと良いやり方があればそちらも併せて教えていただけると助かります。どうぞよろしくお願いいたします。

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;

namespace 直線描画
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();

        }
        private void Form1_Load(object sender, System.EventArgs e)
        {
            // Graphics オブジェクトの取得
            _bitmap = new Bitmap(Screen.PrimaryScreen.WorkingArea.Width,
                                           Screen.PrimaryScreen.WorkingArea.Height);
            pictureBox1.Parent = pictureBox2;

        }
        Graphics grfx;  // Graphics オブジェクト
        int start = 0;  // 1 = 始点 確定
        int startX;     // Line X 始点
        int startY;     // Line Y 始点
        int end = 0;    // 1 = 終点 確定
        int endX;       // Line X 終点
        int endY;       // Line Y 終点
        Bitmap _bitmap = null;

        private void pictureBox1_MouseDown(object sender, MouseEventArgs e)
        {
            grfx = Graphics.FromImage(_bitmap);
            start = 1;
            startX = e.X;
            startY = e.Y;

        }

        private void pictureBox1_MouseMove(object sender, MouseEventArgs e)
        {
            if (start == 0) return;
            end = 1;
            endX = e.X;
            endY = e.Y;
            pictureBox1.Refresh();  // マウスのドラッグ中の直線表示を要求
        }

        private void pictureBox1_MouseUp(object sender, MouseEventArgs e)
        {
            start = 0;
            if (end == 0) return;
            end = 0;

            grfx.DrawLine(Pens.Magenta, startX, startY, endX, endY);
            //pictureBox1.Refresh();
            grfx.Dispose();
        }

         private void pictureBox1_Paint(object sender, PaintEventArgs e)
        {
            // マウスのドラッグ中の直線表示
            if (end == 0) return;
            e.Graphics.DrawLine(Pens.Magenta, startX, startY, endX, endY);
        }

    }
}

  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 過去に投稿した質問と同じ内容の質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 1

checkベストアンサー

0

PictureBoxImageプロパティに設定したビットマップを表示するためのコントロールです。ビットマップを表示するには、Paintイベントで何か描くのではなく、Imageプロパティに設定したビットマップに描画してください。そうすれば、Paintイベントで自動的に表示されます。
Imageを設定せずにPaintイベントで何か描いたとしても、それはどこにも残らないので、次のPaintイベント実行時に消されてしまいます。

とりあえず、各メソッドをこのようにすれば線が描かれると思います(意図通りかは判りませんが)。
Paintイベントは削除して、MouseUpイベントを追加してください。

private void Form1_Load(object sender, System.EventArgs e)
{
    pictureBox1.Image = new Bitmap(pictureBox1.ClientRectangle.Width, pictureBox1.ClientRectangle.Height);
    grfx = Graphics.FromImage(pictureBox1.Image);
}

private void pictureBox1_MouseDown(object sender, MouseEventArgs e)
{
    start = 1;
    startX = e.X;
    startY = e.Y;
}

private void pictureBox1_MouseMove(object sender, MouseEventArgs e)
{
    if(start == 0)
        return;
    grfx.DrawLine(Pens.Magenta, startX, startY, e.X, e.Y);
    startX = e.X;
    startY = e.Y;
    pictureBox1.Invalidate();
}

private void pictureBox1_MouseUp(object sender, MouseEventArgs e)
{
    start = 0;
}

もしかしてこういうことかと思い、ちょっとプログラムを手直ししてみました。
マウスボタンを押した位置から離した位置を直線で結ぶような感じです。2枚の絵を合成する方法で描いています。

private void Form1_Load(object sender, EventArgs e)
{
    int width = pictureBox1.ClientRectangle.Width;
    int height = pictureBox1.ClientRectangle.Height;
    pictureBox1.Image = new Bitmap(width, height);
    imageBg = new Bitmap(width, height);
    imageFg = new Bitmap(width, height);
    grfx = Graphics.FromImage(pictureBox1.Image);
    graphBg = Graphics.FromImage(imageBg);
    graphFg = Graphics.FromImage(imageFg);
    // とりあえず背景を白でクリア。これをやらないとうまく合成できない
    graphBg.Clear(Color.White);
    grfx.DrawImage(imageBg, 0, 0);
}

Bitmap imageBg;     // 背景となるイメージ
Bitmap imageFg;     // 手前のイメージ。ドラッグ中はここに線を描画する
Graphics graphBg;   // 背景描画用
Graphics graphFg;   // 手前描画用

Graphics grfx;  // Graphics オブジェクト 
int start = 0;  // 1 = 始点 確定 
int startX;     // Line X 始点 
int startY;     // Line Y 始点 
int end = 0;    // 1 = 終点 確定 
int endX;       // Line X 終点 
int endY;       // Line Y 終点 

private void pictureBox1_MouseDown(object sender, MouseEventArgs e)
{
    start = 1;
    startX = e.X;
    startY = e.Y;
}

private void pictureBox1_MouseMove(object sender, MouseEventArgs e)
{
    if(start == 0)
        return;
    endX = e.X;
    endY = e.Y;
    // 手前のイメージを透明でクリアしてから線を描画
    graphFg.Clear(Color.FromArgb(0));
    graphFg.DrawLine(Pens.Magenta, startX, startY, e.X, e.Y);
    // 背景と合成してPictureBoxのイメージに描画
    grfx.DrawImage(imageBg, 0, 0);
    grfx.DrawImage(imageFg, 0, 0);
    pictureBox1.Invalidate();
}

private void pictureBox1_MouseUp(object sender, MouseEventArgs e)
{
    start = 0;
    // 背景のイメージに手前のイメージを合成。線が確定
    graphBg.DrawImage(imageFg, 0, 0);
}

PictureBoxが重ねられないと勘違いしていたので、改めてプログラムを手直ししてみました。
PictureBox2枚重ね版です(pictureBox1が手前で2が背景)。前より少しだけコード量が少なくなりますね。

private void Form1_Load(object sender, EventArgs e)
{
    pictureBox1.Parent = pictureBox2;
    pictureBox1.Size = pictureBox2.Size;
    pictureBox1.Location = new Point(0, 0);
    pictureBox1.BackColor = Color.Transparent;

    int width = pictureBox1.ClientRectangle.Width;
    int height = pictureBox1.ClientRectangle.Height;
    pictureBox1.Image = new Bitmap(width, height);
    pictureBox2.Image = new Bitmap(width, height);

    grfxFg = Graphics.FromImage(pictureBox1.Image);
    grfxBg = Graphics.FromImage(pictureBox2.Image);
}

Graphics grfxBg;  // Graphics オブジェクト 
Graphics grfxFg;  // Graphics オブジェクト 
int start = 0;  // 1 = 始点 確定 
int startX;     // Line X 始点 
int startY;     // Line Y 始点 
int end = 0;    // 1 = 終点 確定 
int endX;       // Line X 終点 
int endY;       // Line Y 終点 

private void pictureBox1_MouseDown(object sender, MouseEventArgs e)
{
    start = 1;
    startX = e.X;
    startY = e.Y;
}

private void pictureBox1_MouseMove(object sender, MouseEventArgs e)
{
    if(start == 0)
        return;
    endX = e.X;
    endY = e.Y;
    // 手前のイメージを透明でクリアして線を描画
    grfxFg.Clear(Color.FromArgb(0));
    grfxFg.DrawLine(Pens.Magenta, startX, startY, e.X, e.Y);
    pictureBox1.Invalidate();
}

private void pictureBox1_MouseUp(object sender, MouseEventArgs e)
{
    start = 0;
    // 背景のイメージに手前のイメージを合成。線が確定
    grfxBg.DrawImage(pictureBox1.Image, 0, 0);
}

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2016/06/03 19:42

    PictureBox重ねて表示できますね。勘違いしていました。当該部分は訂正しておきます。

    キャンセル

  • 2016/06/03 20:45

    とても丁寧な回答ありがとうございます。すごく参考になりました。
    Imageプロパティ、Bitmapに描画するところの曖昧なところを理解することが出来ました。

    キャンセル

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 88.32%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

関連した質問

同じタグがついた質問を見る