Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/google-app-engine/4.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
有没有办法让WPF文本块可选择?_Wpf_Xaml_Textbox_Textblock - Fatal编程技术网

有没有办法让WPF文本块可选择?

有没有办法让WPF文本块可选择?,wpf,xaml,textbox,textblock,Wpf,Xaml,Textbox,Textblock,如何允许选择TextBlock的文本 我试图通过使用样式类似于textblock的只读文本框来显示文本来让它工作,但在我的情况下,这将不起作用,因为文本框没有内联线。换句话说,如何使其可选择?我不确定是否可以使TextBlock可选择,但另一种选择是使用RichTextBox-它与您建议的TextBox类似,但支持所需的格式。为TextBlock创建ControlTemplate,并在其中放置一个设置了readonly属性的TextBox。 或者只使用TextBox并将其设置为只读,然后您可以更

如何允许选择
TextBlock
的文本


我试图通过使用样式类似于textblock的只读文本框来显示文本来让它工作,但在我的情况下,这将不起作用,因为文本框没有内联线。换句话说,如何使其可选择?

我不确定是否可以使TextBlock可选择,但另一种选择是使用RichTextBox-它与您建议的TextBox类似,但支持所需的格式。

为TextBlock创建ControlTemplate,并在其中放置一个设置了readonly属性的TextBox。
或者只使用TextBox并将其设置为只读,然后您可以更改TextBox.Style使其看起来像TextBlock。

使用带有这些设置的
TextBox
,将其设置为只读并使其看起来像
TextBlock
控件

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


有一种替代解决方案可能适用于本文中所述的RichTextBox—当用户将鼠标悬停在控件上时,它使用触发器替换控件模板—应该有助于提高性能

TextBlock没有模板。所以为了实现这一点,我们需要使用一个文本框,它的样式被改变为一个文本块


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
         }
};
<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。我需要一个允许我使用文本块的解决方案,这就是我创建的解决方案

我认为正确的方法是扩展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>
窗口代码示例:

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示例(IInstanceHost主机、引用字符串WindowTitle、字符串应用程序ID、字符串参数)
{
初始化组件();
/*用于将选定文本添加到剪贴板*/
this.txtResults.TextSelected+=txtResults\u TextSelected;
}
void txtResults\u TextSelected(字符串SelectedText)
{
剪贴板.SetText(SelectedText);
}
根据:

TextBlock.IsTextSelectionEnabled属性

[针对Windows 10上的UWP应用更新。有关Windows 8.x文章,请参阅 [

获取或设置一个值,该值指示是否启用文本选择 在中,通过用户操作或调用 与选择相关的API

我带来了一些小修改

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

虽然这个问题说的是“可选的”,但我相信有意的结果是将文本放到剪贴板上。这可以通过添加上下文菜单和名为copy的菜单项轻松优雅地实现,该菜单项将Textblock文本属性值放入剪贴板。无论如何,这只是一个想法。

这里的所有答案都只是使用
文本框
或尝试手动执行文本选择,这会导致性能不佳或非本机行为(在
文本框
中闪烁插入符号,手动执行中不支持键盘等)

经过数小时的挖掘和阅读,我发现了一种为
TextBlock
控件(或任何其他控件)启用本机WPF文本选择的方法。关于文本选择的大部分功能是在
System.Windows.Documents.TextEditor
System类中实现的

要为控件启用文本选择,您需要做两件事:

  • 调用
    TextEditor.RegisterCommandHandlers()
    一次以注册类 事件处理程序

  • 为类的每个实例创建一个
    TextEditor
    实例,并将
    System.Windows.Documents.ITextContainer
    的底层实例传递给它

  • 还需要将控件的
    Focusable
    属性设置为
    True

    就是这样!听起来很简单,但不幸的是
    TextEditor
    类被标记为内部类。所以我不得不在它周围写一个反射包装:

    类TextEditorWrapper
    {
    私有静态只读类型TextEditorType=Type.GetType(“System.Windows.Documents.TextEditor,PresentationFramework,版本=4.0.0.0,区域性=中立,PublicKeyToken=31bf3856ad364e35”);
    私有静态只读属性info IsReadOnlyProp=TextEditorType.GetProperty(“IsReadOnly”,BindingFlags.Instance | BindingFlags.NonPublic);
    私有静态只读属性info TextViewProp=TextEditorType.GetProperty(“TextView”,BindingFlags.Instance | BindingFlags.NonPublic);
    私有静态只读MethodInfo RegisterMethod=TextEditorType.GetMethod(“RegisterCommandHandlers”,
    BindingFlags.Static | BindingFlags.NonPublic,null,new[]{typeof(Type),typeof(bool),typeof(bool),typeof(bool)},null);
    私有静态只读类型TextContainerType=Type.GetType(“System.Windows.Documents.ITextContainer,PresentationFramework,版本=4.0.0.0,区域性=中立,PublicKeyToken=31bf3856ad364e35”);
    私有静态只读属性INFO TextContainerTextViewProp=TextContainerType.GetProperty(“TextView”);
    私有静态只读属性INFO TextContainerProp=typeof(TextBlock).GetProperty(“TextContainer”,BindingFlags.Instance | BindingFlags.NonPublic);
    公共静态无效注册表CommandHandlers(类型controlType、bool acceptsRichContent、bool readOnly、bool registerE
    
    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;
        }
    }
    
    public MainPage()
    {
        this.InitializeComponent();
        ...
        ...
        ...
        //Make Start result text copiable
        TextBlockStatusStart.IsTextSelectionEnabled = true;
    }
    
    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;
                }
            }
        }
    }