มีวิธีใดในการเลือกบล็อกข้อความ WPF


224

ฉันต้องการสร้างข้อความที่แสดงในWittyซึ่งเป็นไคลเอนต์ Twitter แบบโอเพ่นซอร์สซึ่งสามารถเลือกได้ มันจะปรากฏขึ้นในขณะนี้โดยใช้บล็อกข้อความที่กำหนดเอง ฉันต้องใช้ TextBlock เพราะฉันทำงานกับ inline ของ textblock เพื่อแสดงและจัดรูปแบบ @ ชื่อผู้ใช้และลิงก์เป็นไฮเปอร์ลิงก์ คำขอที่พบบ่อยคือสามารถคัดลอกข้อความได้ เพื่อที่จะทำอย่างนั้นฉันต้องทำให้ TextBlock สามารถเลือกได้

ฉันพยายามทำให้มันใช้งานได้โดยการแสดงข้อความโดยใช้กล่องข้อความแบบอ่านอย่างเดียวมีสไตล์คล้ายกับบล็อคข้อความ แต่มันจะไม่ทำงานในกรณีของฉันเพราะกล่องข้อความไม่มีอินไลน์ กล่าวอีกนัยหนึ่งฉันไม่สามารถจัดรูปแบบหรือจัดรูปแบบข้อความภายในกล่องข้อความแต่ละรายการอย่างที่ฉันสามารถทำได้ด้วย TextBlock

ความคิดใด ๆ


1
ฉันจะลองใช้ตัวควบคุม RichTextBox เพื่อดูว่าจะใช้งานได้หรือไม่ แต่จากประสบการณ์การทำงานกับ richtextbox ก่อนหน้านี้มีส่วนร่วมมากขึ้น
Alan Le

คุณคิดเกี่ยวกับการใช้ FlowDocumentScrollViewer กับ FlowDocument ที่มี Paragraphs และ Runs หรือไม่? - มันใช้งานได้ดีสำหรับฉันเมื่อฉันต้องการข้อความที่เลือกได้และย่อหน้าและ Run แต่ละชุดสามารถแยกออกจากกันได้
BrainSlugs83

หลังจากลองใช้วิธีแก้ไขปัญหาบางอย่างด้านล่างแล้ว FlowDocumentScrollViewer เป็นหนทางไปข้างหน้า ดูเหมือนว่าจะครอบครองพื้นที่ตรงกลางที่มีประโยชน์ระหว่าง RichTextBox และ TextBlock
Tom Makin

ลงคะแนนเพื่อยอมรับคำตอบที่ไม่ตรงกับความต้องการของคุณ
Blechdose

คำตอบ:


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

6
ฉันมีโครงการที่มี TextBlocks / Labels จำนวนมากฉันไม่สามารถเปลี่ยนเป็น TextBox ได้จริงๆ สิ่งที่ฉันต้องการจะทำคือเพิ่มเวทมนตร์นำไปใช้กับทุกสไตล์ในแอพพลิเคชั่นระดับทรัพยากรดังนั้นมันควรจะส่งผลกระทบต่อ Label / TextBlock ทั้งหมดและทำให้พรีเซนเตอร์ข้อความภายในเป็นกล่องข้อความแบบอ่านอย่างเดียว ที่จะทำมัน?
Shimmy Weitzhandler

5
คุณอาจต้องการเพิ่ม IsTabStop = "False" ขึ้นอยู่กับสถานการณ์ของคุณ
Karsten

1
+1 ทางออกที่ดีมาก! ฉันเพิ่ม Padding = "0" เนื่องจากในโครงการของฉันข้อความด้านล่างถูกตัด ... บางทีอาจเป็นเพราะสไตล์ที่อื่น
reSPAWNed

123
-1 คำถามจะถามวิธีการเลือกบล็อกข้อความโดยเฉพาะ เพราะเขาไม่ต้องการสูญเสียคุณสมบัติ "Inlines" (กล่องข้อความใดไม่มี) 'คำตอบ' นี้เป็นเพียงการแนะนำให้ทำให้กล่องข้อความดูเหมือนกับบล็อกข้อความ
00jt

19
@AlanLe ทำไมคุณถึงยอมรับคำตอบนี้เมื่อมันเป็นสิ่งที่คุณพูดอย่างชัดเจนว่าคุณไม่ต้องการ? และทำไมคน 147 คนไร้เดียงสาจึงโหวตขึ้นมา?
Jim Balter

66

คำตอบทั้งหมดที่นี่เป็นเพียงการใช้TextBoxหรือพยายามที่จะใช้การเลือกข้อความด้วยตนเองซึ่งนำไปสู่ประสิทธิภาพที่ต่ำหรือพฤติกรรมที่ไม่ใช่เจ้าของภาษา (กระพริบ caret ในTextBoxไม่มีการสนับสนุนแป้นพิมพ์ในการใช้งานด้วยตนเอง ฯลฯ )

หลังจากขุดไปหลายชั่วโมงและอ่านซอร์สโค้ด WPFฉันได้ค้นพบวิธีการเปิดใช้งานการเลือกข้อความดั้งเดิมของ WPF สำหรับTextBlockการควบคุม (หรือการควบคุมอื่น ๆ จริงๆ) ฟังก์ชันการทำงานส่วนใหญ่เกี่ยวกับการเลือกข้อความถูกนำไปใช้ในSystem.Windows.Documents.TextEditorคลาสระบบ

ในการเปิดใช้งานการเลือกข้อความสำหรับการควบคุมของคุณคุณต้องทำสองสิ่ง:

  1. โทรTextEditor.RegisterCommandHandlers()หนึ่งครั้งเพื่อลงทะเบียนตัวจัดการเหตุการณ์ของคลาส

  2. สร้างอินสแตนซ์TextEditorสำหรับแต่ละอินสแตนซ์ของคลาสของคุณและส่งอินสแตนซ์พื้นฐานของคุณSystem.Windows.Documents.ITextContainerไปยังคลาสนั้น

นอกจากนี้ยังมีความต้องการว่าการควบคุมของคุณตั้งค่าคุณสมบัติFocusableTrue

นี่ไง! ฟังดูง่าย แต่น่าเสียดายที่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);
    }
}

ฉันยังสร้างสิ่งที่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เพื่อเปิดใช้งานการเลือกข้อความตามต้องการ ในกรณีนี้หากต้องการปิดการใช้งานการเลือกอีกครั้งเราต้องถอด a TextEditorโดยใช้การสะท้อนที่เทียบเท่ากับรหัสนี้:

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

1
คุณจะใช้คลาส SelectableTextBlock ภายใน xaml อื่นที่ควรมีได้อย่างไร
Yoav Feuerstein

1
เช่นเดียวกับที่คุณจะใช้การควบคุมที่กำหนดเองอื่น ๆ ดูstackoverflow.com/a/3768178/332528ตัวอย่าง
torvin

3
@BillyWillough โดยโซลูชันของคุณเพียงจำลองการเลือก มันขาดคุณสมบัติการเลือกเนทีฟจำนวนมาก: การสนับสนุนคีย์บอร์ด, เมนูบริบท ฯลฯ โซลูชันของฉันเปิดใช้งานคุณสมบัติการเลือกเนทีฟ
torvin

3
ดูเหมือนว่าโซลูชันนี้จะทำงานเมื่อTextBlockมีการฝังตัวHyperlinkตราบใดที่Hyperlinkไม่ใช่อินไลน์สุดท้ายในนั้น การเพิ่มส่วนท้ายที่ว่างเปล่าRunไปยังเนื้อหาจะแก้ไขสิ่งที่ปัญหาพื้นฐานคือทำให้เกิดExecutionEngineExceptionการโยนทิ้ง
Anton Tykhyy

2
มันเยี่ยมมาก! ยกเว้นถ้าคุณมีTextTrimming="CharacterEllipsis"บนTextBlockและความกว้างที่มีอยู่ไม่เพียงพอถ้าคุณย้ายตัวชี้เมาส์มากกว่า ... ก็เกิดปัญหากับ System.ArgumentException "ระยะทางขออยู่นอกเนื้อหาของเอกสารที่เกี่ยวข้อง." ที่ System.Windows.Documents.TextPointer.InitializeOffset (ตำแหน่ง TextPointer, ระยะทาง Int32, ทิศทางทิศทาง LogicalDirection) :( ไม่ทราบว่ามีวิธีแก้ปัญหาอื่นนอกจากปล่อย TextTrimming ตั้งเป็น None
Dave Huang

32

ฉันไม่พบตัวอย่างของการตอบคำถามจริงๆ คำตอบทั้งหมดใช้ Textbox หรือ RichTextbox ฉันต้องการโซลูชันที่อนุญาตให้ฉันใช้ TextBlock และนี่คือโซลูชันที่ฉันสร้างขึ้น

ฉันเชื่อว่าวิธีที่ถูกต้องในการทำเช่นนี้คือการขยายคลาส 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);
    }

1
นี่ควรเป็นคำตอบที่ยอมรับได้! ไม่มีการสะท้อนการแฮ็กไม่ใช้กล่องข้อความ ... และสามารถปรับโครงสร้างให้เป็นพฤติกรรมที่ใช้ซ้ำได้ง่าย ดีมากขอบคุณ!
Thomas Levesque

19

ใช้สไตล์นี้ช่องของคุณและที่มัน (แรงบันดาลใจจากบทความนี้ ):

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

1
BTW ณ วันนี้การเชื่อมโยงไปยังบทความดูเหมือนจะตาย
superjos

2
นอกจากนี้: การเติมเต็มควรเป็น -2,0, -2,0 ภายในกล่องข้อความตัวควบคุม TextBoxView ถูกสร้างขึ้นซึ่งมีระยะขอบเริ่มต้นเท่ากับ 2,0,2,0 น่าเสียดายที่คุณไม่สามารถกำหนดสไตล์ใหม่ได้เนื่องจากมีการทำเครื่องหมายภายใน
fdub

11
ดูเหมือนจะไม่มีใครอ่าน OP ต้องการ TextBlock ไม่ใช่ TextBox ที่มีสไตล์เหมือนกับ TextBlock
Jim Balter

18

สร้าง ControlTemplate สำหรับ TextBlock และวางกล่องข้อความไว้ข้างในพร้อมชุดคุณสมบัติแบบอ่านอย่างเดียว หรือเพียงแค่ใช้กล่องข้อความและทำให้อ่านได้อย่างเดียวจากนั้นคุณสามารถเปลี่ยนกล่องข้อความเพื่อให้ดูเหมือนกับ TextBlock


11
คุณตั้งค่า ControlTemplate สำหรับ TextBlock ได้อย่างไร ฉันหาอสังหาริมทรัพย์ไม่เจอ?
HaxElit

18
วิธีการนี้จะไม่ทำงานหาก TextBlock ของคุณมีองค์ประกอบแบบอินไลน์อยู่ภายใน ถ้าคุณมีไฮเปอร์ลิงก์หรือตัวหนาหรือตัวเอียง กล่องข้อความไม่รองรับสิ่งเหล่านี้
dthrasher

1
ไม่ทำงานหากคุณใช้การเรียกใช้แบบอินไลน์และเช่นเดียวกับที่ HaxElit ถามฉันไม่แน่ใจว่าคุณหมายถึงอะไรโดยเทมเพลตการควบคุม
Ritch Melton

7
-1 TextBlock ไม่มี ControlTemplate เนื่องจากเป็นคลาสย่อยโดยตรงของ FrameworkElement กล่องข้อความในทางกลับกันเป็นคลาสย่อยของการควบคุม
reSPAWNed

5
ทำไมไม่มีใครอ่าน OP กล่าวอย่างชัดเจนว่าจำเป็นต้องใช้ TextBlock ไม่ใช่ TextBox เนื่องจาก TextBlock รองรับการจัดรูปแบบอินไลน์และ TextBox ไม่ เหตุใดคำตอบขยะอย่างสมบูรณ์แบบนี้ทำให้มีผู้โหวตมากขึ้น?
Jim Balter

10

ฉันไม่แน่ใจว่าคุณสามารถเลือก TextBlock ได้หรือไม่ แต่ตัวเลือกอื่นจะใช้ RichTextBox ซึ่งเป็นเหมือนกับกล่องข้อความตามที่คุณแนะนำ แต่รองรับการจัดรูปแบบที่คุณต้องการ


1
ฉันพยายามทำเช่นนี้และในกระบวนการต้องทำให้ RichTextBox ผูกมัดกับคุณสมบัติการพึ่งพา น่าเสียดายที่เอกสารเก่าไม่ได้ถูกทิ้งอย่างเหมาะสมและหน่วยความจำรั่วอย่างบ้าคลั่ง อลันฉันสงสัยว่าคุณพบวิธีแก้ไขไหม?
John Noonan

@ AlanLe จากการตอบสนองทั้งหมดที่นี่เป็นเพียงหนึ่งในสองที่ตอบคำถามที่ถามจริง ... คนอื่น ๆ ทั้งหมดพูดคุยเกี่ยวกับการออกแบบกล่องข้อความให้ดูเหมือน TextBlock ในขณะที่ไม่จำเป็นต้องจัดรูปแบบ มันแปลกประหลาดและโชคร้ายที่ OP ยอมรับหนึ่งในคำตอบที่ไม่ใช่คำตอบแทนที่จะใช้คำตอบที่ถูกต้องในการใช้ RichTextBox แทนที่จะเป็น TextBox
Jim Balter

9

อ้างอิงจากWindows Dev Center :

คุณสมบัติ TextBlock.IsTextSelectionEnabled

[อัปเดตสำหรับแอป UWP บน Windows 10 สำหรับบทความ Windows 8.x ดูที่เก็บถาวร ]

รับหรือตั้งค่าที่ระบุว่าการเลือกข้อความถูกเปิดใช้งานในTextBlockไม่ว่าจะเป็นการกระทำของผู้ใช้หรือการเรียก API ที่เกี่ยวข้องกับการเลือก


5
น่าเสียดายที่ไม่สามารถใช้งานร่วมกับ Win7 ได้ (บางครั้งมันเป็นสิ่งที่ต้องมี)
Yury Schkatula

24
Amswer ไม่ถูกต้อง IsTextSelectionEnabled สำหรับ UWP เท่านั้นไม่ใช่ WPF - คำถามเดิมระบุ WPF
Puffin

6

ในขณะที่คำถามพูดว่า 'เลือกได้' ฉันเชื่อว่าผลลัพธ์โดยเจตนาคือการนำข้อความไปยังคลิปบอร์ด สิ่งนี้สามารถทำได้อย่างง่ายดายและสวยงามด้วยการเพิ่มเมนูบริบทและรายการเมนูที่เรียกว่าการคัดลอกที่ทำให้ค่าคุณสมบัติข้อความบล็อกข้อความในคลิปบอร์ด เพียงแค่ความคิดอยู่ดี


4

TextBlock ไม่มีเทมเพลต ดังนั้นเพื่อให้บรรลุสิ่งนี้เราจำเป็นต้องใช้กล่องข้อความที่มีการเปลี่ยนสไตล์เพื่อให้ทำงานเป็น 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>

วิธีนี้มีข้อดีอะไรบ้างเมื่อเปรียบเทียบกับคำตอบอื่น ๆ ฉันไม่เห็นเลย
ท่อง

ฉันลองลักษณะนี้: ไม่ได้กำหนด TextBoxBorder ถ้าคุณแสดงความคิดเห็นออกมาก็ทำงานได้ดี
sthiers

โค้ดตัวอย่างนี้ยอดเยี่ยมมันแสดงวิธีรับสีเริ่มต้นสำหรับ TextBlock
Contango

1
มันค่อนข้างสับสน ก่อน x: คีย์ "TextBlockUsingTextBoxStyle" อยู่ข้างหลัง ควรเป็น "TextBoxUsingTextBlockStyle" ประการที่สอง OP รู้วิธีการสไตล์กล่องข้อความเหมือน TextBlock แต่พูดซ้ำ ๆ ว่าเขาไม่สามารถใช้สิ่งนั้นเพราะเขาต้องการอินไลน์สำหรับการจัดรูปแบบ
Jim Balter

2

มีวิธีแก้ปัญหาทางเลือกที่อาจปรับให้เข้ากับ RichTextBox oulined ในโพสต์บล็อกนี้- มันใช้ทริกเกอร์เพื่อสลับออกแม่แบบการควบคุมเมื่อใช้วนเวียนอยู่เหนือการควบคุม - ควรช่วยด้วยประสิทธิภาพ


1
ลิงก์ของคุณเสียชีวิต โปรดรวมข้อมูลที่เกี่ยวข้องทั้งหมดไว้ในคำตอบและใช้ลิงก์เป็นการอ้างอิงเท่านั้น
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
         }
};


1
สิ่งนี้ไม่เป็นประโยชน์ อ่านคำถามเพื่อดูว่า OP ต้องการอะไรจริง ๆ
Jim Balter

1

การเพิ่มคำตอบของ @ torvin และตามที่ @Dave Huang พูดถึงในความคิดเห็นหากคุณTextTrimming="CharacterEllipsis"เปิดใช้งานแอปพลิเคชันขัดข้องเมื่อคุณวางตัวเหนือจุดไข่ปลา

ฉันลองตัวเลือกอื่น ๆ ที่กล่าวถึงในเธรดเกี่ยวกับการใช้กล่องข้อความ แต่จริงๆแล้วมันไม่ได้เป็นวิธีแก้ปัญหาอย่างใดอย่างหนึ่งเพราะมันไม่แสดง 'จุดไข่ปลา' และถ้าข้อความยาวเกินไปที่จะพอดีกับคอนเทนเนอร์ที่เลือกเนื้อหาของ กล่องข้อความ 'เลื่อน' ภายในซึ่งไม่ใช่ลักษณะการทำงานของ TextBlock

ฉันคิดว่าทางออกที่ดีที่สุดคือคำตอบของ @ 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;
            }
        }
    }
}

0

ฉันใช้งานSelectableTextBlockในไลบรารี opensource control ของฉันแล้ว คุณสามารถใช้สิ่งนี้:

<jc:SelectableTextBlock Text="Some text" />

4
นี่ใช้กล่องข้อความเหมือนกับคำตอบอื่น ๆ มากมายจากเมื่อหลายปีก่อน
Chris


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

1
คุณต้องอธิบายสิ่งที่คุณเปลี่ยนไปจากคำตอบด้านล่างโปรด -1
อเล็กซ์โฮปโอคอนเนอร์

บรรทัดที่ 51 ให้: System.ArgumentNullException: 'ค่าต้องไม่เป็นค่าว่าง ชื่อพารามิเตอร์: position1 '
ม้วน
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.