Any way to make a WPF textblock selectable?

Viewed 133135

How to allow TextBlock's text to be selectable?

I tried to get it to work by displaying the text using a read-only TextBox styled to look like a textblock but this will not work in my case because a TextBox does not have inlines. In other words, how to make it selectable?

18 Answers

Create ControlTemplate for the TextBlock and put a TextBox inside with readonly property set. Or just use TextBox and make it readonly, then you can change the TextBox.Style to make it looks like TextBlock.

I'm not sure if you can make a TextBlock selectable, but another option would be to use a RichTextBox - it is like a TextBox as you suggested, but supports the formatting you want.

Here is what worked for me. I created a class TextBlockEx that is derived from TextBox and is set read-only, and text wrap in the constructor.

public class TextBlockEx : TextBox
{
    public TextBlockEx()
    {
        base.BorderThickness = new Thickness(0);
        IsReadOnly = true;
        TextWrapping = TextWrapping.Wrap;
        //Background = Brushes.Transparent; // Uncomment to get parent's background color
    }
}
public MainPage()
{
    this.InitializeComponent();
    ...
    ...
    ...
    //Make Start result text copiable
    TextBlockStatusStart.IsTextSelectionEnabled = true;
}

Adding to @torvin's answer and as @Dave Huang mentioned in the comments if you have TextTrimming="CharacterEllipsis" enabled the application crashes when you hover over the ellipsis.

I tried other options mentioned in the thread about using a TextBox but it really doesn't seem to be the solution either as it doesn't show the 'ellipsis' and also if the text is too long to fit the container selecting the content of the textbox 'scrolls' internally which isn't a TextBlock behaviour.

I think the best solution is @torvin's answer but has the nasty crash when hovering over the ellipsis.

I know it isn't pretty, but subscribing/unsubscribing internally to unhandled exceptions and handling the exception was the only way I found of solving this problem, please share if somebody has a better solution :)

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;
            }
        }
    }
}

Just use a FlowDocument inside a FlowDocumentScrollViewer, passing your inlines to the element. You can control the style of the element, in my case I added a small border.

<FlowDocumentScrollViewer Grid.Row="2" Margin="5,3" BorderThickness="1" 
                          BorderBrush="{DynamicResource Element.Border}" 
                          VerticalScrollBarVisibility="Auto">
    <FlowDocument>
        <Paragraph>
            <Bold>Some bold text in the paragraph.</Bold>
            Some text that is not bold.
        </Paragraph>

        <List>
            <ListItem>
                <Paragraph>ListItem 1</Paragraph>
            </ListItem>
            <ListItem>
                <Paragraph>ListItem 2</Paragraph>
            </ListItem>
            <ListItem>
                <Paragraph>ListItem 3</Paragraph>
            </ListItem>
        </List>
    </FlowDocument>
</FlowDocumentScrollViewer>

enter image description here

I agree most answers here do not create a selectable TextBlock. @Billy Willoughby's worked well however it didn't have a visible cue for selection. I'd like to extend his extension which can highlight text as it is selected. It also incorporates double and triple click selection. You can add a context menu with a "Copy" if needed. It uses the Background property to "highlight" the selection so it is limited in that it will overwrite Run.Background

https://github.com/mwagnerEE/WagnerControls

Related