Jest jakiś sposób na wybranie bloku tekstowego WPF?

224

Chcę, aby tekst wyświetlany w Witty , kliencie Twittera typu open source, był możliwy do wyboru. Obecnie jest wyświetlany przy użyciu niestandardowego bloku tekstu. Muszę użyć TextBlock, ponieważ pracuję z liniami tekstu bloku, aby wyświetlać i formatować @nazwa_użytkownika i linki jako hiperłącza. Częstym żądaniem jest możliwość skopiowania i wklejenia tekstu. W tym celu należy ustawić opcję TextBlock.

Próbowałem uruchomić go, wyświetlając tekst za pomocą TextBoxa tylko do odczytu, w stylu przypominającym blok tekstowy, ale to nie działa w moim przypadku, ponieważ TextBox nie ma wstawek. Innymi słowy, nie mogę stylizować ani formatować tekstu w TextBox indywidualnie, tak jak mogę to zrobić z TextBlock.

Jakieś pomysły?

Alan Le
źródło
1
Spróbuję użyć formantu RichTextBox, aby zobaczyć, czy to zadziała. Ale z wcześniejszych doświadczeń praca z richtextbox jest znacznie bardziej zaangażowana.
Alan Le
Czy zastanawiałeś się nad użyciem FlowDocumentScrollViewer z FlowDocument zawierającym akapity i przebiegi? - Działa to dla mnie całkiem dobrze, gdy potrzebuję tekstu do wyboru, a każdy akapit i bieg można stylizować osobno.
BrainSlugs83
Po wypróbowaniu niektórych z poniższych obejść FlowDocumentScrollViewer był rozwiązaniem. Wydaje się, że zajmuje przydatne miejsce między RichTextBox i TextBlock.
Tom Makin
głosuj za zaakceptowaniem odpowiedzi, która nie spełnia twoich wymagań.
Blechdose

Odpowiedzi:

218
<TextBox Background="Transparent"
         BorderThickness="0"
         Text="{Binding Text, Mode=OneWay}"
         IsReadOnly="True"
         TextWrapping="Wrap" />
MSB
źródło
6
Mam projekt, który zawiera wiele TextBlocks / Labels, nie mogę tak naprawdę zamienić ich w TextBoxes. Chcę dodać magiczny styl „zastosuj do wszystkich” do zasobu na poziomie aplikacji, aby miał on wpływ na wszystkie Label / TextBlock i sprawił, że ich wewnętrzny prezenter tekstowy byłby tylko TextBox, czy znasz jakiś sposób zrobić to?
Shimmy Weitzhandler
5
Możesz dodać IsTabStop = "False" w zależności od twojej sytuacji
Karsten
1
+1 Bardzo fajne rozwiązanie! Dodałem Padding = "0", ponieważ w moim projekcie spód tekstu został wycięty z ... Być może z powodu stylu gdzie indziej.
ODNOWIONY
123
-1 Pytanie dotyczy w szczególności sposobu wyboru bloku tekstowego. Ponieważ nie chce stracić właściwości „Inlines” (której nie mają pola tekstowe). Ta „odpowiedź” sugeruje, aby pole tekstowe wyglądało jak blok tekstowy.
00jt
19
@AlanLe Dlaczego zaakceptowałeś tę odpowiedź, skoro wyraźnie powiedziałeś, że nie chcesz? I dlaczego 147 nieświadomych ludzi głosowało za tym?
Jim Balter,
66

Wszystkie odpowiedzi tutaj używają po prostu TextBoxlub próbują zaimplementować zaznaczenie tekstu ręcznie, co prowadzi do niskiej wydajności lub nienatywnego zachowania (mruganie okiem TextBox, brak obsługi klawiatury w implementacjach ręcznych itp.)

Po wielu godzinach przekopywania się i czytaniu kodu źródłowego WPF , zamiast tego odkryłem sposób na włączenie natywnego zaznaczania tekstu WPF dla TextBlockformantów (lub naprawdę innych formantów). Większość funkcji związanych z zaznaczaniem tekstu jest zaimplementowana w System.Windows.Documents.TextEditorklasie systemowej.

Aby włączyć zaznaczanie tekstu dla swojej kontroli, musisz zrobić dwie rzeczy:

  1. Zadzwoń TextEditor.RegisterCommandHandlers()raz, aby zarejestrować procedury obsługi zdarzeń klasowych

  2. Tworzenie instancji TextEditordla każdej instancji klasy i przekazać podstawową otworzonym System.Windows.Documents.ITextContainerdo niego

Istnieje również wymóg, aby Focusablewłaściwość kontroli była ustawiona na True.

To jest to! Brzmi łatwo, ale niestety TextEditorklasa jest oznaczona jako wewnętrzna. Musiałem więc napisać wokół niego opakowanie refleksyjne:

class TextEditorWrapper
{
    private static readonly Type TextEditorType = Type.GetType("System.Windows.Documents.TextEditor, PresentationFramework, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35");
    private static readonly PropertyInfo IsReadOnlyProp = TextEditorType.GetProperty("IsReadOnly", BindingFlags.Instance | BindingFlags.NonPublic);
    private static readonly PropertyInfo TextViewProp = TextEditorType.GetProperty("TextView", BindingFlags.Instance | BindingFlags.NonPublic);
    private static readonly MethodInfo RegisterMethod = TextEditorType.GetMethod("RegisterCommandHandlers", 
        BindingFlags.Static | BindingFlags.NonPublic, null, new[] { typeof(Type), typeof(bool), typeof(bool), typeof(bool) }, null);

    private static readonly Type TextContainerType = Type.GetType("System.Windows.Documents.ITextContainer, PresentationFramework, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35");
    private static readonly PropertyInfo TextContainerTextViewProp = TextContainerType.GetProperty("TextView");

    private static readonly PropertyInfo TextContainerProp = typeof(TextBlock).GetProperty("TextContainer", BindingFlags.Instance | BindingFlags.NonPublic);

    public static void RegisterCommandHandlers(Type controlType, bool acceptsRichContent, bool readOnly, bool registerEventListeners)
    {
        RegisterMethod.Invoke(null, new object[] { controlType, acceptsRichContent, readOnly, registerEventListeners });
    }

    public static TextEditorWrapper CreateFor(TextBlock tb)
    {
        var textContainer = TextContainerProp.GetValue(tb);

        var editor = new TextEditorWrapper(textContainer, tb, false);
        IsReadOnlyProp.SetValue(editor._editor, true);
        TextViewProp.SetValue(editor._editor, TextContainerTextViewProp.GetValue(textContainer));

        return editor;
    }

    private readonly object _editor;

    public TextEditorWrapper(object textContainer, FrameworkElement uiScope, bool isUndoEnabled)
    {
        _editor = Activator.CreateInstance(TextEditorType, BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.CreateInstance, 
            null, new[] { textContainer, uiScope, isUndoEnabled }, null);
    }
}

Stworzyłem również SelectableTextBlockpochodną, TextBlockktóra wykonuje czynności wymienione powyżej:

public class SelectableTextBlock : TextBlock
{
    static SelectableTextBlock()
    {
        FocusableProperty.OverrideMetadata(typeof(SelectableTextBlock), new FrameworkPropertyMetadata(true));
        TextEditorWrapper.RegisterCommandHandlers(typeof(SelectableTextBlock), true, true, true);

        // remove the focus rectangle around the control
        FocusVisualStyleProperty.OverrideMetadata(typeof(SelectableTextBlock), new FrameworkPropertyMetadata((object)null));
    }

    private readonly TextEditorWrapper _editor;

    public SelectableTextBlock()
    {
        _editor = TextEditorWrapper.CreateFor(this);
    }
}

Inną opcją byłoby utworzenie dołączonej właściwości TextBlockumożliwiającej zaznaczanie tekstu na żądanie. W takim przypadku, aby ponownie wyłączyć zaznaczenie, należy odłączyć a TextEditorza pomocą równoważnika odbicia tego kodu:

_editor.TextContainer.TextView = null;
_editor.OnDetach();
_editor = null;
torvin
źródło
1
jak użyłbyś klasy SelectableTextBlock w innym xaml, który powinien ją zawierać?
Yoav Feuerstein
1
w ten sam sposób, w jaki używałbyś dowolnej innej kontroli niestandardowej. patrz na przykład stackoverflow.com/a/3768178/332528
torvin
3
@BillyWilloughby Twoje rozwiązanie emuluje tylko wybór. Brakuje wielu natywnych funkcji selekcji: obsługa klawiatury, menu kontekstowe itp. Moje rozwiązanie włącza natywną funkcję selekcji
torvin
3
Wydaje się, że to rozwiązanie ma pracę, gdy TextBlockzostał osadzony Hyperlinks, dopóki Hyperlinknie jest ostatnim inline w nim. Dodanie pustego Runkońca do treści rozwiązuje problem leżący u podstaw, który powoduje ExecutionEngineExceptionrzucenie.
Anton Tykhyy,
2
To jest świetne! Z wyjątkiem, jeśli masz TextTrimming="CharacterEllipsis"na TextBlocki dostępna szerokość jest wystarczająca, jeśli przesunąć wskaźnik myszy nad ..., to wywala z System.ArgumentException „Żądana odległość jest poza treścią towarzyszącym dokumencie.” w System.Windows.Documents.TextPointer.InitializeOffset (pozycja TextPointer, odległość Int32, kierunek LogicalDirection) :( Nie wiem, czy istnieje inne obejście niż pozostawienie TextTrimming ustawionej na None.
Dave Huang
32

Nie znalazłem żadnego przykładu odpowiedzi na pytanie. Wszystkie odpowiedzi wykorzystały pole tekstowe lub RichTextbox. Potrzebowałem rozwiązania, które pozwoliło mi korzystać z TextBlock, i właśnie to stworzyłem.

Uważam, że poprawnym sposobem jest rozszerzenie klasy TextBlock. To jest kod, którego użyłem do rozszerzenia klasy TextBlock, aby umożliwić mi zaznaczenie tekstu i skopiowanie go do schowka. „sdo” to odniesienie do przestrzeni nazw, której użyłem w WPF.

WPF przy użyciu klasy rozszerzonej:

xmlns:sdo="clr-namespace:iFaceCaseMain"

<sdo:TextBlockMoo x:Name="txtResults" Background="Black" Margin="5,5,5,5" 
      Foreground="GreenYellow" FontSize="14" FontFamily="Courier New"></TextBlockMoo>

Kod za klasą rozszerzoną:

public partial class TextBlockMoo : TextBlock 
{
    TextPointer StartSelectPosition;
    TextPointer EndSelectPosition;
    public String SelectedText = "";

    public delegate void TextSelectedHandler(string SelectedText);
    public event TextSelectedHandler TextSelected;

    protected override void OnMouseDown(MouseButtonEventArgs e)
    {
        base.OnMouseDown(e);
        Point mouseDownPoint = e.GetPosition(this);
        StartSelectPosition = this.GetPositionFromPoint(mouseDownPoint, true);            
    }

    protected override void OnMouseUp(MouseButtonEventArgs e)
    {
        base.OnMouseUp(e);
        Point mouseUpPoint = e.GetPosition(this);
        EndSelectPosition = this.GetPositionFromPoint(mouseUpPoint, true);

        TextRange otr = new TextRange(this.ContentStart, this.ContentEnd);
        otr.ApplyPropertyValue(TextElement.ForegroundProperty, new SolidColorBrush(Colors.GreenYellow));

        TextRange ntr = new TextRange(StartSelectPosition, EndSelectPosition);
        ntr.ApplyPropertyValue(TextElement.ForegroundProperty, new SolidColorBrush(Colors.White));

        SelectedText = ntr.Text;
        if (!(TextSelected == null))
        {
            TextSelected(SelectedText);
        }
    }
}

Przykładowy kod okna:

    public ucExample(IInstanceHost host, ref String WindowTitle, String ApplicationID, String Parameters)
    {
        InitializeComponent();
        /*Used to add selected text to clipboard*/
        this.txtResults.TextSelected += txtResults_TextSelected;
    }

    void txtResults_TextSelected(string SelectedText)
    {
        Clipboard.SetText(SelectedText);
    }
Billy Willoughby
źródło
1
To powinna być zaakceptowana odpowiedź! Bez hacków refleksyjnych, bez użycia TextBox ... I można go łatwo przekształcić w zachowanie wielokrotnego użytku. Bardzo fajnie, dziękuję!
Thomas Levesque
19

Zastosuj ten styl do TextBox i to wszystko (inspirowane tym artykułem ):

<Style x:Key="SelectableTextBlockLikeStyle" TargetType="TextBox" BasedOn="{StaticResource {x:Type TextBox}}">
    <Setter Property="IsReadOnly" Value="True"/>
    <Setter Property="IsTabStop" Value="False"/>
    <Setter Property="BorderThickness" Value="0"/>
    <Setter Property="Background" Value="Transparent"/>
    <Setter Property="Padding" Value="-2,0,0,0"/>
    <!-- The Padding -2,0,0,0 is required because the TextBox
        seems to have an inherent "Padding" of about 2 pixels.
        Without the Padding property,
        the text seems to be 2 pixels to the left
        compared to a TextBlock
    -->
    <Style.Triggers>
        <MultiTrigger>
            <MultiTrigger.Conditions>
                <Condition Property="IsMouseOver" Value="False" />
                <Condition Property="IsFocused" Value="False" />
            </MultiTrigger.Conditions>
            <Setter Property="Template">
                <Setter.Value>
                <ControlTemplate TargetType="{x:Type TextBox}">
                    <TextBlock Text="{TemplateBinding Text}" 
                             FontSize="{TemplateBinding FontSize}"
                             FontStyle="{TemplateBinding FontStyle}"
                             FontFamily="{TemplateBinding FontFamily}"
                             FontWeight="{TemplateBinding FontWeight}"
                             TextWrapping="{TemplateBinding TextWrapping}"
                             Foreground="{DynamicResource NormalText}"
                             Padding="0,0,0,0"
                                       />
                </ControlTemplate>
                </Setter.Value>
            </Setter>
        </MultiTrigger>
    </Style.Triggers>
</Style>
sakito
źródło
1
BTW na dzień dzisiejszy link do artykułu wydaje się martwy
superjos,
2
Kolejny dodatek: wypełnienie powinno wynosić -2,0, -2,0. Wewnątrz TextBox tworzona jest kontrolka TextBoxView, która ma domyślny margines 2,0,2,0. Niestety nie można ponownie zdefiniować stylu, ponieważ jest on oznaczony jako wewnętrzny.
fdub,
11
Wydaje się, że nikt nie umie czytać. OP potrzebuje TextBlocka, a nie TextBoxa w stylu TextBlocka.
Jim Balter
18

Utwórz ControlTemplate dla TextBlock i umieść TextBox wewnątrz z zestawem właściwości tylko do odczytu. Lub po prostu użyj TextBox i uczyń go tylko do odczytu, a następnie możesz zmienić TextBox.Style, aby wyglądał jak TextBlock.

Jobi Joy
źródło
11
Jak ustawić ControlTemplate dla TextBlock? Nie mogę znaleźć nieruchomości?
HaxElit
18
To podejście nie zadziała, jeśli TextBlock zawiera elementy wbudowane. Co się stanie, jeśli masz hiperłącza lub pogrubioną czcionkę lub kursywę? TextBox ich nie obsługuje.
dthrasher
1
Nie działa, jeśli używasz uruchomień wbudowanych i jak zapytał HaxElit, nie jestem pewien, co masz na myśli przez szablon kontrolny.
Ritch Melton
7
-1 TextBlock nie ma ControlTemplate, ponieważ jest to bezpośrednia podklasa FrameworkElement. TextBox z drugiej strony jest podklasą kontroli.
ODNOWIONY
5
Dlaczego nikt nie może czytać? OP wyraźnie stwierdził, że TextBlock jest potrzebny, a nie TextBox, ponieważ TextBlock obsługuje formatowanie wbudowane, a TextBox nie. Dlaczego całkowicie błędne odpowiedzi na śmiecie, takie jak ta, otrzymują liczne pozytywne opinie?
Jim Balter,
10

Nie jestem pewien, czy możesz ustawić TextBlock do wyboru, ale inną opcją byłoby użycie RichTextBox - jest jak TextBox, jak sugerowałeś, ale obsługuje odpowiednie formatowanie.

Bruce
źródło
1
Próbowałem to zrobić, a proces musiałem uczynić RichTextBox możliwym do wiązania z właściwością zależności. Niestety stare dokumenty przepływu nie są usuwane poprawnie, a pamięć przecieka jak szalona. Alan, zastanawiam się, czy udało ci się to obejść?
John Noonan
@AlanLe Ze wszystkich odpowiedzi tutaj, to tylko jedna z dwóch, które faktycznie odpowiadają na zadane pytanie ... wszyscy inni mówią o stylowaniu TextBoxa, aby wyglądał jak TextBlock, ignorując potrzebę formatowania. Dziwne i niefortunne jest to, że OP zaakceptował jedną z tych nie-odpowiedzi, zamiast poprawnej odpowiedzi na użycie RichTextBox zamiast TextBox.
Jim Balter
9

Według Windows Dev Center :

TextBlock.IsTextSelectionEnabled właściwość

[Zaktualizowano dla aplikacji UWP na Windows 10. Artykuły na temat Windows 8.x, zobacz archiwum ]

Pobiera lub ustawia wartość wskazującą, czy zaznaczanie tekstu jest włączone w TextBlock , poprzez akcję użytkownika lub wywołanie interfejsu API związanego z wyborem.

Jack Pines
źródło
5
Niestety, nie jest kompatybilny z Win7 (czasami jest to obowiązkowy wymóg)
Yury Schkatula
24
Amswer wydaje się niepoprawny. IsTextSelectionEnabled jest tylko dla UWP, a nie WPF - oryginalne pytanie określa WPF.
Maskonur
6

Chociaż pytanie brzmi „do wyboru”, uważam, że zamierzonym rezultatem jest przeniesienie tekstu do schowka. Można to łatwo i elegancko osiągnąć poprzez dodanie menu kontekstowego i pozycji menu o nazwie kopiowanie, która umieszcza wartość właściwości Textblock Text w schowku. W każdym razie tylko pomysł.

SimperT
źródło
4

TextBlock nie ma szablonu. Aby to osiągnąć, musimy użyć TextBox, którego styl został zmieniony, aby zachowywał się jak TextBlock.

<Style x:Key="TextBlockUsingTextBoxStyle" BasedOn="{x:Null}" TargetType="{x:Type TextBox}">
    <Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.ControlTextBrushKey}}"/>
    <Setter Property="Background" Value="Transparent"/>
    <Setter Property="BorderBrush" Value="{StaticResource TextBoxBorder}"/>
    <Setter Property="BorderThickness" Value="0"/>
    <Setter Property="Padding" Value="1"/>
    <Setter Property="AllowDrop" Value="true"/>
    <Setter Property="FocusVisualStyle" Value="{x:Null}"/>
    <Setter Property="ScrollViewer.PanningMode" Value="VerticalFirst"/>
    <Setter Property="Stylus.IsFlicksEnabled" Value="False"/>
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="{x:Type TextBox}">
                <TextBox BorderThickness="{TemplateBinding BorderThickness}" IsReadOnly="True" Text="{TemplateBinding Text}" Background="{x:Null}" BorderBrush="{x:Null}" />
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>
Saraf Talukder
źródło
Jakie zalety oferuje to podejście w porównaniu z innymi odpowiedziami? Nie widzę żadnych.
surfen
Próbowałem tego stylu: TextBoxBorder nie jest zdefiniowany. Jeśli to skomentujesz,
zadziała
Ten przykładowy kod jest doskonały, pokazuje, jak uzyskać domyślny kolor dla TextBlock.
Contango,
1
To jest dość zagmatwane. Po pierwsze, klucz x: „TextBlockUsingTextBoxStyle” jest odwrócony; powinna to być „TextBoxUsingTextBlockStyle”. Po drugie, OP wiedział już, jak stylizować TextBox jak TextBlock, ale wielokrotnie powtarzał, że nie może tego użyć, ponieważ potrzebuje formatowania do wstawiania.
Jim Balter
2

Istnieje alternatywne rozwiązanie, które można dostosować do RichTextBox, zamieszczonego w tym poście na blogu - użyło wyzwalacza, aby zamienić szablon kontrolny, gdy najechanie kursorem na kontrolę - powinno pomóc w wydajności

Richard
źródło
1
Twój link jest martwy. Podaj wszystkie istotne informacje w odpowiedzi i użyj linków tylko jako cytatów.
Jim Balter,
1

new TextBox
{
   Text = text,
   TextAlignment = TextAlignment.Center,
   TextWrapping = TextWrapping.Wrap,
   IsReadOnly = true,
   Background = Brushes.Transparent,
   BorderThickness = new Thickness()
         {
             Top = 0,
             Bottom = 0,
             Left = 0,
             Right = 0
         }
};

Lu55
źródło
1
To nie jest pomocne. Przeczytaj pytanie, aby zobaczyć, czego tak naprawdę chciał PO.
Jim Balter
1

Dodanie do odpowiedzi @ torvin i jak wspomniano w komentarzach @Dave Huang, jeśli TextTrimming="CharacterEllipsis"włączyłeś aplikację, ulega awarii po najechaniu kursorem na elipsę.

Wypróbowałem inne opcje wymienione w wątku dotyczące korzystania z TextBox, ale tak naprawdę nie wydaje się to rozwiązaniem, ponieważ nie pokazuje „elipsy”, a także jeśli tekst jest zbyt długi, aby zmieścił się w pojemniku, wybierając zawartość pole tekstowe „przewija się” wewnętrznie, co nie jest zachowaniem TextBlock.

Myślę, że najlepszym rozwiązaniem jest odpowiedź @ torvina, ale ma paskudną awarię podczas najechania na elipsę.

Wiem, że to nie jest ładne, ale subskrybowanie / wypisywanie się wewnętrznie z nieobsługiwanymi wyjątkami i obsługa wyjątku była jedynym sposobem, w jaki znalazłem rozwiązanie tego problemu, proszę podziel się, jeśli ktoś ma lepsze rozwiązanie :)

public class SelectableTextBlock : TextBlock
{
    static SelectableTextBlock()
    {
        FocusableProperty.OverrideMetadata(typeof(SelectableTextBlock), new FrameworkPropertyMetadata(true));
        TextEditorWrapper.RegisterCommandHandlers(typeof(SelectableTextBlock), true, true, true);

        // remove the focus rectangle around the control
        FocusVisualStyleProperty.OverrideMetadata(typeof(SelectableTextBlock), new FrameworkPropertyMetadata((object)null));
    }

    private readonly TextEditorWrapper _editor;

    public SelectableTextBlock()
    {
        _editor = TextEditorWrapper.CreateFor(this);

        this.Loaded += (sender, args) => {
            this.Dispatcher.UnhandledException -= Dispatcher_UnhandledException;
            this.Dispatcher.UnhandledException += Dispatcher_UnhandledException;
        };
        this.Unloaded += (sender, args) => {
            this.Dispatcher.UnhandledException -= Dispatcher_UnhandledException;
        };
    }

    private void Dispatcher_UnhandledException(object sender, DispatcherUnhandledExceptionEventArgs e)
    {
        if (!string.IsNullOrEmpty(e?.Exception?.StackTrace))
        {
            if (e.Exception.StackTrace.Contains("System.Windows.Controls.TextBlock.GetTextPositionFromDistance"))
            {
                e.Handled = true;
            }
        }
    }
}
rauland
źródło
0

Zaimplementowałem SelectableTextBlock w mojej bibliotece kontrolek opensource. Możesz użyć tego w następujący sposób:

<jc:SelectableTextBlock Text="Some text" />
Robert Važan
źródło
4
To po prostu wykorzystuje TextBox, jak wiele innych odpowiedzi sprzed wielu lat.
Chris,
0
public MainPage()
{
    this.InitializeComponent();
    ...
    ...
    ...
    //Make Start result text copiable
    TextBlockStatusStart.IsTextSelectionEnabled = true;
}
Anioł T.
źródło
-1
Really nice and easy solution, exactly what I wanted !

Wprowadzam małe modyfikacje

public class TextBlockMoo : TextBlock 
{
    public String SelectedText = "";

    public delegate void TextSelectedHandler(string SelectedText);
    public event TextSelectedHandler OnTextSelected;
    protected void RaiseEvent()
    {
        if (OnTextSelected != null){OnTextSelected(SelectedText);}
    }

    TextPointer StartSelectPosition;
    TextPointer EndSelectPosition;
    Brush _saveForeGroundBrush;
    Brush _saveBackGroundBrush;

    TextRange _ntr = null;

    protected override void OnMouseDown(MouseButtonEventArgs e)
    {
        base.OnMouseDown(e);

        if (_ntr!=null) {
            _ntr.ApplyPropertyValue(TextElement.ForegroundProperty, _saveForeGroundBrush);
            _ntr.ApplyPropertyValue(TextElement.BackgroundProperty, _saveBackGroundBrush);
        }

        Point mouseDownPoint = e.GetPosition(this);
        StartSelectPosition = this.GetPositionFromPoint(mouseDownPoint, true);            
    }

    protected override void OnMouseUp(MouseButtonEventArgs e)
    {
        base.OnMouseUp(e);
        Point mouseUpPoint = e.GetPosition(this);
        EndSelectPosition = this.GetPositionFromPoint(mouseUpPoint, true);

        _ntr = new TextRange(StartSelectPosition, EndSelectPosition);

        // keep saved
        _saveForeGroundBrush = (Brush)_ntr.GetPropertyValue(TextElement.ForegroundProperty);
        _saveBackGroundBrush = (Brush)_ntr.GetPropertyValue(TextElement.BackgroundProperty);
        // change style
        _ntr.ApplyPropertyValue(TextElement.BackgroundProperty, new SolidColorBrush(Colors.Yellow));
        _ntr.ApplyPropertyValue(TextElement.ForegroundProperty, new SolidColorBrush(Colors.DarkBlue));

        SelectedText = _ntr.Text;
    }
}
Titwan
źródło
1
Musisz wyjaśnić, co zmieniłeś od odpowiedzi poniżej. -1
Alex Hope O'Connor
Wiersz 51 daje: System.ArgumentNullException: „Wartość nie może być pusta. Nazwa parametru: miejsce1'
rzuca