Frage

Ich mag den Text in der Witty , ein Open-Source-Twitter-Client, angezeigt machen wählbar. Es wird derzeit einen benutzerdefinierten Textblock angezeigt werden. Ich brauche einen Textblock zu verwenden, weil ich mit dem Textblock des inlines gerade arbeite die @username und Links als Hyperlinks angezeigt wird und formatiert werden. Eine häufige Anforderung ist in der Lage sein, den Text kopieren und einfügen. Um das zu tun, dass ich den Textblock wählbar machen muß.

Ich habe versucht, es durch die Darstellung des Textes unter Verwendung eines Nur-Lese-TextBox zu arbeiten gestylt wie ein Textblock zu suchen, aber das wird in meinem Fall nicht funktionieren, weil ein TextBox inlines nicht hat. Mit anderen Worten, ich kann nicht Stil oder formatieren Sie den Text in einem Textfeld individuell wie ich kann mit einem Textblock.

Irgendwelche Ideen?

War es hilfreich?

Lösung

<TextBox Background="Transparent"
         BorderThickness="0"
         Text="{Binding Text, Mode=OneWay}"
         IsReadOnly="True"
         TextWrapping="Wrap" />

Andere Tipps

Alle Antworten hier sind nur ein TextBox mit oder Textauswahl versuchen, manuell zu implementieren, was zu einem schlechten Leistung führt oder nicht-native Verhalten (caret in TextBox, keine Tastatur-Unterstützung bei der manuellen Implementierungen usw. zu blinken.)

Nach Stunden der Graben um und Lesen der WPF Quellcode , entdeckte ich stattdessen eine Art und Weise die native WPF Textauswahl (oder wirklich alle anderen Kontrollen) für TextBlock Kontrollen zu ermöglichen. Die meisten Funktionen, um die Textauswahl wird in System.Windows.Documents.TextEditor Systemklasse implementiert.

Um die Textauswahl für Ihre Steuerung ermöglichen müssen Sie zwei Dinge tun:

  1. Anruf TextEditor.RegisterCommandHandlers() einmal registrieren Klasse Event-Handler

  2. eine Instanz von TextEditor für jede Instanz der Klasse erstellen und die darunter liegende Instanz Ihrer System.Windows.Documents.ITextContainer es passieren

Es gibt auch eine Anforderung, die Ihre Kontrolle der Focusable Eigenschaft auf True.

Das ist es! Klingt einfach, aber leider ist TextEditor Klasse als interne markiert. So hatte ich um eine Reflexion Wrapper schreiben:

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

Ich habe auch eine SelectableTextBlock von TextBlock abgeleitet, das die Schritte nimmt oben erwähnt:

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

Eine andere Möglichkeit wäre eine angefügte Eigenschaft für TextBlock erstellen Textauswahl auf Anfrage zu ermöglichen. In diesem Fall wird die Auswahl wieder zu deaktivieren, muss man eine TextEditor unter Verwendung der Reflexion äquivalent diesen Codes lösen:

_editor.TextContainer.TextView = null;
_editor.OnDetach();
_editor = null;

Ich habe nicht in der Lage gewesen, ein Beispiel wirklich zu finden, die Frage zu beantworten. Alle verwendeten Antworten eine Textbox oder RichTextbox. Ich brauchte eine Lösung, die mir einen Textblock zu nutzen, und das ist die Lösung, die ich erstellt.

Ich glaube, der richtige Weg, dies zu tun, ist die Textblock-Klasse zu erweitern. Dies ist der Code, den ich verwendet, um die Textblock-Klasse zu erweitern, mir zu erlauben, den Text auswählen und kopieren Sie sie in die Zwischenablage. „SDO“ ist der Namespace Referenz-I in der WPF verwendet.

WPF Mit erweiterten Klasse:

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>

Code-Behind für Extended-Klasse:

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

Beispiel Fenster Code:

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

Wenden Sie diese Art Ihres TextBox und das ist es (inspiriert von dieser Artikel ):

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

Erstellen Control für den Textblock und legt eine TextBox innen mit Nur-Lese-Eigenschaft festgelegt. Oder einfach nur TextBox verwenden und nur lesbar machen, dann können Sie die TextBox.Style ändern, um es wie TextBlock- zu machen aussieht.

Ich bin mir nicht sicher, ob Sie einen Textblock wählbar zu machen, aber eine andere Möglichkeit wäre, einen RichTextBox zu verwenden -. Es wie ein TextBox ist, wie Sie vorgeschlagen, unterstützen aber die Formatierung Sie wollen

Nach Windows-Dev Center :

  

TextBlock.IsTextSelectionEnabled Eigenschaft

     

[Aktualisiert für UWP-Anwendungen auf Windows 10. Für 8.x Artikel Windows finden   die Archiv ]

     

Ruft einen Wert ab, der angibt, ob die Textauswahl aktiviert ist   im TextBlock- entweder durch Benutzeraktionen oder rufen   Auswahl bezogenen API.

Textblock nicht über eine Vorlage. So Inorder dies zu erreichen, brauchen wir einen Text, dessen Stil zu verwenden, wird geändert als Textblock, zu verhalten.

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

Während die Frage sagen, bedeutet ‚Wählbar‘ Ich glaube, die absichtlich Ergebnisse sind den Text in die Zwischenablage zu bekommen. Dies kann leicht und elegant durch Hinzufügen eines Kontextmenü und den Menüpunkt Kopie aufgerufen erreicht werden, die den Textblock Text-Eigenschaftswert in der Zwischenablage legt. Nur eine Idee trotzdem.

Ich habe implementiert SelectableTextBlock in meiner Open-Source-Kontrollen Bibliothek . Sie können es wie folgt verwendet werden:

<jc:SelectableTextBlock Text="Some text" />
public MainPage()
{
    this.InitializeComponent();
    ...
    ...
    ...
    //Make Start result text copiable
    TextBlockStatusStart.IsTextSelectionEnabled = true;
}

Das Hinzufügen @ Torvin Antwort und wie @ Dave Huang in den Kommentaren erwähnt, wenn Sie TextTrimming="CharacterEllipsis" die Anwendung abstürzt aktiviert, wenn Sie über die Auslassungszeichen schweben.

Ich habe versucht, andere im Thread genannten Optionen über einen Text verwenden, aber es scheint wirklich nicht die Lösung zu sein, entweder weil es nicht die ‚Auslassungszeichen‘ zeigt, und auch wenn der Text zu lang ist, um die Behälter Auswahl passen der Inhalt der Textbox ‚blättert‘ intern, das keine TextBlock- Verhalten ist.

Ich denke, die beste Lösung @ Torvin Antwort ist, aber den bösen Absturz, wenn über die Auslassungszeichen schwebt.

Ich weiß, es ist nicht schön, aber die abonnieren / abbestellen intern nicht behandelte Ausnahmen und Umgang mit der Ausnahme war die einzige Art, wie ich dieses Problem zu lösen gefunden, bitte teilen, wenn jemand eine bessere Lösung hat:)

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;
            }
        }
    }
}
Really nice and easy solution, exactly what I wanted !

Ich bringe einige kleine Änderungen

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;
    }
}
Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top