描画文字列のサイズを取得する

画面に描画する文字列の縦横のサイズを取得する方法を紹介します。

文字列の縦横サイズは、描画する文字列の文字数、フォントの種類、フォントサイズによって、文字列の縦横サイズは大きく変化します。

たとえば画面内でマウスクリックした座標のどまんなかに、左右中央揃え、上下中央揃えで文字列を描画したい場合は、その縦横サイズが分からなければ実現することができません。

下記にコードを示します。具体的には 69~72行目の MeasureString() というメソッドで、文字列の幅サイズ、高さサイズ、を取得します。

フォントの種類はここでは "Courier New" としましたが、自分のお気に入りのフォントや、そのサイズを変更して、いろいろ試行してみてください。創英角ポップ体などいかがでしょうか。

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

namespace aaa
{
	public partial class Form1 : Form
	{

		// マウスをクリックした座標.
		int TheX;
		int TheY;

		// 描画のグローバルカウンタ.
		uint GlobalCounter = 0;

		public Form1()
		{
			InitializeComponent();
		}

		private void Form1_Load( object sender, EventArgs e )
		{

			// ウインドウの描画領域のサイズを取得する.
			int cw = this.ClientRectangle.Width;
			int ch = this.ClientRectangle.Height;

			// とりあえず真ん中に出現させる.
			TheX = cw >> 1;
			TheY = ch >> 1;

			// 画面がチラつくといやなので対策する.
			this.DoubleBuffered = true;

			this.Text = "GazoYaro";

		}

		private void Form1_Paint( object sender, PaintEventArgs e )
		{

			// 描画カウンタの100倍の数字を画面に出力する文字列とする.
			uint tmp = GlobalCounter * 100;
			String str = String.Format( "{0}", tmp );

			// 描画カウンタをインクリメントする.
			GlobalCounter++;

			// グラフィックスの描画ハンドルを取得する.
			Graphics grph = e.Graphics;

			// ウインドウの描画領域のサイズを取得する.
			int cw = this.ClientRectangle.Width;
			int ch = this.ClientRectangle.Height;

			// ウインドウを背景色として塗りつぶす.
			SolidBrush sb_bg = new SolidBrush( Color.Navy );
			Rectangle rct = new Rectangle( 0, 0, cw, ch );
			grph.FillRectangle( sb_bg, rct );

			// 出力する文字列のフォントを指定して、文字列のサイズを取得する.
			String font_name = "Courier New";
			float font_size = 32.0f;
			Font font = new Font( font_name, font_size );
			SizeF sz = grph.MeasureString( str, font );

			// サイズのままだと面倒なので幅と高さの変数に代入する.
			float str_w = sz.Width;
			float str_h = sz.Height;

			// 文字列の出力位置はマウスでクリックしたど真ん中にする.
			float str_x = (float)( TheX - ( str_w * 0.5 ));
			float str_y = (float)( TheY - ( str_h * 0.5 ));

			// 文字列を描画する.
			SolidBrush sb_font = new SolidBrush( Color.White );
			PointF point_str = new PointF( str_x, str_y );
			grph.DrawString( str, font, sb_font, point_str );

			// 文字列の周りに枠線を描画する.
			Color c_pen_frame = Color.Lime;
			float pen_width_frame = 1.0f;
			Pen pen_frame = new Pen( c_pen_frame, pen_width_frame );
			Rectangle rect_frame = new Rectangle(
										(int)( str_x ),
										(int)( str_y ),
										(int)( str_w ),
										(int)( str_h )
										);
			grph.DrawRectangle( pen_frame, rect_frame );

			// マウスでクリックした場所に十字線を描画する.
			Color c_pen_crosshair = Color.DeepPink;
			float pen_width_crosshair = 1.0f;
			Pen pen_crosshair = new Pen( c_pen_crosshair, pen_width_crosshair );
			grph.DrawLine( pen_crosshair, 0, TheY, cw, TheY );
			grph.DrawLine( pen_crosshair, TheX, 0, TheX, ch );

			if ( sb_bg != null )
			{
				sb_bg.Dispose();
				sb_bg = null;
			}

			if ( sb_font != null )
			{
				sb_font.Dispose();
				sb_font = null;
			}

			if ( font != null )
			{
				font.Dispose();
				font = null;
			}

			if ( pen_frame != null )
			{
				pen_frame.Dispose();
				pen_frame = null;
			}

			if ( pen_crosshair != null )
			{
				pen_crosshair.Dispose();
				pen_crosshair = null;
			}

			toolStripStatusLabel1.Text = String.Format( "client wh({0},{1})", cw, ch );
			toolStripStatusLabel2.Text = String.Format( "TheXY({0},{1})", TheX, TheY );
			toolStripStatusLabel3.Text = String.Format( "str xy({0:f1},{1:f1})", str_x, str_y );
			toolStripStatusLabel4.Text = String.Format( "str wh({0:f1},{1:f1})", str_w, str_h );

		}

		private void Form1_MouseDown( object sender, MouseEventArgs e )
		{

			// マウスの座標をパブリック変数に代入する.
			TheX = e.X;
			TheY = e.Y;

			this.Invalidate( true );

		}

		private void Form1_MouseMove( object sender, MouseEventArgs e )
		{

			// マウス左ボタンがおされていたら MouseDown をコールする.
			MouseButtons mbs = MouseButtons.Left;
			if (( e.Button & mbs ) == mbs )
			{
				this.Form1_MouseDown( sender, e );
			}

		}

		private void menuApplicationQuit_Click( object sender, EventArgs e )
		{
			this.Close();
		}

	}

}

WPFと比べると、Windows Forms は描画のコードがひどくやっかいですね。描画リソースの解放 Dispose() を忘れそうな場合は try ...catch...finally の中におしこめるか、using{} で囲んでやればいいと思います。

SDK や MFC の経験があるプログラマは、デバイスコンテキストに関連付けた描画アイテムは必ず解放するという作法が身についていると思いますが、たまに解放し忘れて徐々にシステムリソース圧迫して30分後にブルースクリーンみたいな経験もしたことがあるでしょう。

そういう安全性や安定性の観点からは、 WPF は優れたフレームワークであるといってよいでしょう。肝心の描画が遅いのですけれど...

サンプルソースを用意しました。上記のコードのコピーペーストでうまく動かない場合にご利用ください。