2010-11-23 9 views
3

Donc je construis un contrôle personnalisé en C# (pas WPF), et je veux fondamentalement implémenter la mise en surbrillance du texte avec la souris.Trouver un caractère dans une chaîne tracée par l'utilisateur à un point donné

Comment puis-je trouver efficacement le caractère à un Point donné (disons où la souris est cliquée) dans une chaîne? J'ai le rectangle de disposition de la chaîne telle qu'elle a été dessinée et je pourrais calculer la longueur de la chaîne jusqu'à chaque caractère jusqu'à ce que je trouve celle qui est la plus proche de l'endroit où la souris est cliquée ... mais il doit y avoir un meilleur moyen. Aucune suggestion?

+0

Cela fera le travail pour vous. –

+0

Est-ce que vous dérivez ce contrôle de Textbox? – Scottie

+0

@Scottie - Non, il hérite uniquement de UserControl. RichTextBox et TextBox ne sont pas tout à fait ce dont j'ai besoin, et je suis vraiment juste curieux d'apprendre comment construire mes propres bons contrôles personnalisés. – climbage

Répondre

3

Si je devais le faire, je regarderais en arrière.
Je garderais le texte entré comme un membre string dans le contrôle, donc à tout moment je sais ce qui est réellement entré dans le contrôle (comme la propriété Text dans un TextBox).
Ensuite, je voudrais utiliser la méthode TextRenderer.MeasureText() (http://msdn.microsoft.com/en-us/library/7sy6awsb.aspx) et je continuerais à mesurer la longueur de la chaîne à plusieurs reprises jusqu'à ce que je passe la coordonnée X de la souris dans le contrôle, à droite, je sais combien de caractères sont choisis. Par exemple, supposons que l'utilisateur ait écrit le texte Hello dans le contrôle.
Et Coordonnée X frappé à droite entre le l et le o, ce qui pourrait être la valeur 20.
Alors j'appeler à plusieurs reprises MeasureText() sur les chaînes suivantes:

  • H: largeur de 5 pixels.
  • He: largeur de 10 pixels.
  • Hel: largeur de 14 pixels.
  • Hell: largeur de 17 pixels.
  • Hello: largeur de 22 pixels.

alors je sais que la souris a été frappé entre le l et le o, donc je puis mettre en évidence le texte Hell.

Désolé pour l'exemple de mauvais goût =)

MISE À JOUR:
Vous pouvez optimiser ce un peu en calculant les longueurs de façon semblable recherche binaire arbre.
Tout comme si vous recherchiez un nom dans le répertoire, vous ne regardez pas page par page, mais vous le scindez en deux au fur et à mesure, vous vous rapprochez de plus en plus entre ces deux pages.
De même, en particulier pour les longues valeurs de chaîne du contrôle, calculez la largeur de la chaîne entière, puis la moitié de sa longueur, et divisez-la. Je pense que ce serait O(n log n) à ce moment-là.
Bien sûr, il serait O(1) si le texte est de largeur fixe =)

+0

Ouais c'est à peu près ce que je fais maintenant. Je peux juste devoir garder cette solution si je ne peux pas trouver un moyen plus efficace. – climbage

+0

Oh et le texte peut envelopper plusieurs lignes. – climbage

+0

@climbage; ce serait un peu un défi. Vous devez d'abord traduire la coordonnée Y dans l'index du personnage qui commence cette ligne, puis vous suivez la même logique ci-dessus. – BeemerGuy

2

Une autre chose que vous pouvez faire pour construire sur une grande suggestion de BeemerGuy est de précalculer un tableau de décalages. Lorsque la chaîne est modifiée (les types d'utilisateur ou la propriété est définie dans le code), vous pouvez recalculer le tableau de décalage. Cela vous permettra d'économiser l'appel à MeasureFont sur les clics de souris et rendra la recherche du caractère trivial. Au fond, vous itérez le tableau jusqu'à ce que vous trouviez le caractère le plus proche.Puisque les offsets sont implicitement triés par valeur, vous pouvez même utiliser une recherche binaire pour le rendre plus efficace. Vous ne pouvez pas utiliser un contrôle `TextBox` en lecture seule?

+0

J'aime ça; c'est plus comme de l'indexation. C'est un bon moyen de l'optimiser, surtout quand on parle d'interactivité en temps réel; une réactivité rapide est obligatoire là-bas! – BeemerGuy

+0

Le seul problème avec cela est que le mot enveloppement de mon texte peut changer à tout moment afin que les décalages de chaque personnage change, sauf si vous avez un moyen de contourner cela. – climbage

+0

@climbage; En fait, ce serait idéal pour votre cas multi-ligne; vous créez les offsets au début de chaque ligne, puis vous traduisez simplement Y dans le numéro de ligne du tableau, et combinez cela avec la logique de ma réponse. – BeemerGuy