none
WPF Richtextbox: Textselection saven und wiederherstellen RRS feed

  • Frage

  • Hallo zusammen!

    Mir gelingt es nicht, in einer WPF-RichtTextBox den Textcursor und die aktuelle Selektion abzuspeichern und nach diversen Aktionen wieder herzustellen
    Eigentlich eine triviale Aufgabe, aber es funktioniert nicht.

    Fehlerbeschreibung:
    In einem neuen WPF-Projekt einfach eine RichtextBox in ein Grid einfügren:
    <Grid>
      <RichTextBox Height="243" HorizontalAlignment="Left" Margin="42,24,0,0" Name="EditRtf" VerticalAlignment="Top" Width="211" SelectionChanged="richTextBox1_SelectionChanged" />
    </Grid>

    Im CodeBehind einen Trigger setzen, der bei jeder Selektionsänderung anspringt:
    private void richTextBox1_SelectionChanged(object sender, RoutedEventArgs e)
    {
      TextPointer TPStart = null;
      TextPointer TPEnd = null;

      if (bGettingType == true)
        return;
      bGettingType = true;

      // Save actual selection
      TPStart = EditRtf.Selection.Start;
      TPEnd = EditRtf.Selection.End;
     
      // Do something in the document with textpointer and textranges .....
      // ...


      // restore text selection to state of starting this procedure
      EditRtf.Selection.Select(TPStart, TPEnd);

      bGettingType = false;
    }

    Das Problem:
    Schon mit diesem einfachen Beispielprogramm läßt sich das Problem nachstellen.
    Zuerst wird bei einem Selectionswechsel die Routine aufgerufen und die beiden Textpointer für Start und Ende der aktuellen Selektion abgespeichert.

    Dann folgen normalerweise diverse Aktion die den Text parsen, hier aber zum Nachvollziehen des Porblems nicht weiter ausgeführt sind.

    Nach den Aktionen soll die Selektion wieder mit "EditRtf.Selection.Select(TPStart, TPEnd);" hergestellt werden.

    Das Egebnis sieht auf den ersten Blick gut aus, die Selektion wird wieder richtig gesetzt. Aber das Verhalten ist dennoch anders: Zieht man einen Selektionsblock in einem RTF Text von oben nach unten mit der Maus oder Shift-Pfeiltaste auf, so wird der Textblock wie erwartet selektiert.
    Starte ich eine Selektion aber mit der Maus oder Shift+Pfeiltaste oben, so werden immer nur die letzten Buchstaben oder Worte selektiert, 

    Tausche ich die beiden Textpointer um in "EditRtf.Selection.Select(TPEnd, TPStart);", so kann ich nun von unten nach oben selektieren aber nicht mehr von oben nach unten.


    Im Grunde will ich nur einen funktionierenden Save/Restore-Modus für die Richtextbox haben, aber sämtliche Verusche in dieser Richtung (auch andere Vorschläge aus den Foren) führen zu dem unerwarteten Ergebnis.

    Ich hoffe, mir kann noch geholfen werden !  Danke, jochen

    Freitag, 7. Januar 2011 14:08

Antworten

  • Hi Jochen,

    OK, auf die Weise kann ich Dein Problem nachvollziehen und versteh's nun auch endlich. :-)

    Das grundsätzliche Problem bzw. Ursache des Ganzen dürfte sicherlich sein, dass Du die Selection innerhalb genau dieses Events änderst (und sei's nur durch Save=>Restore); eine genaue Ursache für das Verhalten selbst kann ich nicht nennen, aber evt. passiert seitens des Frameworks noch etwas, nachdem der Event-Code beendet wurde. Es stellt sich also weiterhin die Frage, warum Du die Selection überhaupt "anfassen" musst, wenn Du doch gar nichts ändern musst ..? IOW, ich würde versuchen, ohne Änderung der Selection innerhalb des Events auszukommen.

    Außerdem (oder alternativ) scheint mir das in allererster Linie ein Problem der Direction zu sein, denn wenn ich die Maus nicht, nachdem ich mehrmals nach unten gezogen habe, nach oben ziehe (sprich, rückwärts selektiere) gibt's gar kein Problem damit.
    IOW, wenn Du im Code die Selection umdrehst (aus rtbEdit.Selection.Select(SelStart2, SelEnd2); mach rtbEdit.Selection.Select(SelEnd2, SelStart2); ), "dreht" sich auch das Problem. Evt. lässt sich Dein Vorhaben ergo über .Select() allein ganz einfach nicht lösen. Vielleicht gibt's eine Möglichkeit, im Event die Direction abzufragen, mit der die Auswahl erfolgte, ich finde aber ad hoc keine passende Eigenschaft oder Methode dafür (bin auch nicht wirklich allzu versiert mit RTB-/FlowDocument). Das wäre aber die Richtung, in der ich weitersuchen würde.


    Cheers,
    Olaf
    http://blogs.intuidev.com
    Dienstag, 11. Januar 2011 18:04

Alle Antworten

  • Hallo Jochen,

    eventuell muss man bei Select(a,b) die Parameter so wählen, dass a < b ist. Wenn du die LogicalDirection der Selection abfragst, kann Du die Reihenfolge eventuell tauschen, wenn sie absteigend ist.

    Gruß

    Jus

    Freitag, 7. Januar 2011 18:51
  • Hi Jochen,

    ich kann Dein Problem nicht nachvollziehen. Das Beispiel, das Du gepostet hast, ist an sich nicht lauffähig, also musst Du tatsächlich schon eine andersgeartete Variante getestet haben. Nachfolgend mal ein allgemeines Beispiel - das funktioniert hier absolut problemlos:

    <Window x:Class="WpfTests.RichTextBox_StoreRestoreSelection"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="RichTextBox_StoreRestoreSelection" 
        Height="500" Width="500"
        FocusManager.FocusedElement="{Binding rtbEdit}">
      <Grid>
       <Grid.RowDefinitions>
         <RowDefinition Height="*"/>
         <RowDefinition Height="Auto"/>
       </Grid.RowDefinitions>
    
       <RichTextBox Margin="10" Name="rtbEdit">
         <FlowDocument>
          <Paragraph Name="myParagraph">
            <Run>
             Sed ut perspiciatis, unde omnis iste natus error sit voluptatem accusantium doloremque laudantium, 
             totam rem aperiam eaque ipsa, quae ab illo inventore veritatis et quasi architecto beatae vitae dicta sunt, explicabo. 
             Nemo enim ipsam voluptatem, quia voluptas sit, aspernatur aut odit aut fugit, sed quia consequuntur magni dolores eos, 
             qui ratione voluptatem sequi nesciunt, neque porro quisquam est, qui dolorem ipsum, quia dolor sit amet, consectetur, 
             adipisci[ng] velit, sed quia non numquam [do] eius modi tempora inci[di]dunt, ut labore et dolore magnam aliquam quaerat
             voluptatem. Ut enim ad minima veniam, quis nostrum exercitationem ullam corporis suscipit laboriosam, nisi ut aliquid ex
             ea commodi consequatur? Quis autem vel eum iure reprehenderit, qui in ea voluptate velit esse, quam nihil molestiae 
             consequatur, vel illum, qui dolorem eum fugiat, quo voluptas nulla pariatur?
            </Run>
          </Paragraph>
    
          <Paragraph>
            <Run>
             At vero eos et accusamus et iusto odio dignissimos ducimus, qui blanditiis praesentium voluptatum deleniti atque corrupti, 
             quos dolores et quas molestias excepturi sint, obcaecati cupiditate non provident, similique sunt in culpa, qui officia 
             deserunt mollitia animi, id est laborum et dolorum fuga. Et harum quidem rerum facilis est et expedita distinctio. 
             Nam libero tempore, cum soluta nobis est eligendi optio, cumque nihil impedit, quo minus id, quod maxime placeat, facere 
             possimus, omnis voluptas assumenda est, omnis dolor repellendus. Temporibus autem quibusdam et aut officiis debitis aut 
             rerum necessitatibus saepe eveniet, ut et voluptates repudiandae sint et molestiae non recusandae. Itaque earum rerum hic 
             tenetur a sapiente delectus, ut aut reiciendis voluptatibus maiores alias consequatur aut perferendis doloribus asperiores
             repellat...         
            </Run>
          </Paragraph>      
         </FlowDocument>
       </RichTextBox>
    
       <UniformGrid Columns="2" Grid.Row="1">
         <Button x:Name="cmdSaveSel" Content="Save selection"
             Click="cmdSaveSel_Click" Margin="5" Padding="3"/>
         <Button x:Name="cmdRestoreSel" Content="Restore selection"
             Click="cmdRestoreSel_Click" Margin="5" Padding="3"/>
       </UniformGrid>
      </Grid>
    </Window>
    
    

     

    Code-behind:

    using System.Windows;
    using System.Windows.Documents;
    
    namespace WpfTests
    {
    	public partial class RichTextBox_StoreRestoreSelection : Window
    	{
    		TextPointer SelStart { get; set; }
    		TextPointer SelEnd { get; set; }
    
    		public RichTextBox_StoreRestoreSelection()
    		{
    			InitializeComponent();
    		}
    
    		void WindowSelectionSettings_SettingsSaving(object sender, System.ComponentModel.CancelEventArgs e)
    		{
    		}
    
    		private void cmdSaveSel_Click(object sender, RoutedEventArgs e)
    		{
    			SelStart = rtbEdit.Selection.Start;
    			SelEnd = rtbEdit.Selection.End;
    			rtbEdit.Focus();
    		}
    
    		private void cmdRestoreSel_Click(object sender, RoutedEventArgs e)
    		{
    			if (SelStart != null && SelEnd != null)
    			{
    				rtbEdit.Selection.Select(SelStart, SelEnd);
    				rtbEdit.Focus();
    			}
    		}
    	}
    }
    
    

    Falls Du auch damit Dein Problem nachvollziehen kannst, beschreib dies doch mal genauer.


    Cheers,
    Olaf
    http://blogs.intuidev.com
    Samstag, 8. Januar 2011 15:16
  • Hi Olaf,

    Dein Beispiel arbeitet exakt mit den gleichen Befehlen wie mein Beispiel und es funktioniert auch prinzipiell wenn man es mit den Buttons ansteuert.

    Mein Problem ist aber, dass ich das Sichern der Textpointer und das restoren der Textpointer in einer Eventbehandlung von Selchange mache. Dann tritt das Problem auch mit deiner Lösung auf. Immer wenn der Benutzer mit Maus oder Pfeiltasten den Cursor bewegt, so muss ich den Text neu interpretieren und parsen. Das ganze dient dazu, rechts des Textes dem Benutzer anzuzeigen, in welchem Teil er sich bewegt und dort auch Einfluss auf den Inhlat dehmen kann.

    Ich habe das mal gefilmt und auf YouTube gelegt http://www.youtube.com/watch?v=7DFv-OkK5WY

    Zuerst klicke ich in den Text und ziehe bei gedrückter linker Maustaste die Maus etwas nach unten. -> Funktioniert. Im Film mache ich das 3 Mal hinerheiander.

    Dann klicke ich aber wieder mit der Maus in den text, ziehe aber diesmal zur Selektion 3mal mit gedrückter Maustaste nach oben und dann wird eben nicht mehr der Text nach dem Mausklick selekiert sondern die Selektion wandert mit nach oben. Im Filmchen mache ich das 4 mal hintereinander.

    Sieht man den Effekt im Film ?

    Ich habe deinen Code ergänzt wie folgt (Die Eventhandler für die beiden Buttons habe ich weggelassen, die spielen hier keine Rolle)

    XAML:
    <RichTextBox Margin="10" Name="rtbEdit" SelectionChanged="rtbEdit_SelectionChanged">
      <FlowDocument>.... 

    Hier noch der CodeBehind mit der Eventbehandlung. Die Klassenvariable bGettingType dient dazu, dass sich die Eventroutine nicht selbst wieder aufruft, da es sonst zu einem StackOverflow kommt).

    public partial class RichTextBox_StoreRestoreSelection : Window
    {
     bool bGettingType = null;
     TextPointer SelStart { get; set; }
     TextPointer SelEnd { get; set; } 
    
    
     public RichTextBox_StoreRestoreSelection()
     {
      InitializeComponent();
      bGettingType = false;
     }
    
     private void rtbEdit_SelectionChanged(object sender, RoutedEventArgs e)
     {
      if (bGettingType == true)
       return; 
    
      bGettingType = true;
      SelStart = rtbEdit.Selection.Start;
      SelEnd = rtbEdit.Selection.End;
      rtbEdit.Focus(); 
    
    	// Do something in the document with textpointer and textranges .....
      // ...
      // restore text selection to state of starting this procedure
      if (SelStart != null && SelEnd != null)
      {
       rtbEdit.Selection.Select(SelStart, SelEnd);
       rtbEdit.Focus();
      }
      bGettingType = false;
     }
    [....]
    }

     

    Olaf, ich hoffe, ich konnte mein Problem mit dem Film etwas besser rüberbringen. Das problem entsteht scheinbar erst durch die Verwendung in der SelChanged Eventbehandlung.

    Danke vorab an alle, Jochen

    Montag, 10. Januar 2011 12:06
  • Hi Jürgen,

    diesen guten Gedanken hatte ich auch schon und er dürfte auch funktionieren wenn ich den beiden Textpointer entnehmen könnte, welcher am Anfang und welcher am Ende ist.

    In der Praxis zeigt die logicalDirection des Startpointers aber IMMER backward und der Endpointer immer forward an. Egal ob man die selektion mit der Maus oder Tastatur von oben nach unten oder von unten nach oben erstellt.
    Mein zweiter Gedanke war, die beiden Pointer zu vergleichen also:

    if (SelStart.CompareTo(SelEnd) < 0)
     rtbEdit.Selection.Select(SelStart, SelEnd);
    else
     rtbEdit.Selection.Select(SelEnd, SelStart);



    Aber Compare liefert leider auch immer -1. Warum, das verstehe ich auch nicht. Die beiden Textpointer werden scheinbar schon mit "rtbEdit.Selection.Start;" sortiert.

    Also fehlt mir schlichtweg der Anhaltspoint ob von oben nach unten oder von unten nach oben selektiert wurde.

    Ich denke, dass ich nicht der einzige bin, der innerhalb des SelectChanged Events die Textpointer manipulieren muß, oder doch ?

    Danke, für deine Antort!

    Gruss, Jochen

    Montag, 10. Januar 2011 12:27
  • Hallo Jochen,

    ja das scheint alles nicht zu klappen. Ich konnte auch nichts finden.

    Was ich mir allerdings vorstellen könnte: Kann es sein, dass ein TextPointer eventuell ungültig wird, wenn man den Text ändert? Soweit ich es verstanden habe, änderst Du ja den Inhalt der RichTextBox im SelectionChanged-Event, oder? Kannst Du zum Test das mal Ausbauen und schauen, ob die Selection dann richtig zurückgesetzt wird?

    Gruß

    Jus

     

    Montag, 10. Januar 2011 20:00
  • Hi Jochen,

    was genau machst Du denn mit der Selection? Sprich, was verbirgt sich hinter dem Teil, den Du mit "..." darstellst. Das dürfte sicherlich der Schlüssel sein.


    Cheers,
    Olaf
    http://blogs.intuidev.com
    Dienstag, 11. Januar 2011 08:34
  • Hi Jürgen,
    Was ich mir allerdings vorstellen könnte: Kann es sein, dass ein TextPointer eventuell ungültig wird, wenn man den Text ändert?
    habe ich gerade mal ausprobiert - wenn Text mitten in der Selektion löscht, wird diese entspr. erweitert (es bleibt bei Start...Ende). Löscht man am Anfang oder Ende, wird nur der gelöschte Teil nicht mehr selektiert, der Rest bleibt erhalten.

    Cheers,
    Olaf
    http://blogs.intuidev.com
    Dienstag, 11. Januar 2011 08:36
  • Hi Olaf!

    >was genau machst Du denn mit der Selection? Sprich, was verbirgt sich hinter dem Teil, den Du mit "..." darstellst. Das dürfte sicherlich der Schlüssel sein

    In dem Beispielprogramm: Nichts! In dem emoprogramm muss hier nichts stehen um das Problem nachzuvollziehen. Allein schon das Speichern und Restoren der Selektion reicht aus. Über die Buttons gehts, in der Eventbehandlung nicht.
    Zum Nachvollziehen des Problems steht da einfach kein Code drin.

    Im regulären Programm wird der Text hier zeilen- und paragraphenweise untersucht und die Zeilen kategorisiert (Rezeptentwicklung)

    Ich denke, es ist das einfachste wenn ich das Demo-Programm einfach mal komplett zur Verfügung stelle, aber da ist wirklich nicht mehr drin weil schon alles ausser dem geposteten Parts weg gestrippt wurde:
    http://dl.dropbox.com/u/1384202/RTF_Test.zip

    Es ist das komplette Mini-Demo Projekt. Einfach mit VS2010 kompilieren und dann sollte das gleiche Problem sichtbar sein wie im schon geposteten Filmchen.

    Danke im Vorraus, Jochen

    Dienstag, 11. Januar 2011 14:09
  • Hi Jürgen,

    ich habe weiter unten das Projekt mal angehängt. ich mache nichts zwischen den Textpointern, damit kann es (leider) nichts zu tun haben.

    Gruss, Jochen

    Dienstag, 11. Januar 2011 14:36
  • Hi Jochen,

    OK, auf die Weise kann ich Dein Problem nachvollziehen und versteh's nun auch endlich. :-)

    Das grundsätzliche Problem bzw. Ursache des Ganzen dürfte sicherlich sein, dass Du die Selection innerhalb genau dieses Events änderst (und sei's nur durch Save=>Restore); eine genaue Ursache für das Verhalten selbst kann ich nicht nennen, aber evt. passiert seitens des Frameworks noch etwas, nachdem der Event-Code beendet wurde. Es stellt sich also weiterhin die Frage, warum Du die Selection überhaupt "anfassen" musst, wenn Du doch gar nichts ändern musst ..? IOW, ich würde versuchen, ohne Änderung der Selection innerhalb des Events auszukommen.

    Außerdem (oder alternativ) scheint mir das in allererster Linie ein Problem der Direction zu sein, denn wenn ich die Maus nicht, nachdem ich mehrmals nach unten gezogen habe, nach oben ziehe (sprich, rückwärts selektiere) gibt's gar kein Problem damit.
    IOW, wenn Du im Code die Selection umdrehst (aus rtbEdit.Selection.Select(SelStart2, SelEnd2); mach rtbEdit.Selection.Select(SelEnd2, SelStart2); ), "dreht" sich auch das Problem. Evt. lässt sich Dein Vorhaben ergo über .Select() allein ganz einfach nicht lösen. Vielleicht gibt's eine Möglichkeit, im Event die Direction abzufragen, mit der die Auswahl erfolgte, ich finde aber ad hoc keine passende Eigenschaft oder Methode dafür (bin auch nicht wirklich allzu versiert mit RTB-/FlowDocument). Das wäre aber die Richtung, in der ich weitersuchen würde.


    Cheers,
    Olaf
    http://blogs.intuidev.com
    Dienstag, 11. Januar 2011 18:04