C1TextPointer クラスは、C1Document 内の位置を表します。その目的は、C1Document の走査と操作を容易にすることです。この機能は WPF の TextPointer クラスに似ていますが、オブジェクトモデルには多くの違いがあります。
C1TextPointer は、C1TextElement およびその内部のオフセットによって定義されます。ここでは、このドキュメントを例にして説明します。
上の青色の角かっこで囲まれたノードは C1TextElement で、C1TextPointer のオフセットは、その位置がどの子の間にあるかを示します。たとえば、上の C1Document をオフセット0でポイントする位置は最初の C1Paragraph の直前、オフセット1は2つの段落の間、オフセット2は2番目の段落の後を示します。C1TextPointer が C1Run をポイントする場合は、そのテキスト内の各文字が C1Run の子と見なされ、オフセットはテキスト内の位置を示します。C1InlineUIContainer は子を1つだけ持つ(それが表示する UIElement)と見なされ、その子の前と後という2つの位置があります。
ドキュメントを一連のシンボルとして可視化する方法もあります。ここで、シンボルは要素タグまたは何らかのタイプのコンテンツになります。要素タグは、要素の開始または終了を示します。XML では、上のドキュメントは次のように記述されます。
XAML |
コードのコピー
|
---|---|
<C1Document> <C1Paragraph> <C1Run>CAT</C1Run> <C1InlineUIContainer><UI/></C1InlineUIContainer> </C1Paragraph> <C1Paragraph> <C1Run>DOG</C1Run> </C1Paragraph> </C1Document> |
このようにドキュメントを表示する場合、C1TextPointer はタグやコンテンツの間の位置をポイントします。このビューは、C1TextPointer に明確な順序も提供します。実際、C1TextPointer は IComparable を実装し、便宜のために比較演算子もオーバーロードします。
C1TextPointer の後にあるシンボルは、Symbol プロパティを使用して取得できます。このプロパティは、StartTag、EndTag、char、UIElement のいずれかのタイプのオブジェクトを返します。
ドキュメント内の位置を反復処理する場合は、GetPositionAtOffset と Enumerate という2つのメソッドを使用できます。GetPositionAtOffset は低レベルのメソッドで、単に指定された整数オフセットの位置を返します。Enumerate は、位置を反復処理する場合にお勧めする方法です。このメソッドは、指定された方向にすべての位置に対して反復処理を行う IEnumerable を返します。たとえば、次のコードはドキュメントのすべての位置を返します。
コードのコピー
|
|
---|---|
document.ContentStart.Enumerate() |
ContentStart は C1TextElement の最初の C1TextPointer を返します。最後の位置を返す ContentEnd プロパティもあります。
Enumerate の興味深い点は、必要に応じて列挙を返すことです。つまり、IEnumerable が反復処理される場合にのみ C1TextPointer オブジェクトが作成されます。これにより、フィルタ処理、検索、選択などのための LINQ 拡張メソッドを効率的に使用できます。たとえば、C1TextPointer の下に含まれる単語に対応する C1TextRange を取得するとします。次の手順を実行します。
Enumerate メソッドは、指定された方向で位置を検索して返しますが、現在の位置は含まれません。したがって、コードはパラメータの位置が単語の先頭かどうかを最初にチェックし、そうでない場合は逆方向に単語の先頭を検索します。単語の末尾についても同様に、パラメータの位置をチェックしてから順方向に検索します。パラメータの位置を含む単語を探しているので、順方向に移動して最初の単語の末尾を求め、逆方向に移動して最初の単語の先頭を求めます。C1TextPointer には、周囲のシンボルに基づいてその位置が単語の先頭または末尾かどうかを判別する IsWordStart プロパティと IsWordEnd プロパティが既に用意されています。ここでは、First LINQ 拡張メソッドを使用して、必要な述語を満たす最初の位置を探しています。最後に、2つの位置から C1TextRange を作成します。
LINQ 拡張メソッドは、位置を操作する際に大いに役立ちます。別の例として、次のようにするとドキュメント内の単語をカウントできます。
IsWordStart は、正確には単語の先頭ではない位置に対しても True を返すため、単語の先頭に続くシンボルが char かどうかをチェックする必要があることに注意してください。たとえば、C1Run の最初の位置が単語の先頭の場合、C1Run の開始タグの直前の位置は単語の先頭と見なされます。
もう1つの例として、Find メソッドを実装してみます。
指定された位置から単語を見つけるために、順方向にすべての位置を列挙し、テキストの長さが word.Length のすべての範囲を選択します。それぞれの位置に対して、word.Length の距離にある位置を探します。そのために、GetPositionAtOffset メソッドを使用します。このメソッドは、指定されたオフセットの位置を返しますが、すべてのインラインタグも有効な位置として返します。ここでは、単語が2つの C1Run 要素の間で分割されている場合を考慮するために、これを無視する必要があります。C1TextRange.TextTagFilter を使用するのはそのためです。これは、内部ロジックでドキュメントツリーをテキストに変換する際に使用されるフィルタメソッドと同じです。最後の手順として、テキストが検索対象の単語に一致する範囲を検索します。
最後の例として、最初に見つかった単語を置換します。
この例では、まず単語を検索し、次に Text プロパティを割り当ててテキストを置換します。