将过高的WPF RichTextBox分割成多个控件

4
我试图获取RichTextBox的内容高度,如果超过500px,则将其拆分为多个最大高度为500px的RichTextBox。
有人知道如何做到这一点吗?谢谢。
编辑:
通过“拆分”,我指的是将超出高度限制的内容放入另一个RichTextBox中,由于项目的特殊性,解决方案不能只更改当前RichTextBox的外观。

1
拆分它意味着特定的布局配置。您计划将它们放在特定的面板/项目控件上吗? - Sisyphe
2
这是仅用于显示还是需要拆分后端数据?您可以通过将相同的数据绑定到多个文本框并覆盖滚动位置来实现。 - Bob Vale
@BobVale,你的解决方案听起来不错,唯一的问题是用户将能够编辑RichTextBox内容,因此如果用户滚动内容,他们将看到被隐藏的内容,有没有办法更改滚动并删除所有不可见的文本? - Victor Ribeiro da Silva Eloy
谢谢评论,但它仍未解释您的功能场景 :) 基于大小约束从一个文本框中获取两个文本框是可行的,但您必须知道您将如何使用第二个文本框。它将在面板中吗?还是项控件?如果它是项控件,那么它是否被数据绑定等。拆分文本框并将其插入到您的可视树中的方式确实很重要。编辑:对于您来说,由多个文本框组成的面板的控件是否可以接受? - Sisyphe
1
@Sisyphe 我会尝试解释一下情况。我们正在创建一款书籍编辑器,当用户将.doc文件拖放到当前页面时,我们将获取文件内容并将其作为rtf放入RichTextBox中,但是如果内容的高度超过页面高度,我们需要拆分内容并创建一个新页面,在新页面中插入另一个RichTextBox。每次只显示一页。 - Victor Ribeiro da Silva Eloy
5个回答

3
我尝试处理这个分割 RichTextBox 的问题,但它并不容易^^ 我的想法是:
  • 创建一个名为SplitRichTextBox的自定义控件,该控件将继承 RichTextBox 控件(主要是为了获取所有依赖属性)。
  • 添加一个名为MaxContentHeight的双重依赖属性来管理最大大小(在您的情况下为 500px)。
  • 添加一个名为Remainders的 IList< RichTextBox > 依赖属性,它将是您当前 SplitRichTextBox 分割后的余数。
  • 自定义 SplitRichTextBox 的 ControlTemplate,以显示 SplitRichTextBox 内容和余数,并根据布局需求适配面板。
  • 重写 SplitRichTextBox 的 OnTextChanged 方法。

在此方法中,获取底层 richTextBox flowDocument 并检索 Paginator。设置 MaxPageHeight 并计算文档页数。

var flowDocument = this.Document;
flowDocument.MaxPageHeight = this.MaxContentHeight;
DocumentPaginator paginator = ((IDocumentPaginatorSource)flowDocument).DocumentPaginator;
paginator.ComputePageCount();

现在,您可以通过分页器上的GetPage(int i)方法检索每个页面的内容。第一页是您想要用于第一个RichTextBox的内容。其余页面将用于实例化剩余依赖属性。
问题在于页面不是直接的RTF或flowDocument格式。您需要从分页器/页面中提取数据以了解如何拆分文档。这就是我停下来的地方,也许不是最好的方法,但我认为值得一试。
祝你好运!

这听起来不错,我会先尝试@Serge-Belov的方法,因为它不需要更改类层次结构,如果不行,我会尝试你的方法。谢谢,非常感谢你的帮助。 - Victor Ribeiro da Silva Eloy
如果其他人也遇到了这个问题,这里有一种从每个DocumentPage检索数据的方法:在WPF中是否可以通过页码获取每个DocumentPage的内容? - Absolom

1

我想我得到了你的答案...感谢Google和stackoverflow。

一个WPF FlowDocument只能属于一个RichTextBox。但是,如果您使用单个文档,在UI的不同点可以对其进行操作,则不会发生两个RichTextBox同时显示单个文档(因为WPF会抱怨)。但是,您是否正在使用单个文档还是多个文档?如果是单个,请继续阅读;如果不是,请移步下一段。

在此处使用MemoryStream和XamlReader / Writer将不起作用,因为我们希望保留单个文档并在使用它的任何位置反映更改,因此每次复制它都不行。这是从stackoverflow的成员Jared中复制的。

WPF控件可以随意“取消父子关系”和“重新父子关系”,因此只需在向导共享的上下文中使RichTextBox实例可用,并确保在从一页到另一页移动时取消父子关系/重新父子关系即可。这还有一个好处,即在向导的各个页面中保存编辑器状态的任何样式或更改(可能是值得欢迎的)。

如果无法在页面之间共享RichTextBox实例,我认为有一种方法可以将文档与原始的RichTextBox分离。似乎要将文档与RichTextBox1分离,必须为RichTextBox1提供一个新文档。您不能将RichTextBox1.Document设置为null,但是您可以将RichTextBox1.Document设置为new FlowDocument(),我相信这样会起作用。

从上面可以看出,FlowDocument不能直接由多个RichTextBox控件共享。因此,如我上面所述,请使用网格。

<Grid>
    <Grid.ColumnDefinitions>
        <ColumnDefinition />
        <ColumnDefinition />
    </Grid.ColumnDefinitions>

    <Grid.RowDefinitions>
        <RowDefinition />
        <RowDefinition Height="Auto" />
    </Grid.RowDefinitions>

在代码后台中使用包装RTB

    FlowDocument doc = RTB1.Document;
    RTB1.Document = new FlowDocument();
    RTB2.Document = doc;

我不知道这是否会分割控件,但我们可以在另一个 RTB 中看到单个文档。


1
为了获取超出另一个RichTextBox高度限制的内容,我会使用以下方法:
    private TextRange GetTopRange()
    {
        var result = textBox.GetPositionFromPoint(new Point(0, 0), true);
        if (result == null)
            return null;
        result = result.GetInsertionPosition(LogicalDirection.Forward);
        return new TextRange(result.DocumentStart, result);
    }

    private TextRange GetBottomRange()
    {
        var result = textBox.GetPositionFromPoint(new Point(textBox.ActualWidth, textBox.ActualHeight), true);
        if (result == null)
            return null;
        result = result.GetInsertionPosition(LogicalDirection.Backward);
        return new TextRange(result, result.DocumentEnd);
    }

有了文字范围,您可以将它们复制到另一个RichTextBox的新FlowDocument中,截断它们(range.Text = string.Empty)等。


这听起来不错,我会尝试这种方法。等我之后告诉你它是否有效。谢谢。 - Victor Ribeiro da Silva Eloy

1
你尝试过在RichTextBox.ContentsResized事件中使用这段代码吗?
Private Sub rtb_ContentsResized(ByVal sender As Object, ByVal e As System.Windows.Forms.ContentsResizedEventArgs) Handles txtQuestion.ContentsResized
    Dim h = e.NewRectangle.Height, w = e.NewRectangle.Width
    h = Math.Max(h, sender.Font.Height)
    h = Math.Min(h, Me.ClientSize.Height - 10 - sender.Top)
    h += sender.Height - sender.ClientSize.Height + 1
    sender.Height = h

End Sub

如果有任何这方面的C#代码,我不知道。

还有额外的信息- Richtextbox控件是使用GDI绘制的,而“Graphics.MeasureString”是使用GDI+测量字符串的。因此,“MeasureString”不会返回字符串的精确大小。我担心您将不得不深入研究GDI32-API以获得精确结果。如果您有兴趣使用Win32 GDI API调用,请点击此处


我不能只更改RichTextBox的高度,因为每个RichTextBox的内容都应适合最大高度500像素,因此如果更改高度,RichTextBox可能会超出最大高度。而且这是一个WPF应用程序,所以代码不能基于Windows表单组件,谢谢。 - Victor Ribeiro da Silva Eloy
WPF RichTextBox没有提供调整其宽度以适应文本的功能。据我所知,RichTextBox在其可视树中使用FlowDocumentView来呈现Flowdocument。它将使用可用空间来呈现其内容,因此它不会调整其大小以适应内容。由于这是一个内部类,似乎我们无法覆盖布局过程以让RichTextBox调整其大小以适应文本。尝试递归地遍历RichTextBox中的flowdocument或使用具有水平和垂直对齐属性的网格来包装RTB。 - hridya pv

-1
public void CreateNewRtb(object sender, RoutedEventArgs routedEventArgs)
{
  var res = (ResourceDictionary)Application.LoadComponent(new Uri("/Design/Style/TextAreaStyle.xaml", UriKind.Relative));
  var mcRtb = new RichTextBox {Style = (Style) res["TextBoxStyle"], Name = "Folha" + J};
  RegisterName("Folha" + J, mcRtb);
  mcRtb.TextChanged += McRtbContentControl;
  var gcrd = new RowDefinition();
  var gcrdspace = new RowDefinition();
  gcrd.Height = new GridLength(980);
  GridControl.RowDefinitions.Add(gcrd);
  Grid.SetColumn(mcRtb, 1);
  Grid.SetRow(mcRtb, 1 + I);
  GridControl.Children.Add(mcRtb);
  I += 2;
  J++;
  gcrdspace.Height = new GridLength(30);
  GridControl.RowDefinitions.Add(gcrdspace);
  mcRtb.Focus();
}

这是一种简单的方法来控制月牙网格的大小,并随意创建 RTB :D


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接