2009-09-21 20 views
15

Mam ten RichTextBox WPF i chcę programowo wybrać dany zakres liter/słów i zaznacz go. Próbowałem tego, ale to nie działa, prawdopodobnie dlatego, że nie biorę pod uwagę niektórych ukrytych tagów FlowDocument lub podobnych. Na przykład, chcę zaznaczyć litery 3-8 ale 2-6 zostanie wybrany):Wybierz zakres tekstu w RichTextBox WPF (FlowDocument) Programowo

var start = MyRichTextBox.Document.ContentStart; 
var startPos = start.GetPositionAtOffset(3); 
var endPos = start.GetPositionAtOffset(8); 
var textRange = new TextRange(startPos,endPos); 
textRange.ApplyPropertyValue(TextElement.ForegroundProperty, 
    new SolidColorBrush(Colors.Blue)); 
textRange.ApplyPropertyValue(TextElement.FontWeightProperty, 
    FontWeights.Bold); 

Uświadomiłem sobie manipulacja RichTextBox jest nieco trudniejsze niż myślałem :)

Update: Dostałem kilka odpowiedzi na forach MSDN: This thread gdzie „dekurver” Seid:

przesunięcia jesteś określające nie są offsety charakter, ale przesunięcia symbolu. Co trzeba zrobić, to uzyskać wiadomość tekstową , która jest w sąsiedztwie , a następnie można dodać przesunięcia znaków .

i „LesterLobo” powiedział:

trzeba będzie pętli przez akapitów i inlines znaleźć Dalej, a następnie ich przesunięcia w pętli ubiegać się o wszystkich występach Spośród określony tekst. Zwróć uwagę, że tekst został zmieniony, ale Twój tekst nie byłby przesunięty, ponieważ jego znacznik nie byłby przenoszony jako powiązany z przesunięciem, a nie z tekstem . Można jednak utworzyć własny bieg i zapewniają Okładka to ...

nadal kochałbym zobaczyć przykładowy kod dla tego, jeśli ktoś zna swoją drogę FlowDocuments ...

EDIT mam wersję kodu Kratz VB działa, wygląda to tak:

private static TextPointer GetPoint(TextPointer start, int x) 
{ 
    var ret = start; 
    var i = 0; 
    while (i < x && ret != null) 
    { 
     if (ret.GetPointerContext(LogicalDirection.Backward) == 
TextPointerContext.Text || 
      ret.GetPointerContext(LogicalDirection.Backward) == 
TextPointerContext.None) 
      i++; 
     if (ret.GetPositionAtOffset(1, 
LogicalDirection.Forward) == null) 
      return ret; 
     ret = ret.GetPositionAtOffset(1, 
LogicalDirection.Forward); 
    } 
    return ret; 
} 

i używam go tak:

Colorize(item.Offset, item.Text.Length, Colors.Blue); 

private void Colorize(int offset, int length, Color color) 
{ 
    var textRange = MyRichTextBox.Selection; 
    var start = MyRichTextBox.Document.ContentStart; 
    var startPos = GetPoint(start, offset); 
    var endPos = GetPoint(start, offset + length); 

    textRange.Select(startPos, endPos); 
    textRange.ApplyPropertyValue(TextElement.ForegroundProperty, 
new SolidColorBrush(color)); 
    textRange.ApplyPropertyValue(TextElement.FontWeightProperty, 
FontWeights.Bold); 
} 

Odpowiedz

11
Public Function GoToPoint(ByVal start As TextPointer, ByVal x As Integer) As TextPointer 
    Dim out As TextPointer = start 
    Dim i As Integer = 0 
    Do While i < x 
     If out.GetPointerContext(LogicalDirection.Backward) = TextPointerContext.Text Or _ 
      out.GetPointerContext(LogicalDirection.Backward) = TextPointerContext.None Then 
      i += 1 
     End If 
     If out.GetPositionAtOffset(1, LogicalDirection.Forward) Is Nothing Then 
      Return out 
     Else 
      out = out.GetPositionAtOffset(1, LogicalDirection.Forward) 
     End If 


    Loop 
    Return out 
End Function 

Spróbuj tego, to powinien wrócić wskaźnik tekstowy dla danego char offsetowego. (Przepraszam, że w VB, ale to jest to, nad czym pracuję ...)

+0

Nice! Mam działającą wersję tego kodu, doda go do pytania. Twoje zdrowie. –

+0

Jest to również przydatne do zliczania znaków w RichTextBox: po prostu wykonaj pętlę, gdy 'out' nie jest pusta i zwróć' i' na końcu. – devios1

+0

Ta metoda pobiera mi każdą postać, która pojawia się po którymś z podanych przeze mnie tokenów, więc potrzebuję go, aby uzyskać wskaźnik do mojego słowa tylko dlatego, że kiedy próbuję zrobić to pogrubieniem, na przykład, robi to całe zdanie po moim tokenie "pogrubiony" i po prostu potrzebuję go do działania na moim tokenie! – a7madx7

7

Spróbuj tego:

var textRange = MyRichTextBox.Selection; 
var start = MyRichTextBox.Document.ContentStart; 
var startPos = start.GetPositionAtOffset(3); 
var endPos = start.GetPositionAtOffset(8); 
textRange.Select(startPos, endPos); 
textRange.ApplyPropertyValue(TextElement.ForegroundProperty, new SolidColorBrush(Colors.Blue)); 
textRange.ApplyPropertyValue(TextElement.FontWeightProperty, FontWeights.Bold); 
+0

nie wydaje się właściwie Obawiam działać. –

+0

Po prostu próbowałem, to działa dla mnie ... –

+0

@Tomas nie dla mnie Obawiam się. litery 2-6 są wybierane/kolorowe dla mnie przy użyciu tego kodu. Zamierzam spróbować czegoś innego i wrócić tutaj. –

1

Nawiasem mówiąc (i to może być akademickie dla wszystkich oprócz mnie), jeśli ustawisz FocusManager.IsFocusScope = "True" na kontenerze RichTextBox , siatka na przykład

<Grid FocusManager.IsFocusScope="True">...</Grid> 

to powinieneś być w stanie wykorzystać metodę Johan Danforth za Koloruj bez dwóch wywołań ApplyPropertyValue i RichTextBox powinien użyć domyślny wybór tła i pierwszego planu, aby podświetlić wybór.

private void Colorize(int offset, int length, Color color) 
{ 
    var textRange = MyRichTextBox.Selection; 
    var start = MyRichTextBox.Document.ContentStart; 
    var startPos = GetPoint(start, offset); 
    var endPos = GetPoint(start, offset + length); 

    textRange.Select(startPos, endPos); 
} 

nie próbowałem go z RichTextBox, ale działa całkiem dobrze, gdy templating TextBox znalezienia w FlowDocumentReader. Aby upewnić się, że można ustawić wartość pola RichTextBox w zasięgu jego zakresu ostrości, można również ustawić wartość .

Wadą tego jest oczywiście to, że jeśli użytkownik kliknie lub dokona selekcji w RichTextBox, wybór znika.

7

Próbowałem użyć rozwiązania opublikowanego przez KratzVB, ale odkryłem, że ignoruje nowe linie. Jeśli chcesz liczyć \ r \ n symboli następnie ten kod powinien działać:

private static TextPointer GetPoint(TextPointer start, int x) 
{ 

     var ret = start; 
     var i = 0; 
     while (ret != null) 
     { 
      string stringSoFar = new TextRange(ret, ret.GetPositionAtOffset(i, LogicalDirection.Forward)).Text; 
      if (stringSoFar.Length == x) 
        break; 
      i++; 
      if (ret.GetPositionAtOffset(i, LogicalDirection.Forward) == null) 
       return ret.GetPositionAtOffset(i-1, LogicalDirection.Forward) 

     } 
     ret=ret.GetPositionAtOffset(i, LogicalDirection.Forward); 
     return ret; 
} 
+0

to działa dla mnie - musisz dodać średnik po "return ret.GetPositionAtOffset (i-1, LogicalDirection.Forward)" - Próbowałem edytować, ale edycja jest krótsza niż 6 znaków (facepalm). –

1

Moja wersja bazuje na wersji cave_dweller za

private static TextPointer GetPositionAtCharOffset(TextPointer start, int numbertOfChars) 
{ 
    var offset = start; 
    int i = 0; 
    string stringSoFar=""; 
    while (stringSoFar.Length < numbertOfChars) 
    { 
     i++; 
     TextPointer offsetCandidate = start.GetPositionAtOffset(
       i, LogicalDirection.Forward); 

     if (offsetCandidate == null) 
      return offset; // ups.. we are to far 

     offset = offsetCandidate; 
     stringSoFar = new TextRange(start, offset).Text; 
    } 

    return offset; 
} 

Aby pominąć niektóre znaki dodać ten kod wewnątrz pętli:

stringSoFar = stringSoFar.Replace("\r\n", "") 
         .Replace(" ", "") 

Zamiast tego (powoli):

var startPos = GetPoint(start, offset); 
var endPos = GetPoint(start, offset + length); 

Należy to zrobić (szybciej)

var startPos = GetPoint(start, offset); 
var endPos = GetPoint(startPos, length); 

Albo utworzyć oddzielną metodę, aby uzyskać TextRange:

private static TextRange GetTextRange(TextPointer start, int startIndex, int length) 
{ 
    var rangeStart = GetPositionAtCharOffset(start, startIndex); 
    var rangeEnd = GetPositionAtCharOffset(rangeStart, length); 
    return new TextRange(rangeStart, rangeEnd); 
} 

Można teraz format tekstowy bez Select() ING:

var range = GetTextRange(Document.ContentStart, 3, 8); 
range.ApplyPropertyValue(
    TextElement.BackgroundProperty, 
    new SolidColorBrush(Colors.Aquamarine)); 
1

Nie znaleziono rozwiązanie z akceptowalnym rozwiązaniem wydajnościowym dla tego problemu przez długi czas. Następna próbka działa w moim przypadku z najwyższą wydajnością. Mam nadzieję, że to pomoże również komuś.

TextPointer startPos = rtb.Document.ContentStart.GetPositionAtOffset(searchWordIndex, LogicalDirection.Forward); 
startPos = startPos.CorrectPosition(searchWord, FindDialog.IsCaseSensitive); 
if (startPos != null) 
{ 
    TextPointer endPos = startPos.GetPositionAtOffset(textLength, LogicalDirection.Forward); 
    if (endPos != null) 
    { 
     rtb.Selection.Select(startPos, endPos); 
    } 
} 

public static TextPointer CorrectPosition(this TextPointer position, string word, bool caseSensitive) 
{ 
    TextPointer start = null; 
    while (position != null) 
    { 
     if (position.GetPointerContext(LogicalDirection.Forward) == TextPointerContext.Text) 
     { 
      string textRun = position.GetTextInRun(LogicalDirection.Forward); 

      int indexInRun = textRun.IndexOf(word, caseSensitive ? StringComparison.InvariantCulture : StringComparison.InvariantCultureIgnoreCase); 
      if (indexInRun >= 0) 
      { 
       start = position.GetPositionAtOffset(indexInRun); 
       break; 
      } 
     } 

     position = position.GetNextContextPosition(LogicalDirection.Forward); 
    } 

    return start; 
} 
0
private TextPointer GetPoint(TextPointer start, int pos) 
    { 
     var ret = start; 
     int i = 0; 
     while (i < pos) 
     { 
      if (ret.GetPointerContext(LogicalDirection.Forward) == 
    TextPointerContext.Text) 
       i++; 
      if (ret.GetPositionAtOffset(1, LogicalDirection.Forward) == null) 
       return ret; 
      ret = ret.GetPositionAtOffset(1, LogicalDirection.Forward); 
     } 
     return ret; 
    } 
+0

Witaj w SO i dziękuję za przesłanie odpowiedzi. Rozwiń odpowiedź, aby objaśnić swój kod. –

Powiązane problemy