2014年10月2日木曜日

縦書きテキストボックスコントロールの作成(その13) 



まず、簡単そうなプロパティから消化します。

プロパティ名
Font
コントロールでテキストを表示するフォントです
ForeColor
テキストを表示するのに使用される、このコンポーネントの前景色です
まずはフォントプロパティから。


縦書きテキストボックスに送信するためのプロパティやら定数やら
''' <summary>コントロールに送信して、テキスト描画時にコントロールが使用しているフォントを取得します。</summary>
Friend Const WM_GETFONT As Integer = &H31
''' <summary>コントロールに送信して、テキスト描画時にコントロールが使用するフォントを設定します。wParam = hFont;フォントのハンドルを指定します。 0 (NULL) を指定すると、デフォルトのシステムフォントが使用されます。lParam = fRedraw;フォント設定後にコントロールを再描画するかどうかを指定します。 1 (TRUE) を指定すると、設定後直ちに再描画されます。 0 (FALSE) を指定すると、すぐには再描画されません。</summary>
Friend Const WM_SETFONT As Integer = &H30
''' <summary>
''' コントロールのフォントを設定します
''' </summary>
''' <value>指定するフォント</value>
''' <returns></returns>
''' <remarks></remarks>
Property Font As System.Drawing.Font
    Get
        Dim i As IntPtr = SendMessage(_Handles.TextBox, WM_GETFONT, 0, 0)
        Return System.Drawing.Font.FromHdc(i)
    End Get
    Set(value As System.Drawing.Font)
        SendMessage(_Handles.TextBox, WM_SETFONT, value.ToHfont, True)
    End Set
End Property
コントロール側の設定
''' <summary>
''' フォントの取得と設定
''' </summary>
''' <value></value>
''' <returns></returns>
''' <remarks></remarks>
Public Overloads Property Font As System.Drawing.Font
    Get
        Return MyBase.Font
    End Get
    Set(value As System.Drawing.Font)
        MyBase.Font = value
        Module_Font.Font = value
    End Set
End Property

フォントのデータには書体名情報だけでなくサイズやスタイル、文字飾りのデータも含まれているのですが、これらはまとめて全部送信します。
個別設定する場合はコントロールに渡す「Font」のデータに個別指定してやればできるので、ここでは割愛します。

次は「ForeColor」です。これ、結構難航しました。

当初、「SetTextColor関数」を使用すればよいかな?と思いましたが、どうにもうまくいかなかった背景があります。
WM_CTRCOLOREDIT」メッセージを拾って処理すればいいのかな?とも思いましたが、どうにも拾えないという状況。
結果、「EM_SETCHARFORMAT」をSendMessag関数で送ってやることで解決を見ました。

''' <summary>
''' 文字列情報格納用クラス
''' </summary>
''' <remarks></remarks>
<System.Runtime.InteropServices.StructLayout(System.Runtime.InteropServices.LayoutKind.Sequential, CharSet:=System.Runtime.InteropServices.CharSet.Unicode)> _
Friend Class CHARFORMAT
    ''' <summary>構造体のサイズ</summary>
    Public cbSize As Integer = System.Runtime.InteropServices.Marshal.SizeOf(GetType(CHARFORMAT))
    ''' <summary>有効メンバ</summary>
    Public dwMask As FontMask
    ''' <summary>文字の効果</summary>
    Public dwEffects As FontEffects
    ''' <summary>文字の高さ</summary>
    Public yHeight As Integer
    ''' <summary>ベースラインからのオフセット</summary>
    Public yOffset As Integer
    ''' <summary>文字の色</summary>
    Public crTextColor As UInteger
    ''' <summary>キャラクタセット</summary>
    Public bCharSet As Byte
    ''' <summary>フォントファミリとピッチ</summary>
    Public bPitchAndFamily As Byte
    ''' <summary>フォント名</summary>
    <System.Runtime.InteropServices.MarshalAs(System.Runtime.InteropServices.UnmanagedType.ByValArray, SizeConst:=32)> _
    Public szFaceName As Char() = New Char(31) {}
End Class
#Region "CHARFORMAT"
    ''' <summary>
    ''' 有効な情報を持つメンバは設定する属性を指定します。0又は以下の値の組み合わせになります。
    ''' </summary>
    ''' <remarks></remarks>
    Friend Enum FontMask As UInteger
        ''' <summary>dwEffectsメンバのCFE_DOLD値が有効</summary>
        BOLD = &H1
        ''' <summary>dwEffectsメンバのCFE_ITALIC値が有効</summary>
        ITALIC = &H2
        ''' <summary>dwEffectsメンバのCFE_UNDERLINE値が有効</summary>
        UNDERLINE = &H4
        ''' <summary>dwEffectsメンバのCFE_STRIKEOUT値が有効</summary>
        STRIKEOUT = &H8
        ''' <summary>dwEffectsメンバのCFE_PROTECTED値が有効</summary>
        [PROTECTED] = &H10
        ''' <summary>dCharSetメンバが有効</summary>
        CHARSET = &H8000000
        ''' <summary>yOffsetメンバが有効</summary>
        OFFSET = &H10000000
        ''' <summary>szFaceNameメンバが有効</summary>
        FACE = &H20000000
        ''' <summary>crTextColordwEffectsメンバのCFE_AUTOCOLOR値が有効</summary>
        COLOR = &H40000000
        ''' <summary>yHeightメンバが有効</summary>
        SIZE = &H80000000UI
    End Enum
    ''' <summary>
    ''' 文字の効果を指定します。0又は以下の組み合わせで指定します。dwMaskメンバで指定されている文字効果のみ有効です。
    ''' </summary>
    ''' <remarks></remarks>
    Friend Enum FontEffects As UInteger
        None = 0
        ''' <summary>太字で表示されます。</summary>
        BOLD = &H1
        ''' <summary>イタリック体で表示されます。</summary>
        ITALIC = &H2
        ''' <summary>アンダーラインを引きます。</summary>
        UNDERLINE = &H4
        ''' <summary>取り消し線を引きます。</summary>
        STRIKEOUT = &H8
        ''' <summary>文字が保護されます。保護される文字が変更されようとするとき、EN_PROTECTED通知メッセージが送られます。</summary>
        [PROTECTED] = &H10
        ''' <summary>文字色がGETSYSCOLOR関数にCOLOR_WINDOWTEXTを指定したとき取得される色になります。</summary>
        AUTOCOLOR = &H40000000
        Disabled = &H2000
    End Enum
    ''' <summary>
    ''' デフォルトの書式と選択されている文字列の書式のどちらを取得するかを指定します。0を指定するとデフォルトが、1を指定すると現在の選択範囲の書式が取得されます。
    ''' </summary>
    ''' <remarks></remarks>
    Friend Enum fSelection As Integer
        ''' <summary>デフォルトの文字書式</summary>
        [DEFAULT] = 0
        ''' <summary>選択範囲の文字書式</summary>
        SELECTION = 1
        ''' <summary>EM_SETCHARFORMAT時のみ:選択されている単語に適用。選択されていない状態でもカーソルがある単語の中にあれば、その単語に対して書式が適用</summary>
        SELECTWORD = 3
        ''' <summary>EM_SETCHARFORMAT時のみ:書式をコントロール中のすべての文字列に適用</summary>
        ALL = 4
    End Enum
#End Region

以上が下準備です。これから実際のコードを記載します。

''' <summary>
''' フォントカラーの設定を行います。
''' </summary>
''' <param name="Range">フォントカラーにする範囲を指定</param>
''' <param name="isForeColor">変更するフォントカラー</param>
''' <remarks></remarks>
<System.Runtime.CompilerServices.Extension()> _
Public Sub SetForeColor(range As fSelection, isForeColor As System.Drawing.Color)
    Dim cf As New CHARFORMAT()
    cf.dwMask = FontMask.COLOR Or FontMask.CHARSET
    cf.dwEffects = Not FontEffects.AUTOCOLOR
    cf.crTextColor = RGB(isForeColor.R, isForeColor.G, isForeColor.B)
    SendMessage(_Handles.TextBox, EM_SETCHARFORMAT, CType(range, IntPtr), cf)
End Sub


Friend _ForeColor As System.Drawing.Color

''' <summary>
''' コントロールの前景色(文字色)を設定または取得します。
''' </summary>
''' <value></value>
''' <returns></returns>
''' <remarks></remarks>
Property ForeColor As System.Drawing.Color
    Get
        Return _ForeColor
    End Get
    Set(value As System.Drawing.Color)
        Dim Range As fSelection = fSelection.ALL
        Dim NewCLR As Color = value
        Call SetForeColor(Range, NewCLR)
        _ForeColor = value
    End Set
End Property

テキストの文字色の方、問題なく変更となりました。

SetForeColor」プロシを別宣言した理由は、文字色の変更範囲を上のソースでは「Dim Range As fSelection = fSelection.ALL」と全体を指定していますが、部分的に変更する場合のことも考慮したためです。

さて、「WM_SETFONT」でもフォントのデザインやサイズは変更可能ですが、下の例では「WM_SETFONT」ではなく、「EM_SETCHARFORMAT」を使用した例です。

''' <summary>
''' 文字列の太字の設定を行います。
''' </summary>
''' <param name="Range">太字にする範囲を指定</param>
''' <param name="isBold">太字にするか、太字を解除するか</param>
''' <remarks></remarks>
<System.Runtime.CompilerServices.Extension()> _
Public Sub SetBold(Range As fSelection, isBold As Boolean)
    Dim cf As New CHARFORMAT()

    cf.dwMask = cf.dwMask Or FontMask.BOLD
    If isBold Then
        cf.dwEffects = cf.dwEffects Or FontEffects.BOLD
    Else
        cf.dwEffects = cf.dwEffects And Not FontEffects.BOLD
    End If
    SendMessage(_Handles.TextBox, EM_SETCHARFORMAT, CType(Range, IntPtr), cf)
End Sub

''' <summary>
''' 文字列の太字状態
''' </summary>
''' <remarks></remarks>
Friend _Bold As Boolean = False

''' <summary>
''' 文字列の太字の有効、無効の設定
''' </summary>
''' <value></value>
''' <returns></returns>
''' <remarks></remarks>
Property Bold As Boolean
    Get
        Return _Bold
    End Get
    Set(value As Boolean)
        Dim Range As fSelection = fSelection.ALL
        _Bold = value
        Call SetBold(Range, _Bold)
    End Set
End Property

このように、こちらでもフォントの状態を指定できます。リッチテキストボックスとして使用することを考慮すれば、WM_SETFONT」ではなく、「EM_SETCHARFORMAT」の方で行うことになるかと。
しかし、簡単そうと思ったら思いっきり大変だったという罠(涙)


さて、内容がほとんどAPI関数をどう組み込むか?というAPIの使い方に傾倒してしまってきたので、このシリーズはこれで終わりです。
テキストボックスとしての機能はこれではまだ不十分でしょうが、個人的にやりたいだけの機能はもう十分ありますんで。

1 件のコメント:

  1. 縦書きテキストボックスコントロールの作成、興味深く拝見しました。私も同様のコントロールが作りたくて、このサイトへ辿り着きました。このブログを参考にチェレンジしましたがうまくいきませんでした。非常に厚かましいお願いですが、サンプル等頂けないでしょうか? ko_na_yu_a_hi_ti@yahoo.ne.jp

    返信削除