LoginSignup
1
0

More than 3 years have passed since last update.

ContentAlignmentとStringAlignmentの相互変換

Last updated at Posted at 2020-07-24

背景

LabelのTextを自力でDrawしたかったんです。
OnPaintの中でDrawStringすればオッケです。
簡単でしょ?

詳細

なるべく、Labelコントロールのプロパティを尊重したいです。
特にTextAlignプロパティは重要でした。

ところで、Label.TextAlign は ContentAlignment 型のプロパティです。
えーと、こんな感じ

image

しかし、DrawStringで表示位置を制御するにはStringAlignment型のアトリビュート二つ、つまり

  • Alignment
  • LineAlignment

の組み合わせで、縦横の位置を指定しなければなりません。
つまりAlignmentは、この位置を表していて

image

LineAlignmentは、この位置を表していて

image

この組み合わせで、ContentAlignmentと同じ位置指定ができるようになっている。
ちゅー訳です。

NearとFarの位置関係は日本語ではの話、と思ってね

分析

ところで、ContentAlignment型はenumで以下の値を持っています。


TopLeft = 1
TopCenter = 2
TopRight = 4
MiddleLeft = 16
MiddleCenter = 32
MiddleRight = 64
BottomLeft = 256
BottomCenter = 512
BottomRight  =1024

StringAlignment型もenumで以下の値。

Near = 0
Center = 1
Far = 2



因みに、ContentAlignmentを図示すると以下の形。

image

enumの値は実際に立ったビットが2進数値としてどう評価されるかの値。


一方、StringAlignmentは下図。

image

enum値はただの、値を持った識別子。

変換 その1(あっち ⇒ こっち)

えーと、まずは分かり易そうな方から…

StringAlignmentはただの値、と云いながらもよ~く見てみると以下の対応関係が成り立つようになっています。

image

Left = Near なのでContentAlignment(の一部)とStringAlignmentの位置関係は、上の図のようになります。
で、着目すべきは赤い数字。

つまり、$2^{StringAlignment}$とすると、その結果が1だったり2だったり4だったりして、ContentAlignmentの対応位置にビットを立てる事ができるって訳です。


次は垂直方向の位置合わせです。
サクッと図にしてしまうと以下のような感じです。

image

Top / Middle / Bottom其々を4ビットのブロックとして考えると、StringAlignmentの値とブロックの位置関係を対応付ける事が出来ます。
つまり、水平位置の変換結果に垂直位置の変換値を掛け合わせる事で、ContentAlignmentに変換されます。
こう云う事―

ContentAlignment = Math.Pow(2, Alignment) * Math.Pow(16, LineAlignment);

わっかり難ーい!
と云う時には以下の様に考えても可。

$2^{Alignment}$ で求めた水平配置情報を―

  • 0ブロック左シフトすると、垂直方向にはTopに配置される
  • 1ブロック左シフトすると、垂直方向にはMiddleに配置される
  • 2ブロック左シフトすると、垂直方向にはBottomに配置される
ContentAlignment = Math.Pow(2, Alignment) << (4 * LineAlignment);

4ビット左シフトすると云う事は、(2の4乗)=16を掛けるってのと同義だからね。


さて―
簡単な方は片付いたけど、本来やりたかったのはContentAlignmentを二つのStringAlignemntに分割する方でした。

変換 その2(こっち ⇒ あっち)

あっちからこっちへの変換は数式でできたので、こっちからあっちへの逆変換も数式で出来る筈です。
数ⅡBを思い出しましょう!

冪乗の逆関数は対数になります。
$2^n = x$ なら、$n = log_2 x$ と云う事です。

配置 冪乗 対数
left $2^0 = 1$ $0 = log_2 1$
center $2^1 = 2$ $1 = log_2 2$
right $2^2 = 4$ $2 = log_2 4$



さて、もう一回この図を持ってきて逆の見方で読み解くと-

image

もし ContentAlignment の値が $1$ ならば、$log_2 1 = 0$ で、(Topの)Nearです。
もし ContentAlignment の値が $2$ ならば、$log_2 2 = 1$ で、(Topの)Middleになります。
もし ContentAlignment の値が $4$ ならば、$log_2 4 = 2$ で、(Topの)Farに変換できます。

じゃぁ、垂直方向がTop以外の場合はどうなるかと云うと、この場合も先ほどとは逆に4ビット単位で右シフトしてあげれば良いですね。

ここも例に依って対数です。
一つのブロックが4ビット($=2^4=16$)単位ですので $log_{16} ContentAlignment$ とすれば、ビットがどのブロックに含まれているかを知る事が出来ます。

えーと、ここにきて更に公式ですけど…
C#のMathライブラリにおいて扱えるのは、常用対数か自然対数のどちらか1です。(対数の底が $10$ か $e$ かって事)
上に出てきた様に底を $2$とか $16$ にしたければ以下の変換公式を使う必要があります。

$log_x n = log_{10} n / log_{10} x$

ここら辺を考慮すると縦位置はー

LineAlignment = Math.Floor(Math.Log10(ContentAlignment) / Math.Log10(16))

横位置の方は縦位置に従って右シフトした上で対数変換すれば…

Alignment = Math.Log10(ContentAlignment >> (4 * LineAlignment)) / Math.log10(2)

で、縦横のStringAlignmentに変換できました。

じゃぁ、C#で表現してみましょう

多分、Extensionにした方が使い易い様な気がする…
ちょっと癖が出てしまいましたが、双方向ともStringFormatに対する拡張メソッドになります。

public static class ExtClass {
    public static ContentAlignment ToContentAlignment(this StringFormat Me) {
        return (ContentAlignment)((int)Math.Pow(2, (int)Me.Alignment) << (4 * (int)Me.LineAlignment));
    }

    public static void SetStringAlignment(this StringFormat Me, ContentAlignment ca) {
        int Valignment = (int)Math.Floor(Math.Log10((int)ca) / Math.Log10(16));
        int Halignment = (int)(Math.Log10((int)ca >> (4 * Valignment)) / Math.Log10(2));

        Me.LineAlignment = (StringAlignment)Valignment;
        Me.Alignment = (StringAlignment)Halignment;
    }
}
使い方
    StringFormat sf = new StringFormat();
    Console.WriteLine(sf.ToContentAlignment());            //  sfのAlignmentをContentAlignmentで取得

    sf.SetStringAlignment(ContentAlignment.BottomCenter);  //  ContentAlignemnetからsfにAlignmentをコピー
    Console.WriteLine(sf.ToContentAlignment());

てな感じで…


  1. .NET Frmeworkの場合 

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0