2010-03-26 12 views
4

J'essaie de reproduire la disposition de certains formulaires papier dans une application WPF. Les étiquettes pour les zones de texte doivent être "en ligne" avec le contenu des zones de texte, plutôt que "en dehors" comme les formulaires Windows normaux. Ainsi, avec une étiquette Xxxxxx:Etiquettes de zone de texte en ligne avec WPF

+-----------------------------+ 
| Xxxxxx: some text written | 
| in the multiline input.  | 
|        | 
| another paragraph continues | 
| without indentation.  | 
|        | 
|        | 
+-----------------------------+ 

Le Xxxxxx ne peut pas être modifiable, si l'utilisateur sélectionne tout le contenu de la zone de texte, l'étiquette doit rester désélectionnée, je dois être en mesure de style de la couleur du texte/mise en forme de l'étiquette séparément, lorsqu'il n'y a pas de texte dans la zone de texte, mais qu'elle a le focus, le curseur devrait clignoter juste après l'étiquette, et j'ai besoin des lignes de base du texte dans la zone de texte et l'étiquette.

Une solution que j'ai essayée consistait à placer partiellement un bloc de texte sur l'entrée, puis à utiliser le retrait de texte pour mettre en retrait le texte modifiable, mais cela a causé des problèmes avec les paragraphes suivants. Je ne suis pas sûr de savoir comment indenter seulement le premier paragraphe. Il a fallu quelques manipulations pour aligner le texte - une configuration plus fiable serait idéale.

Donc, des suggestions sur la façon de configurer cela?

Merci

Répondre

1

Eh bien, je peux suggérer une manière un peu hackish de le faire.

D'abord, notez que vous pouvez placer des éléments d'interface utilisateur dans un FlowDocument. Alors, qui fait quelque chose comme cela possible:

<RichTextBox> 
    <FlowDocument> 
    <Paragraph> 
     <InlineUIContainer> 
     <TextBlock>This is your label: </TextBlock> 
     </InlineUIContainer> 
     <Run>And this is the editable text.</Run> 
    </Paragraph> 
    </FlowDocument> 
</RichTextBox> 

Le problème devient maintenant l'utilisateur de garder l'édition du InlineUIContainer. C'est vraiment deux problèmes. Le premier problème est de garder l'utilisateur de en sélectionnant. Pour ce faire, vous devez gérer l'événement SelectionChanged. Dans l'événement, trouvez le premier InlineUIContainer dans le document du RTB, et si Selection.Start est avant cela, changez-le.

private void RichTextBox_SelectionChanged(object sender, RoutedEventArgs e) 
{ 
    RichTextBox rtb = (RichTextBox) sender; 
    if (rtb == null) return; 

    InlineUIContainer c = rtb.Document 
     .Blocks 
     .Where(x => x is Paragraph) 
     .Cast<Paragraph>() 
     .SelectMany(x => x.Inlines) 
     .Where(x => x is InlineUIContainer) 
     .Cast<InlineUIContainer>() 
     .FirstOrDefault(); 

    if (c == null) return; 

    if (rtb.Selection.Start.CompareTo(c.ElementEnd) < 0) 
    { 
     rtb.Selection.Select(c.ElementEnd, rtb.Selection.End); 
    } 
} 

Il existe probablement un moyen plus simple de formuler cette requête LINQ, mais je l'apprécie. Et ce n'est pas parfait à 100%; Si vous sélectionnez à l'intérieur du texte et faites glisser à gauche sur le TextBlock, il perdra la sélection. Je suis sûr que cela peut être réparé. Mais ça marche plutôt bien. Il gère même le cas où l'utilisateur navigue avec les touches fléchées.

Juste ce que vous obtenez presque tout le chemin là-bas. L'autre chose qui peut vous déranger, cependant, est si l'utilisateur positionne le curseur au tout début du texte et appuie sur BACKSPACE.

manutention qui nécessite quelque chose de similaire: comparer la position du caret à la fin de la première InlineUIElement, et annuler le retour arrière (en indiquant l'événement traité) si les années caret à cette position:

private void RichTextBox_PreviewKeyDown(object sender, KeyEventArgs e) 
{ 
    if (e.Key != Key.Back) 
    { 
     return; 
    } 

    RichTextBox rtb = (RichTextBox)sender; 
    if (rtb == null) return; 

    InlineUIContainer c = rtb.Document 
     .Blocks 
     .Where(x => x is Paragraph) 
     .Cast<Paragraph>() 
     .SelectMany(x => x.Inlines) 
     .Where(x => x is InlineUIContainer) 
     .Cast<InlineUIContainer>() 
     .FirstOrDefault(); 

    if (c == null) return; 

    if (rtb.CaretPosition.CompareTo(c.ElementEnd.GetInsertionPosition(LogicalDirection.Forward)) <= 0) 
    { 
     e.Handled = true; 
    }    
} 
Questions connexes