DevExpress 備忘録

P-SPACE
就学奨励費システム

マイナンバー対応 特別支援教育 就学奨励費 支給業務支援システム

詳細...

P-SPACE
就学奨励費ソフトウェア

特別支援教育 就学奨励費 支給業務支援ソフトウェア

詳細...

開発室
(備忘録そのほか)

システム開発にまつわる(かもしれない)調査・実験結果など

詳細...

XRLabel、XRTableCellのフォントサイズ自動設定

Excelの帳票をXtraReportに移植しようとしていろいろと実験・調査したところ、

  • セル(XRTableCell)の文字の配置についてExcelの「縮小して全体を表示する」に相当する機能がない
  • セルの文字の配置についてExcelと同様な均等割付けができない
  • XRTableではセルの(縦方向の)結合ができない

といったことがわかりました。

どうやら凝ったデザインのExcelレポートをXRTableで再現するのは難しそうなので、
ラベル(XRLabel)を"セル"に見立てて、これを敷き詰めてExcelの帳票を再現することにしたのですが、このとき特に困ったのがXRLabelにも上記最初の「縮小して全体を表示する」機能がないということです。

DevExpressのサポートページを検索して見つけたトピック
(http://www.devexpress.com/Support/Center/p/S139105.aspx)によると、
「C# Tutorial - Font Scaling」
http://www.switchonthecode.com/tutorials/csharp-tutorial-font-scaling
を読んで自分で実装してちょうだい!ということらしいので、これを参考にして実装しました。

void XRLabel_BeforePrint(object sender, System.Drawing.Printing.PrintEventArgs e) {
    XRLabel label = sender as XRLabel;

    //集計が行われる場合は集計結果をもとに処理
    string displaying_text;
    if (label.Summary == null || label.Summary.Running == SummaryRunning.None) {
        displaying_text = label.Text;
    } else {
        displaying_text = string.Format("{0:N0}";, label.Summary.GetResult());
    }

    if (!string.IsNullOrEmpty(displaying_text)) {
        Font default_font = new Font(label.Font.FontFamily, 9.75f, label.Font.Style);

        //スタイルによるフォント指定がある場合はそれも調整する

        //GetAutoFontSizeの引数base_fontにlabel.Fontを渡すとだめ。
        //Detailの繰り返しの中で、2回目に呼ばれた時、1回目に呼ばれたときに変更したフォントサイズが
        //base_fontとなってしまい、以降フォントサイズが小さいままになってしまう。

        if (label.Styles.Style != null && label.Styles.Style.Font != null) {
            label.Styles.Style.Font = new Font(
                label.Styles.Style.Font.FontFamily,
                this.GetAutoFontSize(label, displaying_text, default_font),
                label.Styles.Style.Font.Style);
        }

        label.Font = new Font(
            label.Font.FontFamily,
            this.GetAutoFontSize(label, displaying_text, default_font),
            label.Font.Style);
    }
}

上記のコードはXRLabelのBeforePrintイベントハンドラです。
GetAutoFontSizeメソッドは先の「C# Tutorial - Font Scaling」を参考に作成した、ちょうどよい大きさのフォントサイズを返すメソッドです(後述)。

XRLabelに表示する内容が集計の場合、集計結果でサイズを決めなくてはいけないので、6行目で分岐させています。

また、XRLabelにスタイルが適用されていて、スタイルでフォントが指定されているような場合にはスタイルのフォントサイズも変更する必要があります。(21行目~26行目)

GetAutoFontSizeメソッドの第三引数に「default_font」を引き渡していますが、これをうっかり「label.Font」を渡してしまうとサイズの自動調節がきちんと働かなくなるので注意が必要です。

ReportHeaderやReportFooter「以外」のBandにこのラベルが配置されている場合には、このラベルのBeforePrintイベントは繰り返し呼び出される可能性があります。
うっかり「label.Font」を渡してしまうと2回目に呼びだされた時、1回目の呼び出しで変更したフォントサイズが2回目のフォント調整の基準のサイズになってしまうので、複数回の呼び出しの後にはフォントがとても小さくなってしまうことがあります。

private float GetAutoFontSize(XRLabel label, string text, Font base_font) {
    using (Graphics gr = Graphics.FromHwnd(IntPtr.Zero)) {
        gr.PageUnit = GraphicsUnit.Millimeter;

        SizeF drawing_text_size = gr.MeasureString(text, base_font);

        float hRatio = (label.Height - (label.Padding.Top + label.Padding.Bottom)) / (drawing_text_size.Height * 10);
        float wRatio = (label.Width - (label.Padding.Left + label.Padding.Right)) / (drawing_text_size.Width * 10);
        float ratio = (hRatio < wRatio) ? hRatio : wRatio;

        if (ratio < 1) { //フォントサイズの拡大はしない
            return base_font.Size * ratio;
        } else {
            return base_font.Size;
        }
    }
}

ラベルのPaddingを考慮したり、フォントサイズが指定されたフォントより大きくならないように配慮していますが、基本的な考え方は「C# Tutorial - Font Scaling」と同じです。

hRatio、wRatioを求めるところでテキストのサイズを10倍してから割っていますが、これはレポートのReportUnit(TenthsOfAMillimeterにセットしています)をMillimeterに合わせるための措置です。

以上で「縮小して全体を表示する」と同様なふるまいをするようになります。

よく使うならいっそのことXRLabelを継承したクラスを作ってしまうといいかもしれません。

参考: How to set the label width so that it is always equal to its text (AutoWidth)
http://www.devexpress.com/Support/Center/p/A2747.aspx