Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / Languages / C#

Mastering Scrollbars in User Interfaces: A Comprehensive Implementation Guide

5.00/5 (3 votes)
25 Aug 2023CPOL9 min read 6.4K   81  
ScrollBars implementation with Windows Forms because the AutoScroll didn't fit my needs
This article explains shortly the use of scrollbars, why they exist in other words. In different scenarios, it's probably the most complex to make moving the scrollbars automatically as you are typing in that window. So, I wrote an implementation of this scenario in C# for Windows Forms. Although, it is possible to rewrite my implementation for WPF.

Image 1

Introduction

Scrollbars play a crucial role in providing users with the ability to navigate content that extends beyond the visible boundaries of a window.

While their purpose is evident, the complexities arise when the need for seamless and automatic scrollbar adjustments emerges—especially when users interact with the window, such as typing within it.

Regarding my approach, I started to test and make it work correctly, firstly, the mobile window with the scrollbars. And then, I tested when the window is resized. At this stage, the challenge is great because I did not understand how the scrollbars work. So I tested and pondered scrollbars for a while, then the solution appeared. Since it now worked correctly, I tested how to make the scrollbars move automatically as long as the user is typing on their keyboard. Thus, the window advances throughout its writing.

Understanding Scrollbars

The primary purpose of a scrollbar is to allow the user to move the point of view of its document when this document exceeds the size of its window.

There exist two kind of scrollbars: horizontal and vertical.

A scrollbar is first and foremost a bar. And you can scroll this bar. As you scroll the bar, the viewpoint of your document moves simultaneously. The top (or left) of the document is where the scroll bar starts. And the bottom (or the right) of the document corresponds to the end of the scroll bar. So you can't go any further than the top or bottom of the document.

The Need for a Custom Scrollbar Implementation

Scrollbars (horizontal and vertical) are a common user interface. In Windows Forms, you can activate both scrollbars by set the AutoScroll Boolean value to true on any window. And, when your content is larger than the window, scrollbars are visible and then you can scroll along. But, you can only make moving yourself the scrollbars. Suppose now, you have a scenario as you need that it moves automatically, you cannot have an access to properties or functions to modify the default behaviour. So, you are invited to make your own implementation to make that the document moves at the point of view that you implicitly define when you are typing in the document.

Anatomy of a Scrollbar

At the top (or left) and bottom (or right) of a vertical (or horizontal) scroll bar, there is a button on which a small arrow is usually drawn. And between these limits, there is a thumb or a bar that can be refined along a track.

Buttons move the scroll bar like a small step in the document. So you can move the viewpoint very slowly. And the thumb can move the document view faster. You can also click on the track to take a big step. These properties are represented by a percentage of the displacement amount. For example, the default values ​​for LargeChange are 20 and SmallChange is 10.

When designing your window, you can drag a horizontal or vertical scrollbar from the toolbox to your window and position the scrollbars generally on the right edge of the window for the vertical scrollbar and on the lower edge.

When you use a scrollbar, you set a value, a minimum, and a maximum to point to the document. The simple action is just to set the minimum to zero and the maximum to your document size. But usually the minimum and maximum are zero and 100 respectively. So it is a percentage and not a maximum length. But if you set the maximum to 100, the value should be within those limits. The Value property of a scrollbar is simply a position in the scrollbar. And, when you move the scrollbar, you now only have to set the point of view of your document when you customize the scrollbars yourself. If you set the AutoScroll boolean to true, the program does all of these things for you.

Coordinating Scrollbars and Content

When you customize the scrollbars, you can set the minimum and maximum to any numeric values ​​you want. But the value cannot exceed these limits. If so, the program throws an error exception.

For the scroll bar to move automatically, you need to set a value on the scroll bar and this value should not exceed the minimum and maximum. This value corresponds to the position of the coordinates of the document.

Now if you move your scrollbar, the value should change accordingly with the scrollbar and you should move your document's viewpoint. To do this, you just have to change the Left and Top properties of your document to negative values. It's just the window clipping that doesn't show the top corners of the document. This is actually the simple method to coordinate scrollbars and content.

Using the Code

I wrote a specific class UsingScrollbars. This class encapsulates the layout process of the two scrollbars:

  • The initialization of the scrollbars
  • A delegate function is added to each scrollbar to capture the Value and move the document
  • A specific function that you can call to position automatically the point of view
  • Two specific functions that you can call if the document is resized and if the window is resized

With a scrollbar, you have five parameters:

  • a Value representing the scrollbar position
  • a LargeChange, representing the big step
  • a width (or a height) of the inner control (the document)
  • a width (or a height) of the panel content (the window)
  • the Left (or the Top) corner of the inner control (the document)

In five parameters, you handle all the functionalities of a scrollbar.

I also write a specific class UsingScrollbarsConverter. This class provides a few functions:

  • ConvertFromValue: converts the Value to the coordinate for the upper corner of the document
  • ConvertToValue: converts the upper corner of the document into a Value
  • VerifyIfNotViewed: says true or false if the position is not viewed

This class simplifies the conversion logic because you have to recall these functions each time in the code. So, this class provides function to just call when needed.

There are two event handlers. One for the horizontal scrollbar and the other for the vertical scrollbar. These event handlers are initialized with the delegate functions, HorizontalScrollBarMethod and VerticalScrollBarMethod respectively. And then, these event handlers are called when you move the scroll bar and then your goal is to move the top corners of the document to change the viewpoint of the document in the window.

C#
private void HorizontalScrollBarMethod(object? sender, ScrollEventArgs e)
{
    Debug.WriteLine("Horizontal new value : " + e.NewValue);
    int convertedValue = -UsingScrollBarsConverter.ConvertFromValue
                         (_panel.Width - _vScrollBar.Width,
                          _innerControl.Width, e.NewValue, _hScrollBar.LargeChange);
    Debug.WriteLine("Horizontal converted value : " + convertedValue);
    _innerControl.Left = convertedValue;
}

private void VerticalScrollBarMethod(object? sender, ScrollEventArgs e)
{
    Debug.WriteLine("Vertical new value : " + e.NewValue);
    int convertedValue = -UsingScrollBarsConverter.ConvertFromValue
                         (_panel.Height - _hScrollBar.Height,
                          _innerControl.Height, e.NewValue, _vScrollBar.LargeChange);
    Debug.WriteLine("Vertical converted value : " + convertedValue);
    _innerControl.Top = convertedValue;
}

Note the negative operator towards the function UsingScrollBarsConverter.ConvertFromValue.

I set the minimum to 0 and the maximum to 100. So, when I get a Value, I need to convert this Value into the point of view of the document. The conversion method is just a proportional division.

C#
public static int ConvertFromValue(int panelContent, int innerControl, 
                                   int newValue, int LargeChange)
{
    // multiply
    float multiply = newValue * (innerControl - panelContent) / 
                                (float)(100 - LargeChange);
    return (int)multiply;
}

Generally, we just set the top corner by calculating the difference between the old value and the new value of the scrollbar. Here, we don't do that, but calculate the position without going past the bottom corner of the document. If the function is not calculating exactly the upper corner of the document, you should go further in the document.

As the maximum is 100, the LargeChange is 20, you can go up to 80% document viewing. This means that 20% should be equivalent to the size of your container (the panel). Below, the picture shows a vertical scrollbar and explains what is exactly the LargeChange value. So, in the function ConvertFromValue, we divide by 100 - LargeChange and not by 100.

Image 2

The LargeChange is set as a value that makes the Value to obtain the number until 80 exactly and not almost or too many. These values are initially equal to:

C#
_vScrollBar.LargeChange = 20;
_hScrollBar.LargeChange = 20;
_vScrollBar.SmallChange = 10;
_vScrollBar.SmallChange = 10;

But, for this to work when the window is resized, you need to set these values ​​proportional to the document and the panel. If the panel is larger than the document, the scroll bar is disabled.

C#
// horizontal scrollbar
float horizontalProportion = GetInnerControlEffectiveSize
                             (innerControl, panel, vScrollBar, hScrollBar).Width
                             / (float)GetPanelContentEffectiveSize
                             (panel, vScrollBar, hScrollBar).Width;
if (horizontalProportion >= 1)
{
    hScrollBar.Enabled = true;
    SetHScrollBarSmallAndLargeChange(horizontalProportion, hScrollBar);
    hScrollBar.Value = 0;
    innerControl.Left = 0;
}
else
{
    hScrollBar.LargeChange = 100;
    hScrollBar.SmallChange = 100;
    hScrollBar.Enabled = false;
    hScrollBar.Value = 0;
    innerControl.Left = 0;
}
Debug.WriteLine("HScrollBar Enabled : " + hScrollBar.Enabled);
Debug.WriteLine("HScrollBar LargeChange :" + hScrollBar.LargeChange);
Debug.WriteLine("HScrollBar SmallChange :" + hScrollBar.SmallChange);

// vertical scrollbar
float verticalProportion = GetInnerControlEffectiveSize
                           (innerControl, panel, vScrollBar, hScrollBar).Height
                           / (float)GetPanelContentEffectiveSize
                           (panel, vScrollBar, hScrollBar).Height;
if (verticalProportion >= 1)
{
    vScrollBar.Enabled = true;
    SetVScrollBarSmallAndLargeChange(verticalProportion, vScrollBar);
    vScrollBar.Value = 0;
    innerControl.Top = 0;
}
else
{
    vScrollBar.LargeChange = 100;
    vScrollBar.SmallChange = 100;
    vScrollBar.Enabled = false;
    vScrollBar.Value = 0;
    innerControl.Top = 0;
}
Debug.WriteLine("VScrollBar Enabled : " + vScrollBar.Enabled);
Debug.WriteLine("VScrollBar LargeChange :" + vScrollBar.LargeChange);
Debug.WriteLine("VScrollBar SmallChange :" + vScrollBar.SmallChange);

When the Coordinates Change

When the coordinates change, you are moving the insertion point in the document. If you go further than the viewport, you will never see the insertion point. Then the scrollbar should move automatically to display the insertion point.

To do this, I have a function to transform a position into a value. This function is named ConvertToValue. It converts a position (the upper corner of the document) to a value as a proportional operation.

C#
/// <summary>
/// Converts a position to a scrollbar value
/// </summary>
/// <param name="coordinate">position (x or y)</param>
/// <param name="maximale">maximale position</param>
/// <returns>the value by percent</returns>
public static int ConvertToValue(int panelContent, 
              int innerControl, int coordinate, int LargeChange, int nTurn = 1)
{
    return (int)(coordinate / (nTurn * (float)(innerControl - panelContent)) * 
                              (float)(100 - LargeChange));
}

The function WhenCoordinatesChanged is the function that hosts the layout and logic operations to move the scrollbar only if you exceeds the visible boundaries of the window. So, we verify first that the insertion point is viewed or not. We have a function named VerifyIfNotViewed.

C#
public static bool VerifyIfNotViewed(int innerControl, 
                   int panelContent, int view, int coordinate)
{
    Debug.WriteLine("View : " + view + ", PanelContent : " + 
                     panelContent + ", Coordinate : " + coordinate + ", 
                     NotViewed : " + ((coordinate - view) > panelContent || 
                     coordinate < view));
    return (coordinate - view) > panelContent || coordinate < view;
}

Below, I show the idea of ​​moving the window to a position in the document and how to calculate the new top corner of the document. At number 1 shown in this image, this position is further down the document. To calculate the Value, I simply call the ConvertToValue function and set the Value of the scrollbar (vertical or horizontal) to that result. At number 2, the position is before the window. And I just call the ConvertToValue function and set the value to that result. But, to know if the position is viewed or not, the 1 answers true to VerifyIfNotViewed because of the comparison (coordinate - view) > panelContent and the 2 answers true because of the comparison coordinate < view.

Image 3

To debug, I use the function:

C#
Debug.WriteLine(string? s);

The debug output is the only solution to read values and not the debugger because it is raised continuously and also, the focus event occurred each time I switched between the debugger and the window.

Conclusion

This was an implementation of the ScrollBars in C# for Windows Forms to view a document that exceeds the boundaries of the window.

There are three advantages to use this implementation:

  1. Automatic Document Navigation: Seamlessly adjust the document's view as you type, eliminating the need for manual scrollbar manipulation during interaction.

  2. Efficient and Encapsulated Design: By encapsulating layout and logic processes within two classes, the implementation offers a streamlined way to import and integrate this functionality into your application, sparing you from extensive debugging efforts. This streamlined integration can save you valuable development time, potentially half an hour or more.

  3. Flexible Adaptation: The code's organizational structure facilitates modifications to both logic and layout, allowing easy adaptation for documents with padding or other variations.

By engaging my problem-solving skills, I employed a comprehensive approach to dissect and understand the intricacies of scrollbar behaviour, ensuring a robust and effective solution.

History

  • 25th August, 2023: Initial version

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)