0

There is a RichTextBox with scrollbars disabled. Tried to find out the maximum scroll value, using:

  • GetScrollPos - returns 0;
  • GetScrollPosInfo - returns 0;
  • GetScrollRange - returns 100;
  • https://techarks.ru/qa/csharp/poluchit-polosu-prokrutki-q-I4/ - allows you to know only the pitch (px), if you scroll to the end will show the maximum, if you want to know it in the beginning - nothing (requires scrolling to the end);
  • float heightLine = targetCtrl.Font.GetHeight() / 3; Maximum = (int)Math.Ceiling(targetCtrl.GetPreferredSize(targetCtrl.Size).Height - targetCtrl.Height + heightLine); - at the beginning the maximum is correct, but as you add or change the size of the elements you get a value that is smaller than the real maximum.
Tatami
  • 105
  • 1
  • 7
  • 1
    It's explained here: [How to scroll a RichTextBox control to a given point regardless of caret position](https://stackoverflow.com/a/65242362/7444103) -- The PreferredSize of the RichTextBox Control is related to the text content before word wrapping, so you cannot use. To calculate the maximum Scroll value, you have to determine the absolute difference between the `Y` position of the first char (becomes negative when scrolling down) and the last, then subtract ClientSize.Height. – Jimi May 30 '21 at 16:57
  • Be careful not to generate stackoverflow exceptions if you decide to use SendMessage to set the scroll position of your own scrollbars. -- *absolute difference* -> absolute distance :) – Jimi May 30 '21 at 17:02
  • Nope, you always need the absolute value of the position at index `0`, to calculate the difference. Then it depends what this measure is for. If you want to use SendMessage, you send the absolute value. E.g., `var pos0 = [RTB].GetPositionFromCharIndex(0).Y; var scrollPos = [RTB].GetPositionFromCharIndex(6).Y + Math.Abs(pos0 - 1); SendMessage([RTB].Handle, WM_VSCROLL, scrollPos << 16 | SB_THUMBPOSITION, 0);`. This will scroll to the line that contains `World` (assuming the RTB is scrollable). – Jimi May 30 '21 at 17:56
  • To determine what the Scrollbar's Max Value will be, then subtract ClientSize.Height. It will probably return 0 here (a RTB with two lines of text when the Control can show just one line). – Jimi May 30 '21 at 17:58

1 Answers1

2

The purpose of all the calculations presented below is: to determine the maximum scroll value of the RichTextBox for working with the custom scrollbar.

Useful materials / tips:

  1. To find out the value that the standard scrollbar receives while scrolling, use the Microsoft Spy++ program;
  2. Absolute Difference Calculator: https://calculatorpack.com/absolute-difference-calculator.

Determine the maximum vertical scroll value:

NOTE: (code works correctly if you don't scale the RichTextBox contents).

private void DetermineVMax(RichTextBox RTB) {      
  // Y position of the first character
  int y1 = RTB.GetPositionFromCharIndex(0).Y;

  // Y position of the last character
  int y2 = RTB.GetPositionFromCharIndex(RTB.TextLength).Y;

  // First option: The height of the line - returns 43
  double heightLine = Math.Ceiling(RTB.Font.GetHeight());

  // Second option: The height of the line - returns 45
  // NOTE: GetFirstCharIndexFromLine( NUMBER )
  // 0 is the first line, 1 is the second line, etc.
  //int index = targetCtrl.GetFirstCharIndexFromLine(1);
  //int heightLine = targetCtrl.GetPositionFromCharIndex(index).Y;

  // Absolute difference between the position of the 1st and the last characters
  int absoluteDifference = Math.Abs(y1 - y2);

  // RichTextBox height
  int heightRtb = RTB.ClientSize.Height;

  // Maximum vertical scroll value
  // NOTE: if you don't add a line height, the RTB content will not be displayed in full
  int max = absoluteDifference - heightRtb + (int)heightLine;
}

Thanks to the stack overflow response (Size RichTextBox according to contents), a another solution was found for calculating the maximum value of vertical / horizontal scrolling (taking into account the scaling inside the RichTextBox).

Order of actions

  1. Sign the RichTextBox to "ContentsResized" and "Resize" events;
  2. Use the code below.

Determine the maximum vertical scroll value:

int heightContentRtb, widthContentRtb;

private void DetermineVMax(RichTextBox RTB) {
    int heightContainerRtb = RTB.ClientSize.Height;
    int max = heightContentRtb - heightContainerRtb + 1;
}    

Determine the maximum horizontal scroll value:

private void DetermineHMax(RichTextBox RTB) {
    int widthContainerRtb = RTB.ClientSize.Width;
    int max = widthContentRtb - widthContainerRtb + 1;
}

Code inside the "ContentsResized" and "Resize" events

private void richTextBox1_ContentsResized(object sender, ContentsResizedEventArgs e) {
    //heightContentRtb = e.NewRectangle.Height; DetermineVMax(richTextBox1);
    widthContentRtb = e.NewRectangle.Width; DetermineHMax(richTextBox1);
}

private void richTextBox1_Resize(object sender, EventArgs e) {
    //DetermineVMax(richTextBox1);
    DetermineHMax(richTextBox1);
}
Tatami
  • 105
  • 1
  • 7
  • Is this an answer? Maybe the method should return a value, and take one parameter of type `RichTextBox`? – Dialecticus Jun 01 '21 at 13:41
  • Tweaked the code to make it easier to understand. – Tatami Jun 01 '21 at 16:16
  • It's not easier. You converted something that appeared to be a C# code to something that no longer appears so. – Dialecticus Jun 02 '21 at 08:25
  • This is correct, implementation-wise (part of this is handled internally by the System Scrollbars - but you may have your own logic in your own designed Scrollbars). As a note, as previously described, you can replace: `int start = RTB.GetFirstCharIndexFromLine(0); int y1 = RTB.GetPositionFromCharIndex(start).Y;` with just `int y1 = RTB.GetPositionFromCharIndex(0).Y;`, that's `relative position 0`. – Jimi Jun 04 '21 at 14:02
  • Thank you for the hint you gave earlier, it helped me a lot. As for the calculations related to finding the "absolute difference", you can omit them, because the following code (RTB.GetPositionFromCharIndex(0).Y;) -will always return zero. – Tatami Jun 04 '21 at 14:35
  • More precisely: `[TextBoxBase].GetPositionFromCharIndex(0).Y` returns `0` or `1` (since it counts the border - `BorderStyle = none` then `0`, otherwise `1`) when your Control's viewport is not scrolled. When you scroll it, the value of `Y` is negative. Reason why you need the absolute distance between this point and the `Y` position of the line to scroll to - it can be incremented by the Font.Height to scroll to the baseline position of the text. – Jimi Jun 04 '21 at 14:52
  • Something like this is described here: [TextBox with dotted lines for typing](https://stackoverflow.com/a/64673192/7444103) and in [Properly draw text using GraphicsPath](https://stackoverflow.com/a/53074638/7444103) – Jimi Jun 04 '21 at 14:56