4

I'm trying to get the content height of a RichTextBox and if it exceeds 500px, split it into multiple RichTextBoxes with the maximum height of 500px.

Anyone know how to do this? Thanks.

EDITED:

by "split" I mean put the content that exceeds the height limit in another RichTextBox, the solution can't just change the appearence of the current RichTextBox due to project specificities.

  • 1
    Splitting it means a specific layout configuration. Do you plan to have them on a specific panel/items control ? – Sisyphe Nov 06 '12 at 08:59
  • 2
    Is this for display only or does the back-end data need to be split. You could possibly achieve this by binding the same data to multiple text boxes and overriding the scroll position – Bob Vale Nov 06 '12 at 10:17
  • @BobVale your solution sounds good, the only thing is that the user will be able to edit the RichTextBox content, so if the users scroll the content they will see the content that was hidden, there are any way to change the scroll and delete all text that aren't visible? – Victor Ribeiro da Silva Eloy Nov 06 '12 at 12:09
  • Thx for the comment but it still does not explain your functional scenario :) Getting two textBoxes out of one based on a size constraint is doable, but you have to know what you will do with the second textbox. Will it be in a panel ? an items control ? If it's an item control will it be dataBound etc. The way you can split the textbox AND insert it in your visual tree does matter. EDIT : Would a control that would be compose of a panel with multiple textboxes be ok for you ? – Sisyphe Nov 06 '12 at 12:43
  • 1
    @Sisyphe I will try to explain the scenario, we are creating a book editor, when the user drop a .doc file inside the current page, we get the file content as rtf and put inside a RichTextBox, but if the the content height exceeds the page height we need to split the content create a new page and insert the other RichTextBox inside the new page. Only one page is shown at time. – Victor Ribeiro da Silva Eloy Nov 06 '12 at 13:03

5 Answers5

3

I played a bit with this splitting RichTextBox issue and it's not easy ^^ My idea would be this :

  • Create a SplitRichTextBox CustomControl that will inherit RichTextBox (mainly to get all the depency properties)
  • Add a MaxContentHeight double dependency property to manage max size (in your case 500px)
  • Add a Remainders IList< RichTextBox > dependency property that will be the remainder of you current SplitRichTextBox once it's been split.
  • Customize the ControlTemplate of your SplitRichTextBox to display the SplitRichTextBox content and the remainder in a panel that will suit your layout needs.
  • override the OnTextChanged Method on your SplitRichTextBox.

In this method retrieve the underlying richTextBox flowDocument and retrieve the paginator. Set the MaxPageHeight and compute the document pages.

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

You will now be able to retrieve the content of the each page thanks to the GetPage(int i) method on the paginator. The first page is what you want for your first RichTextBox. Remainding pages will be used to instantiate the Remainders dependency property.

Issue is that the pages are not directly RTF or flowDocument. You will need to somehow extract the data from the paginator/page to get how to split your document. This is where I stopped and it's maybe not the way to go, but I think it's worth trying.

Good luck !

Sisyphe
  • 4,626
  • 1
  • 25
  • 39
  • This sounds good, I'll try the @Serge-Belov aproach first because it doens't requires changes in the class hierarchy, and if it not works I'll try yours. Thank you I relly appreciate your help. – Victor Ribeiro da Silva Eloy Nov 08 '12 at 19:53
  • If somebody else stumbles on this answer, here is a way to retrieve data from each DocumentPage: [Is it possible in WPF to get content of each DocumentPage by page number?](http://stackoverflow.com/questions/25806961/is-it-possible-in-wpf-to-get-content-of-each-documentpage-by-page-number) – Absolom May 11 '15 at 20:22
1

Have you tried using this code in the RichTextBox.ContentsResized Event.

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

If there's any alternate c# code for this, i don't know.

Also additional info - The richtextbox control is drawn using GDI, while 'Graphics.MeasureString' measuers the string using GDI+. Consequently 'MeasureString' won't return the exact size of the string. I fear you will have to dig into the GDI32-API to get exact results. if you are interested in using Win32 GDI API calls, look here

hridya pv
  • 1,039
  • 1
  • 7
  • 17
  • I can't jus change the height of my RichTextBox, because all the content of each RichTextBox should fit the max height of 500Px, so if i change the heigth the RichTextBox can exceeds the maximum height. And this is a WPF application, so the code can't be based on windows forms components, Thanks. – Victor Ribeiro da Silva Eloy Nov 06 '12 at 12:19
  • WPF RichTextBox doesn't provide the functionality to adjust its width to the text. As far as I know, RichTextBox use a FlowDocumentView in its visual tree to render the Flowdocument. It will take the available space to render its content, so it won't adjust its size to the content. Since this is an internal class, it seems we cannot override the layout process to let a RichTextBox to adjust its size to the text.Try and loop through the flowdocument in RichTextBox recursively or use grid having horizontal and vertical alignment properties to wrap the RTB. – hridya pv Nov 07 '12 at 08:44
1

I think i got your answer..Thanks to google and stackoverflow.

A WPF FlowDocument can only belong to a single RichTextBox. But if you are using a single document which can be manipulated at different points in the UI, then it will not happen that there are two RichTextBoxes simultaneously displaying a single document (and can't, because WPF will complain). But are you using a single document or multiple? if single, read the rest and if not, well, move along.

Using a MemoryStream and XamlReader/Writer won't work here as we would like to retain a single document and reflect changes wherever it is used, so copying it every time is out. Copied from a member Jared of stackoverflow,

WPF controls can be ‘unparented’ and ‘reparented’ at will, so just make the RichTextBox instance available in the context shared throughout your wizard and make sure you unparent / reparent as you move from page to page. This also has the benefit of saving any styles or changes to the editors state across pages in the wizard (which is probably desirable).

If it isn’t possible to share the RichTextBox instance across pages, I think there is a way to disassociate the document from the original RichTextBox. It appears that in order to disassociate the document from RichTextBox1, you have to provide RichTextBox1 with a new document. You can’t set RichTextBox1.Document to null, but you CAN set RichTextBox1.Document to new FlowDocument() and I believe that will work.

From the above, FlowDocument cannot be shared by several RichTextBox controls directly.. So as i comment above, use grid

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

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

to wrap the RTB and in the code behind use

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

i don't know whether this will split the control but we can see the document(single) in another RTB.

hridya pv
  • 1,039
  • 1
  • 7
  • 17
1

To get the content that exceeds the height limit in another RichTextBox, I'd use the following approach :

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

Having the text ranges, you could copy them into a new FlowDocument for another RichTextBox, truncate them (range.Text = string.Empty), etc.

Serge Belov
  • 5,633
  • 1
  • 31
  • 40
-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();
}

this is a easy way to control the crescent grid size and also to create rtb at will :D

SWeko
  • 30,434
  • 10
  • 71
  • 106