次の方法で共有


.NET RichTextBox から WYSIWYG 印刷結果を取得する

 

Martin Müller
4voice AG

2003 年 1 月

適用対象:
   Microsoft® Visual Studio .NET
   Windows フォーム: RichTextBox コントロール

概要: .NET 相互運用メカニズムを使用して、Win32 リッチ エディット コントロールに共通する機能 (WYSIWYG 印刷や便利な選択書式など) を .NET RichTextBox に追加する方法。 (17ページ印刷)

この記事のソース コードをダウンロード します:richtextboxprinting.exe

内容

はじめに
レスキューへの相互運用
新しい RichTextBoxEx クラスのビルド
必要な Win32 構造体の定義
FormatRange() の実装
新しい RichTextBoxEx クラスの使用
新しいクラスへの書式設定の追加
まとめ

はじめに

多くの開発者が豊富な編集コントロールを印刷して、すべての書式設定と埋め込み画像を画面に表示する方法で印刷できます。 MFC を C++ で使用すると、タスクは単純でした。ドキュメント/ビュー アーキテクチャを使用して MFC アプリケーションを作成し、ビュー クラスとして [CRichEditView ] を選択するだけです。 Visual Studio のウィザードは、必要な他のすべてを構築しました。

ただし、開発者が .NET RichTextBox クラスを印刷する場合はどうしますか? これまでのすべての例では、 の 1 つを使用する方法を示しています。NET の印刷クラス PrintDocumentPrintPage イベントのイベント ハンドラーを追加する方法、およびイベント ハンドラーで受信した Graphics オブジェクトの DrawString メンバー関数を使用してテキストを描画する方法。

前の方法は、プレーンテキストのみを使用する場合や、印刷用の書式設定のみを調整する場合に適しています。 ただし、通常 、RichTextBox を使用する理由は、そこで書式設定を行い、用紙上で適切な表現を取得する必要があることです。 この記事では、 RichTextBox コントロールの内容を印刷して目的の結果を取得する方法について説明します。

レスキューへの相互運用

ほぼすべてのWindows フォーム コントロールは、Win32 に対応するコントロールに基づいて構築されています。 基になるリッチ エディット コントロールを見ると、特定のデバイスのテキスト範囲 (.NET RichTextBox クラスを印刷するために必要な内容) をリッチ エディット コントロール形式にするメッセージがあります。 ここで、このメッセージ (EM_FORMATRANGE) を RichTextBox の リッチ エディット コントロールに送信する方法を知る必要があります。

ここでは、名前空間機能 System.Runtime.InteropServices が考慮され、P/invoke またはプラットフォーム呼び出しと呼ばれるものを提供します。 .NET Framework開発者ガイドには、次の内容が記載されています。

"プラットフォーム呼び出しは、マネージド コードが Win32 API などのダイナミック リンク ライブラリ (DLL) に実装されているアンマネージ関数を呼び出すサービスです。"

Visual Basic を知っている開発者は、DLL に配置する外部関数を宣言し、自分の関数と同様にこの関数を呼び出す方法を知っています。 P/invoke と非常によく似た結果を受け取り、すべての .NET 言語がアンマネージ関数呼び出しをマネージド コードにほぼシームレスに統合します。

システムは非常に簡単に理解できます。 DllImportAttribute を使用して、使用する関数を .NET に指示し、署名を指定した後、CLR は通常、DLL を検索してメモリに読み込み、関数エントリ ポイントを検索し、すべてのパラメーターをアンマネージド型に変換してスタックにプッシュし、アンマネージド コードに制御を転送し、マネージド例外をアプリケーションに提供します。 問題が発生した場合に備え、

新しい RichTextBoxEx クラスのビルド

まず、必要なすべての名前空間を追加し、 RichTextBox から派生したクラスを作成します。

// C#
using System;
using System.Windows.Forms;
using System.Drawing;
using System.Runtime.InteropServices;
using System.Drawing.Printing;

/// <summary>
/// An extension for RichTextBox suitable for printing.
/// </summary>
public class RichTextBoxEx : RichTextBox
{
}

' VB.NET
Imports System
Imports System.Windows.Forms
Imports System.Drawing
Imports System.Runtime.InteropServices
Imports System.Drawing.Printing

' An extension to RichTextBox suitable for printing
Public Class RichTextBoxEx
    Inherits RichTextBox
End Class

次に、 EM_FORMATRANGE メッセージに必要なパラメーターを見てみましょう。 これらのパラメーターは、メッセージを送信するウィンドウ ハンドル (幸いにも、すべてのWindows フォーム コントロールは、基になるコントロールへのアクセスを許可する Handle プロパティを公開します)、実際にテキストをレンダリングするか、単に測定するかを指定する整数値、およびデバイス コンテキスト、印刷領域、およびレンダリングするテキスト範囲を含む FORMATRANGE 構造体へのポインターで構成されます。

必要な Win32 構造体の定義

FORMATRANGE 内には、RECTCHARRANGE という 2 種類の構造体が追加で処理されます。

Win32 API は RECT をとして定義します。...

typedef struct tagRECT {
    LONG left;
    LONG top;
    LONG right;
    LONG bottom;
} RECT;

...これは、C# または VB.NET の構造体に非常に簡単に変換されます。

メモ MFC の LONG 型は、 とは異なり、32 ビットです。NET の long。これは 64 ビットです。 そのため、正しい既定のマーシャリングを取得するには、対応するデータ型として を使用 Int32 します。

// C#
[ StructLayout( LayoutKind.Sequential )]
private struct STRUCT_RECT 
{
    public Int32 left;
    public Int32 top;
    public Int32 right;
    public Int32 bottom;
}
' VB.NET
<StructLayout(LayoutKind.Sequential)> _
private Structure STRUCT_RECT
    Public left As Int32
    Public top As Int32
    Public right As Int32
    Public bottom As Int32
End Structure

メモ 各構造体定義の前に StructLayoutAttribute があることに注意してください。 この構造体がアンマネージ関数のパラメーターとして使用される場合は、すべての構造体要素を順番にパックするように .NET に指示します。

CHARRANGEFORMATRANGE の定義は、説明が終わると非常に簡単になります。

typedef struct _charrange { 
    LONG cpMin; 
    LONG cpMax; 
} CHARRANGE;

typedef struct _formatrange { 
    HDC hdc; 
    HDC hdcTarget; 
    RECT rc; 
    RECT rcPage; 
    CHARRANGE chrg; 
} FORMATRANGE;
becomes
// C#
[ StructLayout( LayoutKind.Sequential )]
private struct STRUCT_CHARRANGE
{
    public Int32 cpMin;
    public Int32 cpMax;
}

[ StructLayout( LayoutKind.Sequential )]
private struct STRUCT_FORMATRANGE
{
    public IntPtr hdc; 
    public IntPtr hdcTarget; 
    public STRUCT_RECT rc; 
    public STRUCT_RECT rcPage; 
    public STRUCT_CHARRANGE chrg; 
}
' VB.NET
<StructLayout(LayoutKind.Sequential)> _
Private Structure STRUCT_CHARRANGE
    Public cpMin As Int32
    Public cpMax As Int32
End Structure

<StructLayout(LayoutKind.Sequential)> _
Private Structure STRUCT_FORMATRANGE
    Public hdc As IntPtr
    Public hdcTarget As IntPtr
    Public rc As STRUCT_RECT
    Public rcPage As STRUCT_RECT
    Public chrg As STRUCT_CHARRANGE
End Structure

メモ すべての構造体名は STRUCT_ で始まる点に注意してください。 最初のバージョンでは、すべての構造体は Win32 に対応する名前とまったく同じ名前でした。 これは C# では正常に機能しましたが、VB .NET から クラスを使用しようとすると、コンパイラは、表示レベルが原因で FormatRange にアクセスできないという苦情を表示します。
問題は、実装されるメンバー関数が FormatRange() (MFC の CRichEditCtrl のメンバー関数に似ています) と呼ばれ、構造体の 1 つが FORMATRANGE と呼ばれていたということです。 これは C# の問題ではありませんが、VB です。NET のコンパイラは、同じ名前の構造体と関数に対して (大文字または小文字を除く) チョークします。 したがって、サンプルを同一に保ち、VB コンパイラが混乱しないようにするために、構造体の名前が変更されました。

これらは実際には、 EM_FORMATRANGE メッセージを送信するために必要なすべての構造体です。 実際にメッセージを送信する方法を次に示します。

FormatRange() の実装

アンマネージ コードとの相互運用のために構造体をレイアウトする方法を宣言するのと同様に、アンマネージ関数 (この場合は Win32 の関数 SendMessage()) を使用する必要があるかどうかを .NET に指示する必要もあります。

そのためには、 DllImportAttribute を使用し、アクセスするアンマネージ DLL を指定する必要があります (この場合 はuser32.dll)。 次に、 静的 extern (VB .NET で共有 ) 関数とそのパラメーターをマーシャリングする方法を宣言します。

// C#
[DllImport("user32.dll")]
private static extern Int32 SendMessage(IntPtr hWnd, Int32 msg,
                                        Int32 wParam, IntPtr lParam);

private const Int32 WM_USER        = 0x400;
private const Int32 EM_FORMATRANGE = WM_USER+57;
' VB.NET
<DllImport("user32.dll")> _
Private Shared Function SendMessage(ByVal hWnd As IntPtr, _
                                    ByVal msg As Int32, _
                                    ByVal wParam As Int32, _
                                    ByVal lParam As IntPtr) As Int32
End Function

Private Const WM_USER As Int32 = &H400&
Private Const EM_FORMATRANGE As Int32 = WM_USER + 57

EM_FORMATRANGEの値も必要であるため、プラットフォーム SDK の RichEdit.h では、それが (WM_USER)+57WM_USERが 400 hex (WinUser.h から) であると述べています。

MSDN の SendMessage() の P/invoke には、wParam と lParam の 2 つの整数パラメーターを示す他の例がありますが、ここでは、構造体へのポインターを lParam にマーシャリングする必要があるため、IntPtr が必要です。

構造体を準備して入力し、 SendMessage()を呼び出します。 ここでの唯一のポイントは、.NET はネイティブ ポインターを認識しないため、 FORMATRANGE 構造体へのポインターを送信するには、相互運用機能の関数を使用してメモリを割り当て、構造体の内容をこのメモリにコピーする必要があるということです。

// C#
/// <summary>
/// Calculate or render the contents of our RichTextBox for printing
/// </summary>
/// <param name="measureOnly">If true, only the calculation is performed,
/// otherwise the text is rendered as well</param>
/// <param name="e">The PrintPageEventArgs object from the
/// PrintPage event</param>
/// <param name="charFrom">Index of first character to be printed</param>
/// <param name="charTo">Index of last character to be printed</param>
/// <returns>(Index of last character that fitted on the
/// page) + 1</returns>
public int FormatRange(bool measureOnly, PrintPageEventArgs e,
                       int charFrom, int charTo)
{
    // Specify which characters to print
    STRUCT_CHARRANGE cr;
    cr.cpMin = charFrom;
    cr.cpMax = charTo;

    // Specify the area inside page margins
    STRUCT_RECT rc;
    rc.top        = HundredthInchToTwips(e.MarginBounds.Top);
    rc.bottom    = HundredthInchToTwips(e.MarginBounds.Bottom);
    rc.left        = HundredthInchToTwips(e.MarginBounds.Left);
    rc.right    = HundredthInchToTwips(e.MarginBounds.Right);

    // Specify the page area
    STRUCT_RECT rcPage;
    rcPage.top    = HundredthInchToTwips(e.PageBounds.Top);
    rcPage.bottom = HundredthInchToTwips(e.PageBounds.Bottom);
    rcPage.left   = HundredthInchToTwips(e.PageBounds.Left);
    rcPage.right  = HundredthInchToTwips(e.PageBounds.Right);

    // Get device context of output device
    IntPtr hdc = e.Graphics.GetHdc();

    // Fill in the FORMATRANGE struct
    STRUCT_FORMATRANGE fr;
    fr.chrg      = cr;
    fr.hdc       = hdc;
    fr.hdcTarget = hdc;
    fr.rc        = rc;
    fr.rcPage    = rcPage;

    // Non-Zero wParam means render, Zero means measure
    Int32 wParam = (measureOnly ? 0 : 1);

    // Allocate memory for the FORMATRANGE struct and
    // copy the contents of our struct to this memory
    IntPtr lParam = Marshal.AllocCoTaskMem( Marshal.SizeOf( fr ) ); 
    Marshal.StructureToPtr(fr, lParam, false);

    // Send the actual Win32 message
    int res = SendMessage(Handle, EM_FORMATRANGE, wParam, lParam);

    // Free allocated memory
    Marshal.FreeCoTaskMem(lParam);

    // and release the device context
    e.Graphics.ReleaseHdc(hdc);

    return res;
}
' VB.NET
' Calculate or render the contents of our RichTextBox for printing
'
' Parameter "measureOnly": If true, only the calculation is performed,
'                          otherwise the text is rendered as well
' Parameter "e": The PrintPageEventArgs object from the PrintPage event
' Parameter "charFrom": Index of first character to be printed
' Parameter "charTo": Index of last character to be printed
' Return value: (Index of last character that fitted on the page) + 1
Public Function FormatRange(ByVal measureOnly As Boolean, _
                            ByVal e As PrintPageEventArgs, _
                            ByVal charFrom As Integer, _
                            ByVal charTo As Integer) As Integer
    ' Specify which characters to print
    Dim cr As STRUCT_CHARRANGE
    cr.cpMin = charFrom
    cr.cpMax = charTo

    ' Specify the area inside page margins
    Dim rc As STRUCT_RECT
    rc.top    = HundredthInchToTwips(e.MarginBounds.Top)
    rc.bottom = HundredthInchToTwips(e.MarginBounds.Bottom)
    rc.left   = HundredthInchToTwips(e.MarginBounds.Left)
    rc.right  = HundredthInchToTwips(e.MarginBounds.Right)

    ' Specify the page area
    Dim rcPage As STRUCT_RECT
    rcPage.top    = HundredthInchToTwips(e.PageBounds.Top)
    rcPage.bottom = HundredthInchToTwips(e.PageBounds.Bottom)
    rcPage.left   = HundredthInchToTwips(e.PageBounds.Left)
    rcPage.right  = HundredthInchToTwips(e.PageBounds.Right)

    ' Get device context of output device
    Dim hdc As IntPtr
    hdc = e.Graphics.GetHdc()

    ' Fill in the FORMATRANGE structure
    Dim fr As STRUCT_FORMATRANGE
    fr.chrg      = cr
    fr.hdc       = hdc
    fr.hdcTarget = hdc
    fr.rc        = rc
    fr.rcPage    = rcPage

    ' Non-Zero wParam means render, Zero means measure
    Dim wParam As Int32
    If measureOnly Then
        wParam = 0
    Else
     wParam = 1
    End If

    ' Allocate memory for the FORMATRANGE struct and
    ' copy the contents of our struct to this memory
    Dim lParam As IntPtr
    lParam = Marshal.AllocCoTaskMem(Marshal.SizeOf(fr))
    Marshal.StructureToPtr(fr, lParam, False)

    ' Send the actual Win32 message
    Dim res As Integer
    res = SendMessage(Handle, EM_FORMATRANGE, wParam, lParam)

    ' Free allocated memory
    Marshal.FreeCoTaskMem(lParam)

    ' and release the device context
    e.Graphics.ReleaseHdc(hdc)

    Return res
End Function

メモ Win32 と .NET では、ページ余白などを指定するときに異なる単位が使用されます。 FORMATRANGE 構造体内では、すべてのサイズは twip 単位 (1/1440 インチ) で、.NET のページ余白とページ サイズは 0.01 インチ (1 インチの 100 分の 1) 単位で処理されます。 したがって、1 インチの twip と 100 分の 1 の間で変換するユーティリティ関数が追加されました。

// C#
/// <summary>
/// Convert between 1/100 inch (unit used by the .NET framework)
/// and twips (1/1440 inch, used by Win32 API calls)
/// </summary>
/// <param name="n">Value in 1/100 inch</param>
/// <returns>Value in twips</returns>
private Int32 HundredthInchToTwips(int n)
{
    return (Int32)(n*14.4);
}
' VB.NET
' Convert between 1/100 inch (unit used by the .NET framework)
' and twips (1/1440 inch, used by Win32 API calls)
'
' Parameter "n": Value in 1/100 inch
' Return value: Value in twips
Private Function HundredthInchToTwips(ByVal n As Integer) As Int32
    Return Convert.ToInt32(n * 14.4)
End Function

EM_FORMATRANGEに関するドキュメントを注意深く読む場合は、lParam の値を "コントロールによってキャッシュされた空き情報" に NULL にすることもできます。そのため、印刷が完了したときに呼び出される別の関数を追加することをお勧めします (プログラムで PrintDocumentEndPrint イベントのイベント ハンドラーを追加します)。

// C#
/// <summary>
/// Free cached data from rich edit control after printing
/// </summary>
public void FormatRangeDone()
{
    IntPtr lParam = new IntPtr(0);
    SendMessage(Handle, EM_FORMATRANGE, 0, lParam);
}
' VB.NET
' Free cached data from rich edit control after printing
Public Sub FormatRangeDone()
    Dim lParam As New IntPtr(0)
    SendMessage(Handle, EM_FORMATRANGE, 0, lParam)
End Sub

2 番目のパラメーターとして NULL を指定して SendMessage() を P/invoke するだけです。

新しい RichTextBoxEx クラスの使用

必要な関数が実装されたので、この新しいクラスの使用方法を見てみましょう。

.NET で印刷する一般的な方法には、 PrintDocument クラスのインスタンスの使用が組み込まれています。 このクラスは、印刷プロセスの開始、新しいページの開始、印刷プロセスの終了、および実際に印刷を開始するメソッドのイベントを主に提供します。 イベントのフローは、次のように示すことができます。

図 1 印刷イベントのシーケンス

適切なイベント ハンドラー関数で新しい RichTextBoxEx メソッドを呼び出すことで、リッチ エディット コントロールの内容の印刷が簡単になります。

まず、新しい PrintDocument オブジェクトをインスタンス化し、必要なイベント ハンドラーを追加し、 Print() メソッドを呼び出します。

// C#
public void PrintRichTextContents()
{
    PrintDocument printDoc = new PrintDocument();
    printDoc.BeginPrint += new PrintEventHandler(printDoc_BeginPrint);
    printDoc.PrintPage  += new PrintPageEventHandler(printDoc_PrintPage);
    printDoc.EndPrint   += new PrintEventHandler(printDoc_EndPrint);
    // Start printing process
    printDoc.Print();
}
' VB.NET
Public Sub PrintRichTextContents()
    Dim printDoc As New PrintDocument()
    AddHandler printDoc.BeginPrint, AddressOf printDoc_BeginPrint
    AddHandler printDoc.PrintPage, AddressOf printDoc_PrintPage
    AddHandler printDoc.EndPrint, AddressOf printDoc_EndPrint
    ' Start printing process
    printDoc.Print()
End Sub

次に、次の 3 つのイベント ハンドラー関数を実装します。

// C#
// variable to trace text to print for pagination
private int m_nFirstCharOnPage;

private void printDoc_BeginPrint(object sender,
    System.Drawing.Printing.PrintEventArgs e)
{
    // Start at the beginning of the text
    m_nFirstCharOnPage = 0;
}

private void printDoc_PrintPage(object sender,
    System.Drawing.Printing.PrintPageEventArgs e)
{
    // To print the boundaries of the current page margins
    // uncomment the next line:
    // e.Graphics.DrawRectangle(System.Drawing.Pens.Blue, e.MarginBounds);
    
    // make the RichTextBoxEx calculate and render as much text as will
    // fit on the page and remember the last character printed for the
    // beginning of the next page
    m_nFirstCharOnPage = myRichTextBoxEx.FormatRange(false,
                                            e,
                                            m_nFirstCharOnPage,
                                            myRichTextBoxEx.TextLength);

// check if there are more pages to print
    if (m_nFirstCharOnPage < myRichTextBoxEx.TextLength)
        e.HasMorePages = true;
    else
        e.HasMorePages = false;
}

private void printDoc_EndPrint(object sender,
    System.Drawing.Printing.PrintEventArgs e)
{
    // Clean up cached information
    myRichTextBoxEx.FormatRangeDone();
}
' VB.NET
' variable to trace text to print for pagination
Private m_nFirstCharOnPage As Integer

Private Sub printDoc_BeginPrint(ByVal sender As Object, _
    ByVal e As System.Drawing.Printing.PrintEventArgs)
    ' Start at the beginning of the text
    m_nFirstCharOnPage = 0
End Sub

Private Sub printDoc_PrintPage(ByVal sender As Object, _
    ByVal e As System.Drawing.Printing.PrintPageEventArgs)
    ' To print the boundaries of the current page margins
    ' uncomment the next line:
    ' e.Graphics.DrawRectangle(System.Drawing.Pens.Blue, e.MarginBounds)
    
    ' make the RichTextBoxEx calculate and render as much text as will
    ' fit on the page and remember the last character printed for the
    ' beginning of the next page
    m_nFirstCharOnPage = myRichTextBoxEx.FormatRange(False, _
                                            e, _
                                            m_nFirstCharOnPage, _
                                            myRichTextBoxEx.TextLength)

    ' check if there are more pages to print
    If (m_nFirstCharOnPage < myRichTextBoxEx.TextLength) Then
        e.HasMorePages = True
    Else
        e.HasMorePages = False
    End If 
End Sub

Private Sub printDoc_EndPrint(ByVal sender As Object, _
    ByVal e As System.Drawing.Printing.PrintEventArgs)
    ' Clean up cached information
    myRichTextBoxEx.FormatRangeDone()
End Sub

m_nFirstCharOnPageメンバー変数が必要な理由は、ページ分割のためです。ページに収められた最後の文字のインデックスと 1 をレンダリングして取得する文字の範囲を FormatRange() に伝える必要があります。 したがって、正しい改ページを取得するには、最後の文字のインデックスを印刷したまま、このインデックス +1 で次のページを開始します。 これは、 m_nFirstCharOnPage がリッチ エディット コントロールの最後の文字に達するまで繰り返す必要があります。この時点で、印刷プロセスは e.HasMorePagesfalse に設定して停止します。

最後に、新しい FormatRangeDone() 関数を呼び出して、 RichTextBoxEx の Win32 コントロールからキャッシュされたデータを解放します。

新しいクラスへの書式設定の追加

RichTextBox クラスの WYSIWYG 印刷出力が使用可能になったので、RichTextBox に不足している機能をさらに追加できますが、Win32 リッチ エディット コントロールには、選択範囲の書式設定の一部のみを変更できます。

Standard RichTextBox には、テキストの一部のフォントを取得および設定するための SelectionFont プロパティが用意されています。 問題は、異なるフォントを持つ選択範囲が存在する場合、 SelectionFont を取得すると null (VB では Nothing ) が返され、他のフォント プロパティも指定せずに、選択したテキスト (太字など) の書式を設定することはできません。

ただし、ここでも、リッチ エディット コントロールの Win32 メッセージを使用して、 EM_GETCHARFORMATEM_SETCHARFORMATの動作を実現できます。

必要な定義と構造体を クラスに追加するプロセスは、前に示したのと非常によく似ています。 CHARFORMAT という名前の構造体を見ると、唯一のトリッキーな部分が発生します。

typedef struct _charformat { 
    UINT     cbSize; 
    DWORD    dwMask; 
    DWORD    dwEffects; 
    LONG     yHeight; 
    LONG     yOffset; 
    COLORREF crTextColor; 
    BYTE     bCharSet; 
    BYTE     bPitchAndFamily; 
    TCHAR    szFaceName[LF_FACESIZE]; 
} CHARFORMAT;

要素 szFaceName は、 LF_FACESIZE (つまり 32) TCHARs の固定配列として定義されます。 .NET の構造体を宣言するときは、配列の固定サイズを指定できないため、アンマネージ コードで使用するためにこのような配列をマーシャリングする方法を相互運用サービスに指示する必要があります。 もう一度、これを行う属性があります: MarshalAsAttribute

この属性を使用すると、マーシャリングに使用する型とサイズを非常に正確に指定できます。 この場合、配列は 32 個の要素の固定サイズ (LF_FACESIZE) を持つアンマネージ ByValArray として扱われます。 要素型 (TCHAR) は char の既定のマーシャリング型の 1 つであるため、追加の作業は必要ありません。

完全な定義を次に示します。

// C#
[ StructLayout( LayoutKind.Sequential )]
private struct STRUCT_CHARFORMAT
{
    public int    cbSize; 
    public UInt32 dwMask; 
    public UInt32 dwEffects; 
    public Int32  yHeight; 
    public Int32  yOffset; 
    public Int32   crTextColor; 
    public byte   bCharSet; 
    public byte   bPitchAndFamily; 
    [MarshalAs(UnmanagedType.ByValArray, SizeConst=32)]
    public char[] szFaceName; 
}
' VB.NET
<StructLayout(LayoutKind.Sequential)> _
Private Structure STRUCT_CHARFORMAT
    Public cbSize As Integer
    Public dwMask As UInt32
    Public dwEffects As UInt32
    Public yHeight As Int32
    Public yOffset As Int32
    Public crTextColor As Int32
    Public bCharSet As Byte
    Public bPitchAndFamily As Byte
    <MarshalAs(UnmanagedType.ByValArray, SizeConst:=32)> _
    Public szFaceName As Char()
End Structure

残りはもう一度、FormatRange()の実装と同様です。すべての書式設定オプションとフラグに必要な定数を検索して追加し、必要な構造体を作成して入力し、SendMessage() をEM_GETCHARFORMATまたはEM_SETCHARFORMATで呼び出します。

まとめ

を使用します。NET のWindows フォーム名前空間には、豊富なユーザー インターフェイスを構築するための多くのクラスがあります。 これらのクラスのほとんどは、対応する MFC クラスとよく似ていますが、MFC で使用できる機能が不足している場合があります。 この説明に示す手法を使用すると、相互運用サービスに頼り、マネージド コードから SendMessage() を呼び出すことで、Win32 コントロールの基本が提供するすべての機能にアクセスできるようになります。

著者について

Martin Müller は、 4voice AG の開発リーダーです。 現時点では、Martin は音声認識の使用を研究し、医療サービスと医師向けの音声対応ソリューションを開発しています。 Martin は 、こちらからメールでアクセスできます。