Delphi Programming / Object Pascal
サイトのトップページへ リンクのページへ ロゴマーク
[掲載 2017年05月24日] [更新 2017年05月24日] GDI+ 関係サンプル
GDI+ 関係サンプル 
G016_直線の端点を矢印にする
動作確認等 Windows 7 U64(SP1) + Delphi XE(UP1) Pro
G016_GDIPlus_ArrowLine.zip [2,424 KB] 2017年05月24日版 (EXE 同梱)







01_TGPAdjustableArrowCap を使用した矢印付き直線の描画


GDI+ で矢印付きの直線を描画するには TGPAdjustableArrowCap が利用できます.
  • 矢印の幅と高さを指定して TGPAdjustableArrowCap オブジェクトを生成
  • TGPPen の SetCustomStartCap または SetCustomEndCap メソッドで設定
  • その TGPPen を使用して TGPGraphics.DrawLine メソッドで線を描画
という手順になります.始点と終点の矢印の形状は別々に設定できます.矢印の三角形の高さに相当するのが矢印の高さです.三角形の底辺の長さが矢印の幅になります.この値は,線の太さ (幅) の倍数で指定します.したがって,線の太さが変わると自動的に矢印のサイズも変わります.


[備考]
連続した直線は TGPGraphic.DrawLines で描画できます.
パスの場合は TGPGraphicsPath.AddLines で作成できます.座標値の配列を使用する場合は以下の記事を参考にしてください.静的配列の使用例と動的配列の使用法の説明があります.



図1
実行結果

リスト1
TGPAdjustableArrowCap を使用すると矢印付きの直線を描画できる
unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, ExtCtrls, StdCtrls, GDIPOBJ, GDIPAPI, GDIPUTIL;

type
  TMyPanel = class(TPanel)
  protected
    procedure Paint; override;
    procedure DrawImage(ACanvas: TCanvas);
  end;

  TForm1 = class(TForm)
    procedure FormCreate(Sender: TObject);
  private
    { Private 宣言 }
    MyPanel : TMyPanel;
  public
    { Public 宣言 }
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

//=============================================================================
//  フォーム生成時の処理
//=============================================================================
procedure TForm1.FormCreate(Sender: TObject);
begin
  Self.Caption := 'GDI+  矢印';

  //TMyPanelを生成して配置
  MyPanel := TMyPanel.Create(Self);
  with MyPanel do begin
    Parent  := Self;
    Left    := 5;
    Top     := 35;
    Width   := Self.ClientWidth  - 10;
    Height  := Self.ClientHeight - Top - 5;
    Anchors := [akLeft, akTop, akRight, akBottom];
    Color   := clWhite;
    ParentBackground := False;
    DoubleBuffered   := True;
  end;
end;

{ TMyPanel }

//-----------------------------------------------------------------------------
//  実際の描画コード
//  直線を描画する時の端点の形状(ャップ)を矢印にする
//
//  ACanvas : 描画先のTCanas
//-----------------------------------------------------------------------------
procedure TMyPanel.DrawImage(ACanvas: TCanvas);
var
  LGPGraphics   : TGPGraphics;
  LGPPen        : TGPPen;
  LGPStartArrow : TGPAdjustableArrowCap;
  LGPEndArrow   : TGPAdjustableArrowCap;
  LLineLength   : Integer;
  LPosX         : Integer;
  LPosY         : Integer;
begin
  //直線の長さと描画開始X座標値
  LLineLength := 280;
  LPosX       := 20;

  //始点と終端の矢印のオブジェクトを生成
  //最初の引数は矢印の高さ(三角形の高さ)
  //次の引数は矢印の幅(三角形の底辺の値)
  //これらの値は線の太さの倍数で指定する
  //最後の引数がFalseの時は塗り潰しなし

  LGPStartArrow := TGPAdjustableArrowCap.Create(8, 8, False);
  LGPEndArrow   := TGPAdjustableArrowCap.Create(8, 8, True);

  //線を描画するために使用するTGPPenの生成
  //生成した端点形状の情報をセット
  LGPPen := TGPPen.Create(MakeColor(255, 0, 0, 0));
  LGPPen.SetCustomStartCap(LGPStartArrow);
  LGPPen.SetCustomEndCap(LGPEndArrow);

  //描画先を指定してTGPGraphics生成
  LGPGraphics := TGPGraphics.Create(ACanvas.Handle);

  //TGPPenを使用して線を描画
  LPosY := 20;
  LGPGraphics.DrawLine(LGPPen, LPosX, LPosY, LPosX + LLineLength, LPosY);

  //線の太さを変更して描画
  LPosY := 70;
  LGPPen.SetWidth(5.0);
  LGPGraphics.DrawLine(LGPPen, LPosX, LPosY, LPosX + LLineLength, LPosY);

  //線の太さを変更して描画
  LPosY := 150;
  LGPPen.SetWidth(10.0);
  LGPGraphics.DrawLine(LGPPen, LPosX, LPosY, LPosX + LLineLength, LPosY);


   LGPStartArrow.Free;
   LGPEndArrow.Free;
   LGPPen.Free;
   LGPGraphics.Free;
end;

//-----------------------------------------------------------------------------
//  TMyPanelのPaintメソッド
//  CanvasをTImage.CanvasにすればTImageへの描画となる
//-----------------------------------------------------------------------------
procedure TMyPanel.Paint;
begin
  inherited;
  DrawImage(Canvas);
end;

end.




02_矢印の幅と高さを変更する


TSpinEdit の値で矢印の幅と高さを設定して,その結果を確認できるようにしたサンプルです.



図2
実行開始時の画面
図3
設定例
  • 左の TSpinEdit が矢印の幅 (三角形の底辺の寸法)
  • 右の TSpinEdit が高さの設定用
  • これらは線の太さの倍数値 (Single 型)

リスト2
矢印の幅と高さを変更して結果を確認するサンプル
unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, ExtCtrls, StdCtrls, GDIPOBJ, GDIPAPI, GDIPUTIL, Spin, ExtDlgs;

type
  TMyPanel = class(TPanel)
  private
    FArrowWidth  : Integer;
    FArrowHeight : Integer;
    procedure SetFArrowWidth(const Value: Integer);
    procedure SetFArrowHeight(const Value: Integer);
  protected
    procedure Paint; override;
    procedure DrawImage(ACanvas: TCanvas);
  public
    constructor Create(AOwner: TComponent); override;
    property ArrowWidth  : Integer read FArrowWidth  write SetFArrowWidth;
    property ArrowHeight : Integer read FArrowHeight write SetFArrowHeight;
  end;

  TForm1 = class(TForm)
    SpinEdit1: TSpinEdit;
    SpinEdit2: TSpinEdit;
    OpenPictureDialog1: TOpenPictureDialog;
    procedure FormCreate(Sender: TObject);
    procedure SpinEdit1Change(Sender: TObject);
    procedure SpinEdit2Change(Sender: TObject);
  private
    { Private 宣言 }
    MyPanel : TMyPanel;
  public
    { Public 宣言 }
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

//=============================================================================
//  フォーム生成時の処理
//=============================================================================
procedure TForm1.FormCreate(Sender: TObject);
begin
  Self.Caption := 'GDI+  矢印';

  //TMyPanelを生成して配置
  MyPanel := TMyPanel.Create(Self);
  with MyPanel do begin
    Parent  := Self;
    Left    := 5;
    Top     := 35;
    Width   := Self.ClientWidth  - 10;
    Height  := Self.ClientHeight - Top - 5;
    Anchors := [akLeft, akTop, akRight, akBottom];
    Color   := clWhite;
    ParentBackground := False;
    DoubleBuffered   := True;
  end;

  SpinEdit1.Value := MyPanel.ArrowWidth;
  SpinEdit2.Value := MyPanel.ArrowHeight;
end;

//=============================================================================
//  SpinEdit1のOnChangeイベント処理
//  TMyPanelのArrowWidthプロパティ設定
//=============================================================================
procedure TForm1.SpinEdit1Change(Sender: TObject);
begin
  MyPanel.ArrowWidth := SpinEdit1.Value;
end;

//=============================================================================
//  SpinEdit2のOnChangeイベント処理
//  TMyPanelのArrowHeightプロパティ設定
//=============================================================================
procedure TForm1.SpinEdit2Change(Sender: TObject);
begin
  MyPanel.ArrowHeight := SpinEdit2.Value;
end;

{ TMyPanel }

//-----------------------------------------------------------------------------
//  TMyPanelのコントラクタ
//-----------------------------------------------------------------------------
constructor TMyPanel.Create(AOwner: TComponent);
begin
  inherited;

  FArrowWidth  := 8;
  FArrowHeight := 8;
end;

//-----------------------------------------------------------------------------
//  TMyPanelのArrowWidthプロパティ設定用メソッド
//  矢印の幅(三角形の底辺の長さ)の設定
//  この値は線の太さの倍数値
//-----------------------------------------------------------------------------
procedure TMyPanel.SetFArrowWidth(const Value: Integer);
begin
  if FArrowWidth <> Value then begin
    FArrowWidth := Value;
    Invalidate;
  end;
end;

//-----------------------------------------------------------------------------
//  TMyPanelのArrowHeightプロパティ設定用メソッド
//  矢印の高さ(三角形の高さ)の設定
//  この値は線の太さの倍数値
//-----------------------------------------------------------------------------
procedure TMyPanel.SetFArrowHeight(const Value: Integer);
begin
  if FArrowHeight <> Value then begin
    FArrowHeight := Value;
    Invalidate;
  end;
end;

//-----------------------------------------------------------------------------
//  実際の描画コード
//  直線を描画する時の端点の形状(ャップ)を矢印にする
//
//  ACanvas : 描画先のTCanas
//-----------------------------------------------------------------------------
procedure TMyPanel.DrawImage(ACanvas: TCanvas);
var
  LGPGraphics   : TGPGraphics;
  LGPPen        : TGPPen;
  LGPStartArrow : TGPAdjustableArrowCap;
  LGPEndArrow   : TGPAdjustableArrowCap;
  LLineLength   : Integer;
  LPosX         : Integer;
  LPosY         : Integer;
begin
  //直線の長さと描画開始X座標値
  LLineLength := 280;
  LPosX       := 20;

  //始点と終端の矢印のオブジェクトを生成
  //最後の引数がFalseの時は塗り潰しなし
  LGPStartArrow := TGPAdjustableArrowCap.Create(FArrowHeight, FArrowWidth, False);
  LGPEndArrow   := TGPAdjustableArrowCap.Create(FArrowHeight, FArrowWidth, True);

  //線を描画するために使用するTGPPenの生成
  //生成した端点形状の情報をセット
  LGPPen := TGPPen.Create(MakeColor(255, 0, 0, 0));
  LGPPen.SetCustomStartCap(LGPStartArrow);
  LGPPen.SetCustomEndCap(LGPEndArrow);

  //描画先を指定してTGPGraphics生成
  LGPGraphics := TGPGraphics.Create(ACanvas.Handle);

  //線の太さを指定して描画
  LPosY := 50;
  LGPPen.SetWidth(5.0);
  LGPGraphics.DrawLine(LGPPen, LPosX, LPosY, LPosX + LLineLength, LPosY);

  //線の太さを変更して描画
  LPosY := 150;
  LGPPen.SetWidth(10.0);
  LGPGraphics.DrawLine(LGPPen, LPosX, LPosY, LPosX + LLineLength, LPosY);

  LGPStartArrow.Free;
  LGPEndArrow.Free;
  LGPPen.Free;
  LGPGraphics.Free;
end;

//-----------------------------------------------------------------------------
//  TMyPanelのPaintメソッド
//  CanvasをTImage.CanvasにすればTImageへの描画となる
//-----------------------------------------------------------------------------
procedure TMyPanel.Paint;
begin
  inherited;
  DrawImage(Canvas);
end;

end.




03_矢印のインセット ( 底辺の切り込み ) の深さを変更する


矢印の底辺に切り込み (インセット) を入れることができます.
このインセットの値も線の太さの倍数指定します.TMyPanel には矢印の幅と高さのプロパティも実装していますが,本サンプルはインセットの値だけのテストにしています.


図4
実行開始時の画面
図5
設定例

リスト3
矢印の底辺部の切り込みのサイズ (深さ) を設定して結果を確認するサンプル
unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, ExtCtrls, StdCtrls, GDIPOBJ, GDIPAPI, GDIPUTIL, Spin, ExtDlgs;

type
  TMyPanel = class(TPanel)
  private
    FArrowWidth  : Integer;
    FArrowHeight : Integer;
    FArrowMiddle : Integer;
    procedure SetFArrowWidth(const Value: Integer);
    procedure SetFArrowHeight(const Value: Integer);
    procedure SetFArrowMiddle(const Value: Integer);
  protected
    procedure Paint; override;
    procedure DrawImage(ACanvas: TCanvas);
  public
    constructor Create(AOwner: TComponent); override;
    property ArrowWidth  : Integer read FArrowWidth  write SetFArrowWidth;
    property ArrowHeight : Integer read FArrowHeight write SetFArrowHeight;
    property ArrowMiddle : Integer read FArrowMiddle write SetFArrowMiddle;
  end;

  TForm1 = class(TForm)
    SpinEdit1: TSpinEdit;
    OpenPictureDialog1: TOpenPictureDialog;
    procedure FormCreate(Sender: TObject);
    procedure SpinEdit1Change(Sender: TObject);
  private
    { Private 宣言 }
    MyPanel : TMyPanel;
  public
    { Public 宣言 }
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

//=============================================================================
//  フォーム生成時の処理
//=============================================================================
procedure TForm1.FormCreate(Sender: TObject);
begin
  Self.Caption := 'GDI+  矢印';

  //TMyPanelを生成して配置
  MyPanel := TMyPanel.Create(Self);
  with MyPanel do begin
    Parent  := Self;
    Left    := 5;
    Top     := 35;
    Width   := Self.ClientWidth  - 10;
    Height  := Self.ClientHeight - Top - 5;
    Anchors := [akLeft, akTop, akRight, akBottom];
    Color   := clWhite;
    ParentBackground := False;
    DoubleBuffered   := True;
  end;

  SpinEdit1.Value := MyPanel.ArrowMiddle;
end;

//=============================================================================
//  SpinEdit1のOnChangeイベント処理
//  TMyPanelのArrowMiddleプロパティ設定
//=============================================================================
procedure TForm1.SpinEdit1Change(Sender: TObject);
begin
  MyPanel.ArrowMiddle := SpinEdit1.Value;//
end;

{ TMyPanel }

//-----------------------------------------------------------------------------
//  TMyPanelのコントラクタ
//-----------------------------------------------------------------------------
constructor TMyPanel.Create(AOwner: TComponent);
begin
  inherited;

  FArrowWidth  := 8;
  FArrowHeight := 8;
  FArrowMiddle := 0;
end;

//-----------------------------------------------------------------------------
//  TMyPanelのArrowWidthプロパティ設定用メソッド
//  矢印の幅(三角形の底辺の長さ)の設定
//  この値は線の太さの倍数値
//-----------------------------------------------------------------------------
procedure TMyPanel.SetFArrowWidth(const Value: Integer);
begin
  if FArrowWidth <> Value then begin
    FArrowWidth := Value;
    Invalidate;
  end;
end;

//-----------------------------------------------------------------------------
//  TMyPanelのArrowHeightプロパティ設定用メソッド
//  矢印の高さ(三角形の高さ)の設定
//  この値は線の太さの倍数値
//-----------------------------------------------------------------------------
procedure TMyPanel.SetFArrowHeight(const Value: Integer);
begin
  if FArrowHeight <> Value then begin
    FArrowHeight := Value;
    Invalidate;
  end;
end;

//-----------------------------------------------------------------------------
//  TMyPanelのArrowMiddleプロパティ設定用メソッド
//  矢印のインセット(切り込み)の設定
//  この値は線の太さの倍数値
//-----------------------------------------------------------------------------
procedure TMyPanel.SetFArrowMiddle(const Value: Integer);
begin
  if FArrowMiddle <> Value then begin
    FArrowMiddle := Value;
    Invalidate;
  end;
end;

//-----------------------------------------------------------------------------
//  実際の描画コード
//  直線を描画する時の端点の形状(ャップ)を矢印にする
//
//  ACanvas : 描画先のTCanas
//-----------------------------------------------------------------------------
procedure TMyPanel.DrawImage(ACanvas: TCanvas);
var
  LGPGraphics   : TGPGraphics;
  LGPPen        : TGPPen;
  LGPEndArrow   : TGPAdjustableArrowCap;
  LLineLength   : Integer;
  LPosX         : Integer;
  LPosY         : Integer;
begin
  //直線の長さと描画開始X座標値
  LLineLength := 280;
  LPosX       := 20;


  //終端の矢印のオブジェクトを生成
  LGPEndArrow := TGPAdjustableArrowCap.Create(FArrowHeight, FArrowWidth);

  //インセットの値を設定
  LGPEndArrow.SetMiddleInset(FArrowMiddle);

  //線を描画するために使用するTGPPenの生成
  //生成した端点形状の情報をセット
  LGPPen := TGPPen.Create(MakeColor(255, 0, 150, 80));
  //この方にもSet...という各種の設定用メソッドがある
  LGPPen.SetCustomEndCap(LGPEndArrow);

  //描画先を指定してTGPGraphics生成
  LGPGraphics := TGPGraphics.Create(ACanvas.Handle);

  //線の太さを指定して描画
  LPosY := 100;
  LGPPen.SetWidth(10.0);
  LGPGraphics.DrawLine(LGPPen, LPosX, LPosY, LPosX + LLineLength, LPosY);

  LGPEndArrow.Free;
  LGPPen.Free;
  LGPGraphics.Free;
end;

//-----------------------------------------------------------------------------
//  TMyPanelのPaintメソッド
//  CanvasをTImage.CanvasにすればTImageへの描画となる
//-----------------------------------------------------------------------------
procedure TMyPanel.Paint;
begin
  inherited;
  DrawImage(Canvas);
end;

end.




04_指定可能な端点形状の定数名による端点形状


ここまでのサンプルては,TGPPen.SetCustomStartCap, TGPPen.SetCustomEndCap で端点形状としての矢印を設定ししました.TGPPen の SetStartCap, SetEndCpa というメソッドでも端点形状を設定できます.これらのメソッドの引数には,GDI+ があらかじめ用意している定数名を使用します.
それらの定数名を使用した場合の確認テストです.


図6
実行結果
  • 一番上は LineCapFlat で,これがデフォルトの端点形状

リスト4
指定可能な端点形状の定数名による端点形状
//-----------------------------------------------------------------------------
//  実際の描画コード
//  直線を描画する時の端点形状
//  TGPPen.SetStartCapとTGPPen.SetEndCapで指定可能な値の場合
//
//  ACanvas : 描画先のTCanas
//-----------------------------------------------------------------------------
procedure TMyPanel.DrawImage(ACanvas: TCanvas);
var
  LGPGraphics  : TGPGraphics;
  LGPPen       : TGPPen;
  LLineLength  : Integer;
  LPosX        : Integer;
  LPosY        : Integer;
  LIncY        : Integer;
begin
  //直線の長さと描画開始X座標値
  LLineLength := 270;
  LPosX       := 30;
  LIncY       := 35;

  //線を描画するために使用するTGPPenの生成
  LGPPen := TGPPen.Create(MakeColor(255, 0, 150, 100));
  LGPPen.SetWidth(10.0);

  //描画先を指定してTGPGraphics生成
  LGPGraphics := TGPGraphics.Create(ACanvas.Handle);


  //端点の形状を指定して描画
  LPosY := 50;
  LGPPen.SetStartCap(LineCapFlat);
  LGPPen.SetEndCap(LineCapFlat);
  LGPGraphics.DrawLine(LGPPen, LPosX, LPosY, LPosX + LLineLength, LPosY);

  //端点の形状を変更して描画
  LPosY := LPosY + LIncY;
  LGPPen.SetStartCap(LineCapSquare);
  LGPPen.SetEndCap(LineCapSquare);
  LGPGraphics.DrawLine(LGPPen, LPosX, LPosY, LPosX + LLineLength, LPosY);

  //端点の形状を変更して描画
  LPosY := LPosY + LIncY;
  LGPPen.SetStartCap(LineCapRound);
  LGPPen.SetEndCap(LineCapRound);
  LGPGraphics.DrawLine(LGPPen, LPosX, LPosY, LPosX + LLineLength, LPosY);

  //端点の形状を変更して描画
  LPosY := LPosY + LIncY;
  LGPPen.SetStartCap(LineCapTriangle);
  LGPPen.SetEndCap(LineCapTriangle);
  LGPGraphics.DrawLine(LGPPen, LPosX, LPosY, LPosX + LLineLength, LPosY);

  //端点の形状を変更して描画
  LPosY := LPosY + LIncY;
  LGPPen.SetStartCap(LineCapNoAnchor);
  LGPPen.SetEndCap(LineCapNoAnchor);
  LGPGraphics.DrawLine(LGPPen, LPosX, LPosY, LPosX + LLineLength, LPosY);

  //端点の形状を変更して描画
  LPosY := LPosY + LIncY;
  LGPPen.SetStartCap(LineCapSquareAnchor);
  LGPPen.SetEndCap(LineCapSquareAnchor);
  LGPGraphics.DrawLine(LGPPen, LPosX, LPosY, LPosX + LLineLength, LPosY);

  //端点の形状を変更して描画
  LPosY := LPosY + LIncY;
  LGPPen.SetStartCap(LineCapRoundAnchor);
  LGPPen.SetEndCap(LineCapRoundAnchor);
  LGPGraphics.DrawLine(LGPPen, LPosX, LPosY, LPosX + LLineLength, LPosY);

  //端点の形状を変更して描画
  LPosY := LPosY + LIncY;
  LGPPen.SetStartCap(LineCapDiamondAnchor);
  LGPPen.SetEndCap(LineCapDiamondAnchor);
  LGPGraphics.DrawLine(LGPPen, LPosX, LPosY, LPosX + LLineLength, LPosY);

  //端点の形状を変更して描画
  LPosY := LPosY + LIncY;
  LGPPen.SetStartCap(LineCapArrowAnchor);
  LGPPen.SetEndCap(LineCapArrowAnchor);
  LGPGraphics.DrawLine(LGPPen, LPosX, LPosY, LPosX + LLineLength, LPosY);


  LGPPen.Free;
  LGPGraphics.Free;
end;

//-----------------------------------------------------------------------------
//  TMyPanelのPaintメソッド
//  CanvasをTImage.CanvasにすればTImageへの描画となる
//-----------------------------------------------------------------------------
procedure TMyPanel.Paint;
begin
  inherited;
  DrawImage(Canvas);
end;