Home>C# Tips>

キャレットを使ったサンプル

更新日:2003/08/24

キャレットとはキーボードから文字入力を受け付けるところに表示されているチカチカしているヤツのことです。エディットコントロールを自作しようとか考えなければ、まず使うことは無く、実際問題.NET Frameworkでもサポートされていません。@IT会議室への投稿の使い回し。


クラス構成

MyCaret
Win32APIをラッパしたキャレットクラス。
MyForm
Caretを使ったサンプルプログラム。キャレットの移動とテキストの選択が出来る。というか、それしか出来ない。

mycaret.cs

using System;
using System.Windows.Forms;
using System.Runtime.InteropServices;
using System.Drawing;

public class MyCaret {
  [DllImport("user32.dll")]
  public static extern int CreateCaret(IntPtr hwnd, IntPtr hbm, int cx, int cy);
  [DllImport("user32.dll")]
  public static extern int DestroyCaret();
  [DllImport("user32.dll")]
  public static extern int SetCaretPos(int x, int y);
  [DllImport("user32.dll")]
  public static extern int ShowCaret(IntPtr hwnd);
  [DllImport("user32.dll")]
  public static extern int HideCaret(IntPtr hwnd);

  Control ctrl;
  Size  size;
  Point  pos;
  bool  bVisible;

  public MyCaret(Control ctrl) {
    this.ctrl = ctrl;
    Position = Point.Empty;
    Size = new Size(1, ctrl.Font.Height);
    Control.GotFocus += new EventHandler(OnGotFocus);
    Control.LostFocus += new EventHandler(OnLostFocus);

    if (ctrl.Focused)
      OnGotFocus(ctrl, new EventArgs());
  }

  public Control Control {
    get { return ctrl; }
  }

  public Size Size {
    get { return size; }
    set { size = value; }
  }

  public Point Position {
    get { 
      return pos; 
    }
    set { 
      pos = value; 
      SetCaretPos(pos.X, pos.Y);
    }
  }

  public bool Visible {
    get {
      return bVisible;
    }
    set {
      bVisible = value;
      if (bVisible)
        ShowCaret(Control.Handle);
      else
        HideCaret(Control.Handle);
    }
  }

  public void Dispose() {
    if (ctrl.Focused)
      OnLostFocus(ctrl, new EventArgs());
    Control.GotFocus -= new EventHandler(OnGotFocus);
    Control.LostFocus -= new EventHandler(OnLostFocus);
  }

  private void OnGotFocus(object sender, EventArgs e) {
    CreateCaret(Control.Handle, IntPtr.Zero, Size.Width, Size.Height);
    SetCaretPos(Position.X, Position.Y);
    Visible = true;
  }

  private void OnLostFocus(object sender, EventArgs e) {
    Visible = false;
    DestroyCaret();
  }
}

caretsample.cs

using System;
using System.Drawing;
using System.ComponentModel;
using System.Windows.Forms;
using System.Runtime.InteropServices;

public class MyForm : Form {
  private Container components = null;
  MyCaret caret;
  string[] lines;
  Point cur;
  Point org;
  bool bSel;

  public MyForm() {
    InitializeComponent();

    caret = new MyCaret(this);
    lines = new string[] {
                 "public class MyApp {",
                 "    System.Console.WriteLine(\"Hello,World\");",
                 "}"};
    cur = Point.Empty;
    org = Point.Empty;
    bSel = false;
  }

  protected override void Dispose( bool disposing ) {
    if( disposing ) {
      if (components != null) {
        components.Dispose();
      }
    }
    base.Dispose( disposing );
  }

  protected override void OnPaint(PaintEventArgs e) {
    base.OnPaint (e);
    StringFormat fmt = StringFormat.GenericTypographic;
    fmt.FormatFlags |= StringFormatFlags.MeasureTrailingSpaces;
    for (int i = 0; i < lines.Length; ++i)
      e.Graphics.DrawString(lines[i], Font, new SolidBrush(this.ForeColor), 
        0, Font.Height * i, StringFormat.GenericTypographic);
    if (bSel) {
      DrawSelected(e.Graphics);
    }
  }

  protected override void OnKeyDown(KeyEventArgs e) {
    if (e.Shift) {
      if (!bSel) {
        org = cur;
        bSel = true;
      }
    }
    else {
      org = cur;
      bSel = false;
    }

    switch (e.KeyCode) {
      case Keys.Left:
        if (cur.X > 0) 
          --cur.X;
        else if (cur.Y > 0) {
          --cur.Y;
          cur.X = lines[cur.Y].Length;
        }
        break;
      case Keys.Right:
        if (cur.X < lines[cur.Y].Length)
          ++cur.X;
        else if (cur.Y < lines.Length - 1) {
          ++cur.Y;
          cur.X = 0;
        }
        break;
      case Keys.Up:
        if (cur.Y > 0) {
          --cur.Y;
          if (cur.X > lines[cur.Y].Length)
            cur.X = lines[cur.Y].Length;
        }
        break;
      case Keys.Down:
        if (cur.Y < lines.Length - 1) {
          ++cur.Y;
          if (cur.X > lines[cur.Y].Length)
            cur.X = lines[cur.Y].Length;
        }
        break;
    }
    MoveCaret();
    Invalidate();
  }

  void DrawSelected(Graphics g) {
    Point bgn;
    Point end;

    if (org.Y < cur.Y || org.Y == cur.Y && org.X < cur.X) {
      bgn = org;
      end = cur;
    }
    else {
      bgn = cur;
      end = org;
    }

    int p;
    int q;
    PointF lt = PointF.Empty;
    PointF rb = PointF.Empty;
    for (int i = bgn.Y; i <= end.Y; ++i) {
      lt.Y = i * Font.Height;
      rb.Y = (i + 1) * Font.Height;

      if (i == bgn.Y) { 
        lt.X = CalcWidth(g, lines[i].Substring(0, bgn.X));
        p = bgn.X;
      }
      else { 
        lt.X = 0; 
        p = 0;
      }
      if (i == end.Y) {
        rb.X = CalcWidth(g, lines[i].Substring(0, end.X));
        q = end.X;
      }
      else {
        rb.X = CalcWidth(g, lines[i]);
        q = lines[i].Length;
      }
      g.FillRectangle(Brushes.DarkBlue, lt.X, lt.Y, rb.X - lt.X, rb.Y - lt.Y);
      g.DrawString(lines[i].Substring(p, q - p), Font, new SolidBrush(Color.White), 
        lt.X, lt.Y, StringFormat.GenericTypographic);
    }
  }

  float CalcWidth(Graphics g, string s) {
    StringFormat fmt = StringFormat.GenericTypographic;
    fmt.FormatFlags |= StringFormatFlags.MeasureTrailingSpaces;
    SizeF size = g.MeasureString(s, Font, Point.Empty, fmt);
    return size.Width;
  }

  void MoveCaret() {
    using (Graphics g = Graphics.FromHwnd(Handle)) {
      float w = CalcWidth(g, lines[cur.Y].Substring(0, cur.X));
      caret.Position = new Point((int)w, Font.Height * cur.Y);
    }
  }

  private void InitializeComponent() {
    this.AutoScaleBaseSize = new System.Drawing.Size(6, 12);
    this.ClientSize = new System.Drawing.Size(292, 266);
    this.Font = new System.Drawing.Font("MS ゴシック", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((System.Byte)(0)));
    this.Name = "Form1";
    this.Text = "Form1";

  }

  [STAThread]
  static void Main() {
    Application.Run(new MyForm());
  }
}

Home>C# Tips>