문제

텍스트를 표시하고 싶습니다 재치 있는, 오픈 소스 트위터 클라이언트, 선택 가능. 현재 사용자 정의 텍스트 블록을 사용하여 표시됩니다. 텍스트 블록의 인라인으로 작업하여 @username과 링크를 하이퍼 링크로 표시하고 형식화하기 때문에 TextBlock을 사용해야합니다. 자주 요청은 텍스트를 복사 할 수있는 것입니다. 그렇게하려면 TextBlock을 선택할 수 있도록해야합니다.

텍스트 블록처럼 보이도록 스타일로 읽은 읽기 전용 텍스트 상자를 사용하여 텍스트를 표시하여 작동하도록 노력했지만 텍스트 상자에 인라인이 없기 때문에 제 경우에는 작동하지 않습니다. 다시 말해, TextBlock과 같이 개별적으로 텍스트 상자 내에서 텍스트를 스타일링하거나 형식화 할 수 없습니다.

어떤 아이디어?

도움이 되었습니까?

해결책

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

다른 팁

여기의 모든 답변은 a를 사용하는 것입니다 TextBox 또는 수동으로 텍스트 선택을 구현하려고 시도하여 성능 저하 또는 비 원어민 행동으로 이어집니다 (깜박임 CARET TextBox, 수동 구현 등의 키보드 지원 없음)

몇 시간 동안 파고 읽은 후 WPF 소스 코드, 나는 대신 기본 WPF 텍스트 선택을 활성화하는 방법을 발견했습니다. TextBlock 제어 (또는 실제로 다른 컨트롤). 텍스트 선택에 관한 대부분의 기능은 System.Windows.Documents.TextEditor 시스템 클래스.

제어에 대한 텍스트 선택을 가능하게하려면 두 가지 작업을 수행해야합니다.

  1. 부르다 TextEditor.RegisterCommandHandlers() 클래스 이벤트 처리기를 등록하기 한 번

  2. 인스턴스를 만듭니다 TextEditor 수업의 각 사례에 대해 System.Windows.Documents.ITextContainer 그것에

귀하의 통제 요구 사항도 있습니다 Focusable 속성이 설정됩니다 True.

이거 야! 쉽지 않지만 불행히도 TextEditor 클래스는 내부로 표시됩니다. 그래서 나는 그 주위에 반사 래퍼를 써야했다.

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

나는 또한 a SelectableTextBlock 로부터 나오다 TextBlock 위에서 언급 한 단계를 수행합니다.

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

또 다른 옵션은 첨부 된 속성을 만드는 것입니다. TextBlock 주문시 텍스트 선택을 가능하게합니다. 이 경우 선택을 다시 비활성화하려면 분리해야합니다. TextEditor 이 코드와 동등한 반사를 사용하여 :

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

나는 실제로 질문에 대답하는 예를 찾을 수 없었습니다. 모든 답변은 TextBox 또는 RichTextBox를 사용했습니다. TextBlock을 사용할 수있는 솔루션이 필요했으며 이것이 제가 만든 솔루션입니다.

이를 수행하는 올바른 방법은 TextBlock 클래스를 확장하는 것입니다. 이것은 텍스트 블록 클래스를 확장하는 데 사용한 코드입니다. 텍스트를 선택하고 클립 보드에 복사 할 수 있습니다. "SDO"는 WPF에서 사용한 네임 스페이스 참조입니다.

확장 클래스 사용 WPF :

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>

확장 클래스에 대한 코드 :

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

예제 창 코드 :

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

이 스타일을 텍스트 상자에 적용하면 그게 IT입니다 (영감 이 기사):

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

TextBlock의 ControlTemplate을 작성하고 readonly 속성 세트로 텍스트 상자를 내부에 넣으십시오. 또는 TextBox를 사용하고 읽기 위해 만들면 TextBox.Style을 변경하여 TextBlock처럼 보이도록 할 수 있습니다.

텍스트 블록을 선택할 수 있는지 확실하지 않지만 또 다른 옵션은 RichTextBox를 사용하는 것입니다. 제안한대로 텍스트 상자와 같지만 원하는 형식을 지원합니다.

에 따르면 Windows Dev Center:

TextBlock.istexTectionEnabled 속성

Windows 10의 UWP 앱에 대한 업데이트 보관소 ]

텍스트 선택이 활성화되어 있는지 여부를 나타내는 값을 가져 오거나 설정합니다. 텍스트 블록, 사용자 조치 또는 호출 선택 관련 API를 통한.

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>

문제는 '선택 가능한'이라고 말하지만 의도적 인 결과는 텍스트를 클립 보드로 가져 오는 것이라고 생각합니다. 텍스트 블록 텍스트 속성 값을 클립 보드에 넣는 컨텍스트 메뉴와 메뉴 항목을 추가하여 쉽고 우아하게 달성 할 수 있습니다. 어쨌든 아이디어.

이것에 뾰족한 RichTextbox에 적응할 수있는 대체 솔루션이 있습니다. 블로그 게시물 - 트리거를 사용하여 컨트롤을 통해 사용하는 경우 컨트롤 템플릿을 교체했습니다. 성능에 도움이됩니다.


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

나는 구현했다 selectabletextBlock 내 OpenSource 컨트롤 라이브러리에서. 다음과 같이 사용할 수 있습니다.

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

@Torvin의 답변에 추가 및 @Dave Huang이 의견에 언급 한대로 TextTrimming="CharacterEllipsis" 타원체 위로 호버링하면 응용 프로그램 충돌을 활성화했습니다.

텍스트 상자를 사용하는 것에 대해 스레드에서 언급 된 다른 옵션을 시도했지만 '엘립스 시스 (Ellipsis)'를 표시하지 않고 텍스트가 너무 길어서 컨테이너에 맞지 않는 경우에도 솔루션이 아닌 것 같습니다. 텍스트 블록 동작이 아닌 내부적으로 텍스트 상자 '스크롤'.

최선의 해결책은 @Torvin의 답변이지만 엘립스 시스 위로 떠날 때 불쾌한 충돌이 발생합니다.

나는 그것이 예쁘지 않다는 것을 알고 있지만, 내부적으로 처리되지 않은 예외를 구독/구독하지 않고 예외를 처리하는 것이이 문제를 해결하는 유일한 방법은 누군가가 더 나은 솔루션을 가지고 있다면 공유하십시오 :)

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 !

나는 약간의 수정을 가져옵니다

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;
    }
}
라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top