/ / Come posso ottenere un TextPointer da un clic del mouse in un FlowDocument - .net, wpf, flowdocument

Come posso ottenere un oggetto TextPointer con un clic del mouse in un oggetto FlowDocument - .net, wpf, flowdocument

Vorrei ottenere la parola su cui un utente ha fatto clic in un FlowDocument.

Attualmente sto aggiungendo un gestore di eventi a tuttiEsegui nel documento ed esegui l'iterazione attraverso i TextPointers nell'Esegui su cui è stato fatto clic, chiamando GetCharacterRect () su ciascuno e verificando se il rettangolo contiene il punto.

Tuttavia, quando si verifica un clic vicino alla fine di una lunga corsa, ci vogliono> 10 secondi.

Esiste un metodo più efficiente?

risposte:

6 per risposta № 1

Direi che il modo più semplice è usare le interfacce di automazione:

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);

L'utilizzo di ITextProvider richiede un riferimento al'assembly UIAutomationProvider. Questo assembly non è comunemente referenziato, quindi potrebbe essere necessario aggiungerlo. Saranno necessari anche UIAutomationTypes per utilizzare alcuni dei suoi metodi.

Si noti che esistono molte opzioni per la creazione del peer di automazione in base alla modalità di presentazione di 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);

Aggiornare

Ho provato questo e funziona bene, anche se la conversione da ITextRangeProvider a TextPointer si è rivelata più difficile di quanto mi aspettassi.

Ho impacchettato l'algoritmo in un metodo di estensione ScreenPointToTextPointer per un facile utilizzo. Ecco un esempio di come il mio metodo di estensione può essere utilizzato per mettere in grassetto tutto il testo prima del puntatore del mouse e annullare il grassetto per tutto il testo dopo di esso:

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);
}

Ecco il codice per il metodo di estensione:

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;
}

}

Si noti inoltre l'uso di PointToScreen nel metodo MouseMove di esempio per far passare un punto dello schermo nel metodo di estensione.


1 per risposta № 2

Se FlowDocument è quello di un RichTextBox, è possibile utilizzare GetPositionFromPoint () metodo per ottenere TextPointer.


0 per risposta № 3

Gli eventi Click del mouse sono gorgogliati verso l'alto,invece puoi semplicemente agganciare PreviewMouseLeftButtonUp nel tuo documento e guardare il mittente / l'origine originale dell'evento, otterrai la Run che ti ha inviato l'evento.

Quindi puoi RangeFromPoint e puoi usare,

PointToScreen che convertirà il punto del mouse locale in punto globale.