WPF RichTextBox 中的只读 Run 元素?

发布于 2024-07-26 02:27:05 字数 790 浏览 2 评论 0原文

我可能完全是在想象这一点,但我可以发誓有一种方法可以使 RichTextBox 中的各个 Run(或 Parapgraph)元素成为只读。 我还可以发誓,几周前我自己尝试了一种方法,并对结果感到满意 - 我隐约记得它看起来像这样:

<RichTextBox x:Name="richTextBox"
             AcceptsTab="True"
             AcceptsReturn="True"
             FontFamily="Courier New"
             FontSize="14">
    <FlowDocument>
        <Paragraph>
            <Run IsReadOnly="True">I wish this was read-only!</Run>
        </Paragraph>
    </FlowDocument>
</RichTextBox>

现在,几周后,我尝试让 Run 元素读取-只有在RichTextBox中才发现它似乎不可能。

MSDN 论坛上的这篇文章< /a> 似乎证实了这一点。

这是我完全想象出来的吗? 或者有办法做我想做的事吗?

I may be completely imagining this, but I could have sworn there was a way to make individual Run (or Parapgraph) elements in a RichTextBox read-only. I also could have sworn I tried a method for doing this out myself a few weeks ago and was satisfied with the results - I vaguely remember it looked something like this:

<RichTextBox x:Name="richTextBox"
             AcceptsTab="True"
             AcceptsReturn="True"
             FontFamily="Courier New"
             FontSize="14">
    <FlowDocument>
        <Paragraph>
            <Run IsReadOnly="True">I wish this was read-only!</Run>
        </Paragraph>
    </FlowDocument>
</RichTextBox>

Now, a few weeks later, I go to try to make Run elements read-only in a RichTextBox only to find it doesn't seem to be possible.

This post on the MSDN forums seems to confirm that.

Did I completely imagine this? Or is there a way to do what I want to do?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

骄兵必败 2024-08-02 02:27:05

好吧,我已经想出了一个适合我的情况的解决方案 - 但可能不适用于每个想要这样的东西的人。 虽然很乱,但它能完成工作。

几天内我不会接受我自己的答案,以防万一其他人有更好的方法来完成这个任务。

首先是 XAML:

<Window x:Class="WpfApplication1.Window1"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="Window1"
        Height="500"
        Width="600">
    <DockPanel LastChildFill="True">
        <RichTextBox x:Name="rtb"
                     FontFamily="Courier New"
                     FontSize="14"
                     PreviewKeyDown="rtb_PreviewKeyDown">
            <FlowDocument>
                <Paragraph>
                    <InlineUIContainer Unloaded="InlineUIContainer_Unloaded">
                        <TextBlock FontFamily="Courier New" FontSize="14">This line of text is not editable.</TextBlock>
                    </InlineUIContainer>
                    <Run Foreground="Blue">But this is editable.</Run>
                </Paragraph>
            </FlowDocument>
        </RichTextBox>
    </DockPanel>
</Window>

以及背后的代码:

using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;

namespace WpfApplication1
{
    public partial class Window1 : Window
    {
        public Window1()
        {
            InitializeComponent();
        }

        private void InlineUIContainer_Unloaded(object sender, RoutedEventArgs e)
        {
            (sender as InlineUIContainer).Unloaded -= new RoutedEventHandler(InlineUIContainer_Unloaded);

            TextBlock tb = new TextBlock();
            tb.FontFamily = new FontFamily("Courier New");
            tb.FontSize = 14;
            tb.Text = "This line of text is not editable.";

            TextPointer tp = rtb.CaretPosition.GetInsertionPosition(LogicalDirection.Forward);
            InlineUIContainer iuic = new InlineUIContainer(tb, tp);
            iuic.Unloaded += new RoutedEventHandler(InlineUIContainer_Unloaded);
        }

        private void rtb_PreviewKeyDown(object sender, KeyEventArgs e)
        {
            if (e.Key == Key.Enter)
            {
                var newPointer = rtb.Selection.Start.InsertLineBreak();
                rtb.Selection.Select(newPointer, newPointer);

                e.Handled = true;
            }
        }
    }
}

我的解决方案依赖于这样一个事实:当从 UI 中删除 InlineUIContainer 时,它的 Unloaded() 方法是叫。 此时,我只需将已删除的 InlineUIContainer 重新插入到当前插入符号位置即可。

与任何黑客一样,也有很多缺点。 我发现的缺点如下:

  • 我想要只读的文本需要包装在 InlineUIContainer 中。 这对该解决方案有一点限制。
  • 我必须捕获“Enter”键并手动插入换行符,否则,每次按下 Enter 键时 InlineUIContainer.Unloaded() 都会不断触发。 不好玩,但它适用于我的情况。

这不是一个很好的解决方案,但我认为它对我有用。 就像我说的,我不会将此标记为我自己问题的答案 - 希望其他人有更好的方法来做到这一点。

Alright, I've come up with a solution that works for my case - but may not work for everyone who wants something like this. It's messy, but it does the job.

I'm not going to accept my own answer for a few days, just in case someone else has a better way of accomplishing this.

Here we go, first, the XAML:

<Window x:Class="WpfApplication1.Window1"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="Window1"
        Height="500"
        Width="600">
    <DockPanel LastChildFill="True">
        <RichTextBox x:Name="rtb"
                     FontFamily="Courier New"
                     FontSize="14"
                     PreviewKeyDown="rtb_PreviewKeyDown">
            <FlowDocument>
                <Paragraph>
                    <InlineUIContainer Unloaded="InlineUIContainer_Unloaded">
                        <TextBlock FontFamily="Courier New" FontSize="14">This line of text is not editable.</TextBlock>
                    </InlineUIContainer>
                    <Run Foreground="Blue">But this is editable.</Run>
                </Paragraph>
            </FlowDocument>
        </RichTextBox>
    </DockPanel>
</Window>

And the code behind:

using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;

namespace WpfApplication1
{
    public partial class Window1 : Window
    {
        public Window1()
        {
            InitializeComponent();
        }

        private void InlineUIContainer_Unloaded(object sender, RoutedEventArgs e)
        {
            (sender as InlineUIContainer).Unloaded -= new RoutedEventHandler(InlineUIContainer_Unloaded);

            TextBlock tb = new TextBlock();
            tb.FontFamily = new FontFamily("Courier New");
            tb.FontSize = 14;
            tb.Text = "This line of text is not editable.";

            TextPointer tp = rtb.CaretPosition.GetInsertionPosition(LogicalDirection.Forward);
            InlineUIContainer iuic = new InlineUIContainer(tb, tp);
            iuic.Unloaded += new RoutedEventHandler(InlineUIContainer_Unloaded);
        }

        private void rtb_PreviewKeyDown(object sender, KeyEventArgs e)
        {
            if (e.Key == Key.Enter)
            {
                var newPointer = rtb.Selection.Start.InsertLineBreak();
                rtb.Selection.Select(newPointer, newPointer);

                e.Handled = true;
            }
        }
    }
}

My solution relies on the fact that when an InlineUIContainer is removed from the UI, it's Unloaded() method is called. At that point, I simply reinsert the deleted InlineUIContainer at the current caret position.

As with any hack, there are a bunch of disadvantages. The disadvantages I'm finding are the following:

  • The text I want to be read-only needs to be wrapped in a InlineUIContainer. That is a little limiting for this solution.
  • I have to capture the 'Enter' key and insert line breaks manually, otherwise, InlineUIContainer.Unloaded() keeps firing everytime the Enter key is pressed. Not fun, but it works for my case.

It's not a great solution, but I think it will work for me. Like I said, I'm not going to mark this as an answer to my own question yet - hopefully someone else will have a better way of doing this.

野味少女 2024-08-02 02:27:05

这可以通过处理两个事件来实现:1) OnMouseDown 2) OnPreviewKeyDown

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Controls;
using System.Windows;
using System.Windows.Input;

namespace WpfApplication2
{
public class MultiPartTextBox : TextBox
{
    private string _prefix;
    private string _content;

    public string Prefix
    {
        get { return _prefix; }
        set { _prefix = value;
        Text = _prefix;
        }
    }

    public string Content
    {
        get { return _content; }
        set { 
            _content = value;
            Text = _prefix + _content;
        }
    }

    public MultiPartTextBox() { _prefix = string.Empty; }

    protected override void OnMouseDown(MouseButtonEventArgs e)
    {

        base.OnMouseDown(e);
        this.SelectionStart = _prefix.Length;
        this.SelectionLength = 0;
    }

    //tab In
    protected override void OnGotFocus(RoutedEventArgs e)
    {
        this.SelectionStart = _prefix.Length;
        this.SelectionLength = 0;
        base.OnGotFocus(e);
    }

    protected override void OnPreviewKeyDown(KeyEventArgs e)
    {
        if (e.Key == Key.Back 
            || e.Key == Key.Delete
            || e.Key==Key.Left)
        {
            if (CaretIndex <= _prefix.Length)
            {
                e.Handled = true;
                return;
            }
        }
        base.OnPreviewKeyDown(e);
    }
  }
  }

在 Xaml 中,我们必须按以下方式处理它:

   xmlns:uc="clr-namespace:WpfApplication2"

       <uc:MultiPartTextBox Height="30" HorizontalAlignment="Left" 
             Margin="80,94,0,0" x:Name="multiPartTxt1" VerticalAlignment="Top" 
             Width="224" Prefix="NON-EDITABLE" CaretIndex="4" >            
       </uc:MultiPartTextBox>

This can be achieved by handling two event: 1) OnMouseDown 2) OnPreviewKeyDown

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Controls;
using System.Windows;
using System.Windows.Input;

namespace WpfApplication2
{
public class MultiPartTextBox : TextBox
{
    private string _prefix;
    private string _content;

    public string Prefix
    {
        get { return _prefix; }
        set { _prefix = value;
        Text = _prefix;
        }
    }

    public string Content
    {
        get { return _content; }
        set { 
            _content = value;
            Text = _prefix + _content;
        }
    }

    public MultiPartTextBox() { _prefix = string.Empty; }

    protected override void OnMouseDown(MouseButtonEventArgs e)
    {

        base.OnMouseDown(e);
        this.SelectionStart = _prefix.Length;
        this.SelectionLength = 0;
    }

    //tab In
    protected override void OnGotFocus(RoutedEventArgs e)
    {
        this.SelectionStart = _prefix.Length;
        this.SelectionLength = 0;
        base.OnGotFocus(e);
    }

    protected override void OnPreviewKeyDown(KeyEventArgs e)
    {
        if (e.Key == Key.Back 
            || e.Key == Key.Delete
            || e.Key==Key.Left)
        {
            if (CaretIndex <= _prefix.Length)
            {
                e.Handled = true;
                return;
            }
        }
        base.OnPreviewKeyDown(e);
    }
  }
  }

IN Xaml we have to handle it in following way:

   xmlns:uc="clr-namespace:WpfApplication2"

       <uc:MultiPartTextBox Height="30" HorizontalAlignment="Left" 
             Margin="80,94,0,0" x:Name="multiPartTxt1" VerticalAlignment="Top" 
             Width="224" Prefix="NON-EDITABLE" CaretIndex="4" >            
       </uc:MultiPartTextBox>
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文