2008-09-25 41 views
179

我想讓Witty(一個開源的Twitter客戶端)中顯示的文本可選。它當前使用自定義文本塊顯示。我需要使用TextBlock,因爲我正在使用文本塊的內聯來顯示和格式化@username和鏈接作爲超鏈接。頻繁的請求是能夠複製粘貼文本。爲了做到這一點,我需要使TextBlock可選。任何使WPF文本塊可選的方法?

我試圖通過使用只讀TextBox來顯示文本看起來像一個文本塊,但這不會在我的情況下工作,因爲TextBox沒有內聯。換句話說,我無法像使用TextBlock一樣單獨設置文本框中的文本的樣式或格式。

任何想法?

+1

我會嘗試使用RichTextBox控件,看看是否會工作。但從之前的經驗來看,使用richtextbox更爲重要。 – 2008-09-25 22:06:58

+0

您是否想過使用FlowDocumentScrollViewer,並在FlowDocument中包含段落和運行? - 當我需要可選文本時,這對我非常有用,並且每個段落和運行可以單獨設置樣式。 – BrainSlugs83 2015-01-25 04:58:06

+0

已經嘗試了下面的一些解決方法,FlowDocumentScrollViewer是前進的方向。它似乎佔據了RichTextBox和TextBlock之間的有用中間地帶。 – 2016-05-19 15:04:47

回答

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

我有一個包含許多TextBlocks/Labels的項目,我無法真正將它們變成TextBoxes。我想要做的是,爲應用程序級資源添加一個魔術應用於所有樣式,以便它應該影響所有Label/TextBlock,並將其內部文本演示者設置爲只讀文本框,您是否知道任何方式去做吧? – Shimmy 2011-01-18 08:34:33

8

我不確定您是否可以選擇TextBlock,但是另一個選擇是使用RichTextBox - 它就像您建議的那樣是一個TextBox,但支持您想要的格式。

+0

這是我打算接下來的路線。謝謝 – 2008-09-26 17:23:15

+1

我試過這樣做,並且在這個過程中必須使RichTextBox與依賴項屬性綁定。不幸的是,舊的流動文檔沒有被正確丟棄,並且內存正在瘋狂地泄漏。 艾倫,我想知道你是否找到了解決辦法? – 2009-04-24 05:59:53

+0

@AlanLe在這裏的所有答案中,這只是其中的一個,它實際上回答了所問的問題......所有其他人都在討論將TextBox設置爲TextBlock的樣式,而忽略格式化的需要。奇怪的是,不幸的是,OP接受了其中一個非答案,而不是使用RichTextBox而不是TextBox的正確答案。 – 2016-04-15 03:37:40

20

爲TextBlock創建ControlTemplate並將TextBox內置爲readonly屬性集。 或者只是使用TextBox並使其只讀,然後您可以更改TextBox.Style以使其看起來像TextBlock。

+11

如何爲TextBlock設置ControlTemplate?我找不到房產? – HaxElit 2010-01-14 15:39:52

+0

您也可以設置它應該看起來像TextBlock的邊框樣式。我認爲TextBox的性能比TextBlock低得多。 – Shimmy 2010-02-15 10:49:00

+16

如果您的TextBlock包含內嵌元素,則此方法無效。如果你有超鏈接或粗體或斜體文本? TextBox不支持這些。 – dthrasher 2011-03-02 23:11:53

2

有一個替代的解決方案,可能是適應這個blog post oultined RichTextBox的 - 它採用了觸發換出控制模板時使用懸停在控制 - 應與性能幫助

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

TextBlock沒有模板。所以爲了達到這個目的,我們需要使用一個TextBox,其樣式被改變爲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> 
17

應用這種風格到您的文本框,這就是它(從this article啓發):

<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

我已經在我的開源控件庫實現SelectableTextBlock。你可以這樣使用它:

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

我一直無法找到任何真正回答問題的例子。所有答案都使用了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); 
    } 
9

Windows Dev Center

TextBlock.IsTextSelectionEnabled財產

[更新爲Windows 10 UWP應用對於Windows 8.x的文章,請參閱 的archive]

獲取或設置一個值,該值通過用戶操作或呼叫 與選擇相關的API指示TextBlock中的文本選擇是否啓用 。

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

雖然問題不說「可選」我相信有意結果是讓文本複製到剪貼板。這可以通過添加一個上下文菜單和稱爲複製的菜單項將剪貼板中的文本塊文本屬性值置入,從而輕鬆而優雅地實現。無論如何,只是一個想法。

14

所有的答案,這裏只是用TextBox或試圖手動實現文本選擇,從而導致性能不佳或者非本地的行爲(在TextBox閃爍插入符號,沒有鍵盤支持手動實現等)

經過幾個小時的挖掘並閱讀WPF source code,我反而找到了一種爲TextBlock控件(或其他任何控件)啓用本機WPF文本選擇的方法。圍繞文本選擇的大多數功能都在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); 
    } 
} 

我還創建從TextBlock獲得的SelectableTextBlock這需要上面提到的步驟:

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;