グラフィック

Graphicsオブジェクトの取得

Graphicsオブジェクトのコンストラクタはpublicではないため、newでは作成できません。よって他の方法によって取得する必要があります。

ControlのPaintイベントのハンドラから

Controlの派生クラスに描画するときには、そのPaintイベントのPaintEventArgsから取得できます。

private void Form1_Paint( object sender, PaintEventArgs e )
{
    Graphics g = e.Graphics;
    g.DrawLine( Pens.Black, 0, 0, 10, 20 );
}

Controlのメソッドから

Controlの派生クラスでPaintイベント以外で描画するには、そのクラスのCreateGraphics()を呼び出します。

Graphics g = this.CreateGraphics();
g.DrawLine( Pens.Black, 0, 0, 10, 20 );

Graphicsのメソッドから

特定のImageに描画するならば、Graphics.FromImage()からそれを操作できるGraphicsを取得できます。

Image image = Image.FromFile("sample.bmp");

Graphics g = Graphics.FromImage(image);
g.DrawLine(Pens.Black, 0, 0, 10, 20);
Graphics.FromImage メソッド (Image) (System.Drawing) | MSDN

主要なオブジェクト

色 (Color)

Color構造体のプロパティはすべて読み取り専用のため、色を変更するときにはオブジェクトを作成し直します。

HSB (HSV) の値を取得するメソッドは用意されていますが、設定する方法は提供されていないため、その必要があるときには独自に実装します。Is there a built-in C#/.NET System API for HSV to RGB? - Stack Overflow

任意の色

メソッド 作成基準 備考
FromArgb(int argb) 32bitのARGB値から argbは、AARRGGBBの順
FromArgb(int alpha, Color baseColor) 8bitのアルファ値と、基となるColor構造体から  
FromArgb(int red, int green, int blue) 8bitの赤、緑、青から  
FromArgb(int alpha, int red, int green, int blue) 8bitのアルファ値、赤、緑、青から 引数はすべて0~255の範囲
Color.FromArgb メソッド (System.Drawing) | MSDN
Color red = Color.FromArgb(255, 0, 0);

定義済みの色

メソッド 作成基準
FromKnownColor(KnownColor color) KnownColor列挙型の要素から
FromName(string name) 定義済みの色の名前から (有効な名前は、KnownColor列挙型の要素の名前と同じ)
public static Color FromKnownColor(
    KnownColor color
)
Color.FromKnownColor メソッド (KnownColor) (System.Drawing) | MSDN
public static Color FromName(
    string name
)
Color.FromName メソッド (String) (System.Drawing) | MSDN
システムの色
プロパティ 対象
ActiveBorder アクティブなウィンドウ境界線
 
Highlight 選択された項目の背景色
 
Window クライアント領域の背景色
WindowText クライアント領域のテキストの色
SystemColors クラス (System.Drawing) | MSDN

すべて静的なプロパティで、SystemColors.WindowTextのように取得できます。

色の情報の取得

Color color = Color.AliceBlue; // 定義済みの色

string name = color.Name;  // "AliceBlue"
byte red = color.R;        // 240

int argbVal = color.ToArgb();                // -984833
string argb = Convert.ToString(argbVal, 16); // "fff0f8ff"

RGB値を指定して作成した色は、そのコードが名前となります。

Color color = Color.FromArgb(0x1, 0x2, 0x3);
string name = color.Name; // "ff010203"

int argbVal = color.ToArgb();
string argb = Convert.ToString(argbVal, 16);             // "ff010203"
string rgb = Convert.ToString(argbVal, 16).Remove(0, 2); // "010203"

ペン (Pen)

Pen p1 = new Pen( Color.Black );
Pen p2 = Pens.Black;

Pen p3 = SystemPens.WindowText;
Pen(Brush brush)
Pen(Brush brush, float width)

Pen(Color color)
Pen(Color color, float width)
Pen コンストラクター (System.Drawing)

widthを省略すると既定値の1.0となり、0.0と指定するとPageUnitやPageScaleに無関係に1ピクセル幅となります。

ペンなどのリソースは確実に破棄するために、Dispose()を呼ぶかusingステートメントを用います。 Pen.Dispose Method (System.Drawing) | MSDN c# - What happens if I don't call Dispose on the pen object? - Stack Overflow

using (Pen pen = new Pen(Color.Gray, 0.0f))
{
    //
}

ブラシ (Brush)

public SolidBrush(
    Color color //
)
SolidBrush コンストラクター (Color) (System.Drawing) | MSDN
public TextureBrush(
    Image bitmap //
)
TextureBrush コンストラクター (Image) (System.Drawing) | MSDN
public HatchBrush(
    HatchStyle hatchstyle, //
    Color foreColor        //
)
HatchBrush コンストラクター (HatchStyle, Color) (System.Drawing.Drawing2D) | MSDN
public LinearGradientBrush(
    Rectangle rect, //
    Color color1,   //
    Color color2,   //
    LinearGradientMode linearGradientMode //
)
LinearGradientBrush コンストラクター (Rectangle, Color, Color, LinearGradientMode) (System.Drawing.Drawing2D) | MSDN
public PathGradientBrush(
    Point[] points,   //
    WrapMode wrapMode //
)
Brush b1 = new SolidBrush( Color.White );
Brush b2 = Brushes.White;

Brush b3 = SystemBrushes.Window;

フォント (Font)

Fontクラスのプロパティはすべて読み取り専用のため、プロパティの値を変更する必要があるときには、オブジェクトを作成し直します。たとえばコントロールのフォントスタイルだけを変更するには、次のようにします。

this.Font = new Font(this.Font, FontStyle.Bold);
public Font(
    Font prototype,
    FontStyle newStyle
)
Font コンストラクター (Font, FontStyle) (System.Drawing) | MSDN

フォントサイズの指定

コントロールのフォントサイズを現在の1.5倍にするには、次のようにします。

this.Font = new Font(this.Font.FontFamily, this.Font.Size * 1.5F);
public Font(
    FontFamily family, // FontFamilyオブジェクト
    float emSize
)
Font コンストラクター (FontFamily, Single) (System.Drawing) | MSDN

フォントファミリーの指定

public Font(
    string familyName, // FontFamilyを表す文字列
    float emSize
)
Font コンストラクター (String, Single) (System.Drawing) | MSDN

フォントファミリーの名前を文字列で指定するか、FontFamilyオブジェクトを生成して渡します。

new Font("MS ゴシック", 9F);
new Font(new FontFamily("MS ゴシック"), 9F);

new Font(FontFamily.GenericSansSerif, 9F);
new Font(new FontFamily(GenericFontFamilies.SansSerif), 9F);

いずれの方法でもフォントを生成できますが、インストールされていないフォントファミリーを指定したときの挙動が異なります。

Font f1 = new Font("dummy", 1.0f);
// "Microsoft Sans Serif"が代用される

Font f2 = new Font(new FontFamily("dummy"), 1.0f);
// 「フォント 'dummy' が見つかりません。」として、System.ArgumentException例外が発生
FontFamily
public FontFamily(
    string name
)
FontFamily コンストラクター (String) (System.Drawing) | MSDN

汎用のフォントを指定するならば、GenericFontFamilies列挙型を渡します。

public FontFamily(
    GenericFontFamilies genericFamily
)
FontFamily コンストラクター (GenericFontFamilies) (System.Drawing) | MSDN
GenericFontFamilies列挙型
メンバ名 形式
Serif 一般的な、Serifオブジェクト
SansSerif 一般的な、Sans Serifオブジェクト
Monospace 一般的な、Monospaceオブジェクト
GenericFontFamilies 列挙型 (System.Drawing.Text) | MSDN

SystemFonts

プロパティ 用途 Windows 10での既定値
CaptionFont ウィンドウのタイトル バー Yu Gothic UI, 9
DefaultFont ダイアログ ボックスおよびフォームのアプリケーション (既定) MS UI Gothic, 9
DialogFont ダイアログ ボックスおよびフォームのアプリケーション MS UI Gothic, 9
IconTitleFont アイコン タイトル Yu Gothic UI, 11.25
MenuFont メニュー Yu Gothic UI, 9
MessageBoxFont メッセージ ボックス Yu Gothic UI, 9
SmallCaptionFont ツール ウィンドウなどの小さなウィンドウのタイトル バー Yu Gothic UI, 9
StatusFont ステータス バー Yu Gothic UI, 9
プロパティ - SystemFonts クラス (System.Drawing) | MSDN

矩形 (Rectangle)

図形の描画

メソッド 描画される図形
DrawArc() 円弧
DrawBezier() ベジェ曲線 (ベジエ スプライン (Bézier spline))
DrawBeziers() ベジェ曲線。一連の (ベジエ スプライン (Bézier spline))
DrawClosedCurve() カーディナル スプライン。閉じた (Cardinal spline)
DrawCurve() カーディナル スプライン。配列を通過する (Cardinal spline)
DrawEllipse() 楕円
DrawIcon() アイコン (Icon)。スケーリングされた
DrawIconUnstretched() アイコン (Icon)。スケーリングされていない
DrawImage() イメージ (Image)。スケーリングされた
DrawImageUnscaled() イメージ (Image)。スケーリングされていない
DrawImageUnscaledAndClipped() イメージ (Image)。スケーリングされずクリッピングされた
DrawLine() 直線。2つの点を結ぶ
DrawLines() 線分。配列を接続する
DrawPie() 扇形
DrawPolygon() 多角形。配列で定義された
DrawRectangle() 四角形
DrawRectangles() 四角形。一連の
DrawString() 文字列
DrawPath() GraphicsPathの図形
メソッド - Graphics クラス (System.Drawing) | MSDN
塗りつぶしを伴う描画
メソッド 描画される図形
FillClosedCurve() カーディナル スプライン。閉じた
FillEllipse() 楕円
FillPie() 扇形
FillPolygon() 多角形
FillRectangle() 四角形
FillRectangles() 四角形。一連の
FillRegion() 四角形とパスから成る図形
FillPath() GraphicsPathの図形

直線の描画

1本の直線はDrawLine()で、連続した直線はDrawLines()で描画します。また終了点を開始点に接続するならば、DrawPolygon()を用います。

Graphics g = e.Graphics;

g.DrawLine(new Pen(ForeColor), 10, 0, 20, 30);

Point[] points = {
    new Point(10,0),
    new Point(20,30),
    new Point(0,10),
};
g.DrawLines(new Pen(ForeColor), points);
public void DrawLine(
    Pen pen,
    int x1,
    int y1,
    int x2,
    int y2
)
Graphics.DrawLine メソッド (Pen, Int32, Int32, Int32, Int32) (System.Drawing) | MSDN
public void DrawLines(
    Pen pen,
    Point[] points
)
Graphics.DrawLines メソッド (Pen, Point[]) (System.Drawing) | MSDN
public void DrawPolygon(
    Pen pen,
    Point[] points
)
Graphics.DrawPolygon メソッド (Pen, Point[]) (System.Drawing) | MSDN

楕円の描画

public void DrawEllipse(
    Pen pen,
    int x,
    int y,
    int width,
    int height
)
public void DrawEllipse(
    Pen pen,
    Rectangle rect
)
Graphics.DrawEllipse メソッド (System.Drawing) - desc | MSDN

たとえばフォームのクライアント領域に内接する円を描くならば、次のようにします。

Graphics g = e.Graphics;
g.DrawEllipse(Pens.Red, form.ClientRectangle);

四角形の描画

public void DrawRectangle(
    Pen pen,
    int x,     // 描画する四角形の左上のx座標
    int y,     // 描画する四角形の左上のy座標
    int width, // 描画する四角形の幅
    int height // 描画する四角形の高さ
)
Graphics.DrawRectangle メソッド (Pen, Int32, Int32, Int32, Int32) (System.Drawing) | MSDN
public void FillRectangle(
    Brush brush,
    int x,     // 塗りつぶす四角形の左上のx座標
    int y,     // 塗りつぶす四角形の左上のy座標
    int width, // 塗りつぶす四角形の幅
    int height // 塗りつぶす四角形の高さ
)
Graphics.FillRectangle メソッド (Brush, Int32, Int32, Int32, Int32) (System.Drawing) | MSDN

DrawRectangle()ではペンの幅が影響するため、描画される位置や幅がFillRectangle()と異なることがあります。

Graphics g = e.Graphics;
g.ScaleTransform(10.0f, 10.0f);

g.FillRectangle(Brushes.Black, 10, 10, 1, 1); // 下図の1列目に描画
g.FillRectangle(Brushes.Black, 10, 20, 2, 2);

g.DrawRectangle(Pens.Black, 20, 10, 1, 1); // 下図の2列目に描画
g.DrawRectangle(Pens.Black, 20, 20, 2, 2);

using (Pen pen = new Pen(Color.Black, 0.0f))
{
    g.DrawRectangle(pen, 30, 10, 1, 1); // 下図の3列目に描画
    g.DrawRectangle(pen, 30, 20, 2, 2);
}


出力例

点の描画

点を描画するメソッドは用意されていないため、1ピクセル幅の四角形を描画することで代用します。c# - Draw a single pixel on Windows Forms - Stack Overflow

g.FillRectangle(Brushes.Black, x, y, 1, 1);

またはBitmapを作成し、それのSetPixel()でピクセルの色を指定することで実現します。

Bitmap bitmap = new Bitmap(100, 100);
bitmap.SetPixel(10, 10, Color.Red);

Graphics g = e.Graphics;
g.DrawImage(bitmap, 0, 0);

イメージ (Image)

Imageクラスの生成

メソッド 読み込み元
FromFile(String) ファイル
FromHbitmap(IntPtr) GDI ビットマップ ハンドル
FromStream(Stream) ストリーム
メソッド - Image クラス (System.Drawing) | MSDN
Image image = Image.FromFile("sample.bmp");

Graphics g = e.Graphics;
g.DrawImage(image, 0, 0);

Graphics.FromImage()からGraphicsを取得することで、Imageに対して描画できます。c# - how to draw a line on a image? - Stack Overflow

イメージの描画

メソッド 描画範囲と形状
DrawImage(Image, Point) 全体が、元のサイズで描画
DrawImage(Image, Point[]) 全体が、指定の3点を頂点とする平行四辺形の形状にスケーリングされて描画
DrawImage(Image, Rectangle) 全体が、指定の四角形の形状にスケーリングされて描画
DrawImage(Image, Rectangle, Rectangle, GraphicsUnit) 指定部分が、指定の四角形の形状にスケーリングされて描画
Graphics.DrawImage メソッド (System.Drawing) | MSDN
public void DrawImage(
    Image image,
    Rectangle destRect,
    int srcX,
    int srcY,
    int srcWidth,
    int srcHeight,
    GraphicsUnit srcUnit,             // 切り取り部を定義するための単位
    ImageAttributes imageAttrs,       // 変色とガンマ情報
    Graphics.DrawImageAbort callback, // 描画時に呼び出されるコールバック
    IntPtr callbackData               // このメソッドの停止を決定するためのコールバックの追加データ
)
Graphics.DrawImage メソッド (Image, Rectangle, Int32, Int32, Int32, Int32, GraphicsUnit, ImageAttributes, Graphics.DrawImageAbort, IntPtr) (System.Drawing) | MSDN

画像の一部の切り取り (クリッピング) だけをするならば、スケーリングを伴わないDrawImageUnscaledAndClipped()を用います。

public void DrawImageUnscaledAndClipped(
    Image image,
    Rectangle rect
)
Graphics.DrawImageUnscaledAndClipped メソッド (Image, Rectangle) (System.Drawing) | MSDN

イメージの加工

RotateFlip()

イメージを回転、反転、またはそれを同時に行えます。

public void RotateFlip(
    RotateFlipType rotateFlipType
)
Image.RotateFlip メソッド (RotateFlipType) (System.Drawing) | MSDN

ピクセル単位の操作

Bitmapクラスで、ピクセル単位でイメージを操作できます。

主なコンストラクタ
メソッド 読み込み元
Bitmap(Image) 他のImage
Bitmap(Stream) ストリーム
Bitmap(String) ファイル
Bitmap(Type, String) リソース
Bitmap(Int32, Int32) 読み込まず、新規に作成
Bitmap コンストラクター (System.Drawing) | MSDN
public Bitmap(
    string filename
)
Bitmap コンストラクター (String) (System.Drawing) | MSDN

対応するファイル形式は次の通りです。グラフィックス ファイル形式 - ビットマップの種類 | MSDN

  • BMP
  • GIF (Graphics Interchange Format)
  • JPEG (Joint Photographic Experts Group)
  • EXIF (Exchangeable Image File)
  • PNG (Portable Network Graphics)
  • TIFF (Tag Image File Format)

非対応のファイルを読ませるとSystem.ArgumentException例外が発生します。

Bitmap bitmap = new Bitmap("sample.bmp");

Graphics g = e.Graphics;
g.DrawImage(bitmap, 0, 0);

プロパティ

プロパティ 内容
ImageFormat RawFormat ファイル形式
int Width  
int Height  
プロパティ - Bitmap クラス (System.Drawing) | MSDN

データ形式の変換

ImageConverterクラスで、Imageクラスのオブジェクトを他のデータ形式と相互に変換できます。

メソッド

public object ConvertTo(
    object value,
    Type destinationType
)
TypeConverter.ConvertTo メソッド (Object, Type) (System.ComponentModel) | MSDN

指定のオブジェクトを、指定の型に変換できます。

Bitmap bitmap = new Bitmap("sample.bmp");

ImageConverter imageConverter = new ImageConverter();
byte[] bytes = (byte[])imageConverter.ConvertTo(bitmap, typeof(byte[]));

座標系の変換

  • ワールド変換 (ワールド座標 → ページ座標)
    • TranslateTransform()
    • ScaleTransform()
    • RotateTransform()
  • ページ変換 (ページ座標 → デバイス座標)
    • PageUnit
    • PageScale

ワールド変換

public void TranslateTransform(
    float dx, // x軸方向の移動量
    float dy  // y軸方向の移動量
)
Graphics.TranslateTransform メソッド (Single, Single) (System.Drawing) | MSDN
public void ScaleTransform(
    float sx, // x軸方向の拡大率
    float sy  // y軸方向の拡大率
)
Graphics.ScaleTransform メソッド (Single, Single) (System.Drawing) | MSDN

拡大率を負数とすることで、軸を反転できます。たとえばsyを-1.0とするとy軸の正の方向が上向きとなり、グラフの描画に便利です。ただし軸を反転させるだけではy軸の正の領域が描画範囲から外れるため、必要ならばTranslateTransform()でy軸方向に移動させます。

スケールを変更すると直線などの幅にも影響するため、それが期待するものでなければペンの幅を0.0とします。

public void RotateTransform(
    float angle // 回転角度
)
Graphics.RotateTransform メソッド (Single) (System.Drawing) | MSDN

Imageを継承したクラスを90度単位で回転させるならば、RotateFlip()で可能です。

原点の変更

たとえばフォーム左下が(-1.0,-1.0)、右上が(1.0,1.0)で、中央が(0,0)となるように座標を変換するには、次のようにします。

float width = 2.0f;
float height = -2.0f;

Graphics g = e.Graphics;
g.ScaleTransform(form.ClientSize.Width / width, form.ClientSize.Height / height);
g.TranslateTransform(width / 2.0f, height / 2.0f); // フォームの中央が(0,0)となるように移動


using (Pen pen = new Pen(Color.Black, 0.0f))
{
    g.DrawLine(pen, -1.0f, 0.0f, 1.0f, 0.0f); // x軸の描画
    g.DrawLine(pen, 0.0f, -1.0f, 0.0f, 1.0f); // y軸の描画
}

グローバル変換とローカル変換

  • グローバル変換 … Graphicsオブジェクトで描画し、すべての項目に適用
  • ローカル変換 … GraphicsPathオブジェクトなどで描画し、特定の項目に適用
グローバル変換とローカル変換 | MSDN
グローバル変換 (Global Transformations)

前述の、Graphics.TranslateTransform()メソッドなどで変換します。

ローカル変換 (Local Transformations)

GraphicsPathに直線セグメントなどを追加し、それに変換行列を乗じます。そしてGraphics.DrawPath()などで描画します。ローカル変換 - グローバル変換とローカル変換 | MSDN

GraphicsPath graphicsPath = new GraphicsPath();
graphicsPath.AddLine(10, 20, 10, 40);

Matrix matrix = new Matrix();
matrix.Scale(2.0f, 1.0f);
matrix.Translate(0.0f, -1.0f);
graphicsPath.Transform(matrix);

Graphics g = e.Graphics;
g.DrawPath(new Pen(ForeColor), graphicsPath);

アンチエイリアシング (Antialiasing)

GraphicsのSmoothingModeプロパティにAntiAliasまたはHighQualityを指定することで、以降描画する画像にアンチエイリアシングが適用されます。

e.Graphics.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.AntiAlias;

トラブル対処法

描画時にちらつく

次の3つの解決法があります。

  1. ダブル バッファリングを有効にする。
  2. ControlStyles.Opaqueをtrueに設定し、背景の消去を無効にする。
  3. 親クラスのOnPaintBackground()が呼び出されないようにし、背景の消去を無効にする。

ダブル バッファリングを有効にするには、DoubleBufferedプロパティをtrueとするだけです。ダブル・バッファリングにより描画を行うには?(DoubleBuffered編)[2.0のみ、C#、VB] - @IT 遠藤孝信 (2006/05/19)

SetStyle()がprotectedのため、ControlStyles.OpaqueをtrueにするにはControlを継承したクラスでしか用いられません。.NET TIPS:背景の描画を禁止して再描画時のちらつきをなくすには? - @IT 遠藤孝信 (2004/08/19)

コントロールのリサイズ時に一部しか再描画されない

次の2つの解決法があります。

  1. ControlStyles.ResizeRedrawをtrueに設定し、コントロールのリサイズ時に全体が再描画されるようにする。
  2. Resizeイベント発生時にInvalidate()で描画領域の全体を無効化し、全体が再描画されるようにする。
MSDN (Microsoft Developer Network) から検索