FlowDocument でユーザーがクリックした単語を取得したいと思います。
現在、ドキュメント内のすべての Run にイベント ハンドラーを追加し、クリックされた Run 内の TextPointers を反復処理し、それぞれで GetCharacterRect() を呼び出し、四角形にポイントが含まれているかどうかを確認しています。
ただし、長いランの終わり近くでクリックが発生すると、これには 10 秒以上かかります。
より効率的な方法はありますか?
FlowDocument でユーザーがクリックした単語を取得したいと思います。
現在、ドキュメント内のすべての Run にイベント ハンドラーを追加し、クリックされた Run 内の TextPointers を反復処理し、それぞれで GetCharacterRect() を呼び出し、四角形にポイントが含まれているかどうかを確認しています。
ただし、長いランの終わり近くでクリックが発生すると、これには 10 秒以上かかります。
より効率的な方法はありますか?
最も簡単な方法は、オートメーション インターフェイスを使用することだと思います。
using System.Windows.Automation.Peers;
using System.Windows.Automation.Provider;
FlowDocument flowDocument = ...;
Point point = ...;
var peer = new DocumentAutomationPeer(flowDocument);
var textProvider = (ITextProvider)peer.GetPattern(PatternInterface.Text);
var rangeProvider = textProvider.RangeFromPoint(point);
ITextProvider を使用するには、UIAutomationProvider アセンブリへの参照が必要です。このアセンブリは一般的に参照されないため、追加する必要がある場合があります。一部のメソッドを使用するには、UIAutomationTypes も必要になります。
FlowDocument の表示方法に応じて、自動化ピアを作成するための多くのオプションがあることに注意してください。
var peer = new DocumentAutomationPeer(flowDocument);
var peer = new DocumentAutomationPeer(textBlock);
var peer = new DocumentAutomationPeer(flowDocumentScrollViewer);
var peer = new TextBoxAutomationPeer(textBox);
var peer = new RichTextBoxAutomationPeer(richTextBox);
アップデート
これを試してみたところうまくいきましたが、ITextRangeProvider から TextPointer への変換は予想以上に難しいことがわかりました。
ScreenPointToTextPointer
簡単に使用できるように、アルゴリズムを拡張メソッドにパッケージ化しました。これは、私の拡張メソッドを使用して、マウス ポインターの前のすべてのテキストを太字にし、その後のすべてのテキストを太字にする方法の例です。
private void Window_MouseMove(object sender, MouseEventArgs e)
{
var document = this.Viewer.Document;
var screenPoint = PointToScreen(e.GetPosition(this));
TextPointer pointer = document.ScreenPointToTextPointer(screenPoint);
new TextRange(document.ContentStart, pointer).ApplyPropertyValue(TextElement.FontWeightProperty, FontWeights.Bold);
new TextRange(pointer, document.ContentEnd).ApplyPropertyValue(TextElement.FontWeightProperty, FontWeights.Normal);
}
拡張メソッドのコードは次のとおりです。
using System.Windows.Automation.Peers;
using System.Windows.Automation.Provider;
using System.Windows.Automation.Text;
public static class DocumentExtensions
{
// Point is specified relative to the given visual
public static TextPointer ScreenPointToTextPointer(this FlowDocument document, Point screenPoint)
{
// Get text before point using automation
var peer = new DocumentAutomationPeer(document);
var textProvider = (ITextProvider)peer.GetPattern(PatternInterface.Text);
var rangeProvider = textProvider.RangeFromPoint(screenPoint);
rangeProvider.MoveEndpointByUnit(TextPatternRangeEndpoint.Start, TextUnit.Document, 1);
int charsBeforePoint = rangeProvider.GetText(int.MaxValue).Length;
// Find the pointer that corresponds to the TextPointer
var pointer = document.ContentStart.GetPositionAtOffset(charsBeforePoint);
// Adjust for difference between "text offset" and actual number of characters before pointer
for(int i=0; i<10; i++) // Limit to 10 adjustments
{
int error = charsBeforePoint - new TextRange(document.ContentStart, pointer).Text.Length;
if(error==0) break;
pointer = pointer.GetPositionAtOffset(error);
}
return pointer;
}
}
また、サンプルの MouseMove メソッドで PointToScreen を使用して、拡張メソッドに渡すスクリーン ポイントを取得していることにも注意してください。
FlowDocument が RichTextBox の場合、GetPositionFromPoint()メソッドを使用して TextPointer を取得できます。
マウス クリック イベントは上部にバブルされます。代わりに、PreviewMouseLeftButtonUp をドキュメントにフックして、イベントの送信者/元のソースを監視するだけで、イベントを送信した Run を取得できます。
次に、RangeFromPoint を使用して、
ローカル マウス ポイントをグローバル ポイントに変換する PointToScreen 。