Click here to Skip to main content
15,867,568 members
Articles / Programming Languages / C# 3.5

ViewFile and Head Tools

Rate me:
Please Sign up or sign in to vote.
4.82/5 (10 votes)
21 Dec 2022CPOL13 min read 22.4K   1.2K   29   4
This article is a significant revision to the earlier version and introduces a new version of ViewFile.

1. Introduction Table of Contents

This article is the last planned revision to earlier versions of the ViewFile tool. What triggered the revision was the large size of a file I was creating and the need to examine a specific byte within that file. Scrolling down through the file until I reached the desired byte did not seem like the best user experience.

The revision includes:

  • Addition of a Start At TextBox that allows a user to specify the byte location where the display is to start.
  • Setting the background color of read only objects to the GradientActiveCaption system color.
  • Increasing the font of the user interface.
  • Removal of the Head application

2. Background Table of Contents

Often, during the development process, it is useful to know the contents of a file in a format that is not the default format provided by applications. For example, a .txt file is usually displayed by Notepad as a series of lines of text; a .png file by an image viewer as a picture. Normally, default Windows tools do not offer the option to display the contents of a file in a byte-ordered display. ViewFile seeks to repair that ommission.

3. A Note on Typography and Images Table of Contents

In the following discussions, objects, used internally by the software are displayed in italicized text; objects that are accessed by the user are displayed in italicized bold text. For those images whose contents are difficult to read, the image is a thumbnail. Upon clicking on the image, a larger version will be displayed using the default image viewer.

4. ViewFile Table of Contents

ViewFile

ViewFile is a straight-forward file dump facility. On its initial execution, ViewFile accepts the name of an input file when the user clicks on the Browse button.

 

ViewFile Initial

ViewFile provides a last directory visited feature that "remembers" the path of the last file examined. This path is placed into a file, named last_directory.dat, for later reference. Because last_directory.dat may be revised during the execution of ViewFile, it is placed into the user's profile folder. The path in Windows 7 is C:\Users\<Username>\ViewFile\last_directory.dat. If that file does not exist, it will be created and the last directory visited will be set to C:\Users\<Username>\ViewFile and saved in the newly created file.

ViewFile is reentrant, meaning that the Browse button may be clicked again to supply a new input file name.

When the user has provided the name of the input file, ViewFile saves that file's directory path in last_directory.dat. Viewfile then displays the file's size, a TextBox into which the user may specify the byte at which the display is to start, and a Go button that starts the display.

ViewFile Start

When the user clicks the Go button, the file contents are displayed, starting at the value in the Start At TextBox.

ViewFile Executing

Note that, in this case, the file whose contents are being displayed is a Portable Network Graphics file (recognized not only by its name, .png, but also by its file signature, 89 50 4E 47 0D 0A 1A 0A). For a good source of file signatures see Gary Kessler's File Signatures Table [^].

The user may scroll up and down through the file contents using the vertical scroll bar to the right of the window.

Keyboard Keys

ViewFile also responds to the following keyboard keys.

  • Home
  • Page Up
  • End
  • Page Down
  • Up Arrow
  • Down Arrow

ViewFile responds to a key that is being held down (with the exception of Home and End).

 

The user may choose to:

  • Change the left-hand format from hexadecimal to decimal.
  • Cause the display to stay on top of all other desktop windows.
  • Replace spaces on the right-hand side by the characters "<SP>". This is useful when the number of space characters on the right may not be immediately apparent.
 

5. Implementation Table of Contents

The ViewFile constructor is:

C#
:
// ******************************************* class constants
:
const int       TIMER_REPEAT_DELAY = 400;
:
// ************************************************** ViewFile

public ViewFile ( )
    {

    InitializeComponent ( );

    Application.ApplicationExit += new EventHandler (
                                        OnApplicationExit );
    OnApplicationExit ( this, EventArgs.Empty );
                                // TextBox MouseWheel
    if ( SystemInformation.MouseWheelPresent )
        {
        contents_TB.MouseWheel += new MouseEventHandler (
                                          TB_MouseWheel );
        }
                                // repeat timer
    timer = new Timer ( );
    timer.Interval = TIMER_REPEAT_DELAY;
    timer.Tick += new EventHandler ( TIMER_Tick );
    timer.Enabled = false;

    initialize_global_variables ( contents_TB );
    signon ( );
    initialize_GUI ( contents_TB );

    } // ViewFile

contents_TB is the TextBox that displays the contents of the user-chosen file. It is declared in the Visual Studio Designer [^]. TIMER_REPEAT_DELAY is a global constant that contains the number of milliseconds (400) before the timer Tick event is raised again. The value cannot be less than one. The value of 400 milliseconds appears to be a reasonable choice for the timer repeat interval.

After these tasks complete, ViewFile is wholly event driven.

5.1. ViewFile Event Handlers Table of Contents

5.1.1. OnApplicationExit Event Handler Table of Contents

Although this event handler is not strictly required, it is declared so that the FileStream [^], whose contents are to be displayed, will be closed and disposed and the Windows Timer [^] will be stopped and disposed when the application exits.

C#
using System;
:
using System.IO;
:
using System.Windows.Forms;
    :
    // ******************************************* class variables
    :
    FileStream  file_stream = null;
    :
    Timer       timer = null;       // Windows Timer
    :
    // ***************************************** OnApplicationExit

    void OnApplicationExit ( object    sender,
                             EventArgs e )
        {

        if ( file_stream != null )
            {
            file_stream.Close ( );
            file_stream.Dispose ( );
            file_stream = null;
            }

        if ( timer != null )
            {
            if ( timer.Enabled )
                {
                timer.Stop ( );
                }
            timer.Dispose ( );
            timer = null;
            }
        }

5.1.2. BUT_Click Event Handler Table of Contents

The BUT_Click event handler fields the Button [^] Click Events [^] of the Browse, Go, and Exit buttons.

C#
// ************************************************* BUT_Click

void BUT_Click ( object     sender,
                 EventArgs  e )
    {
    Button  button = ( Button ) sender;
    string  name = button.Name.Trim ( );

    switch ( name )
        {
        case "browse_BUT":
            if ( browse ( ) )
                {
                if ( open_file_stream ( ) )
                    {
                    if ( get_file_statistics ( ) )
                        {
                        file_size_TB.Text =
                            file_bytes.ToString ( );
                        start_at = 0;
                        start_at_TB.Text =
                            start_at.ToString ( );
                        start_at_and_go_GB.Visible = true;
                        }
                    }
                }
            break;

        case "go_BUT":
            if ( start_at_valid ( ) )
                {
                display_file ( );
                contents_TB.Focus ( );
                }
            break;

        case "exit_BUT":
            Application.Exit ( );
            break;

        default:
            throw new ApplicationException (
                String.Format (
                    "{0} is not a recognized Button name",
                    name ) );
        }

    } // BUT_Click

browse obtains the filename of the file that is to be displayed using the last_directory; open_file_stream attempts to open the user specified file; and get_file_statistics obtains the length of the input file in bytes.

start_at_valid determines if the user supplied value is valid. If there were no errors, display_file is invoked. Note that display_file is invoked only once for each file.

C#
:
// ******************************************* class variables
:
VScrollBar  textbox_VSB = null;
:
// ********************************************** display_file

/// <summary>
/// performs initialization of GUI variables; invokes
/// display_input_file
/// </summary>
/// <note>
/// display_file is invoked at the beginning of the display of
/// a file; thereafter display_input_file is invoked directly
/// </note>
void display_file ( )
    {

    new_value = start_at / MAXIMUM_ENTRIES_PER_LINE;

    textbox_VSB.Minimum = 0;
    textbox_VSB.Maximum = file_lines;
    textbox_VSB.Value = 0;
                        // set display_changed to true
                        // forcing display_input_file
                        // to fill the textbox
    display_changed = true;

    options_and_size_GB.Visible = true;

    display_input_file ( );

    } // display_file

When the file specific variables have been initialized, display_file invokes display_input_file.

display_changed must be set true before display_input_file is invoked. In this case, both new_value and textbox_VSB.Value are set to zero. Normally, in display_input_file, this situation would be interpreted as "no need to redraw." However, display_file is invoked when a newly opened file is to be displayed. Therefore it is necessary to force display_input_file to redraw the display. Other instances where display_changed must be set to true are when the options Hexadecimal or Use <SP> for Spaces change the display itself. In those cases no change to new_value or textbox_VSB.Value occurs. Unless display_changed is set true, the display will not reflect the user's wishes.

display_input_file is the workhorse of ViewFile.

C#
    :
    // ******************************************* class constants

    const int       MAXIMUM_BUFFER = MAXIMUM_LINES * 
                                     MAXIMUM_ENTRIES_PER_LINE;
    const int       MAXIMUM_ENTRIES_PER_LINE = 8;
    const int       MAXIMUM_LINES  = 20;
    :
    // ******************************************* class variables
    :
    FileStream  file_stream = null;
    :
    int         new_value = 0;
    VScrollBar  textbox_VSB = null;
    :
    // **************************************** display_input_file

    /// <summary>
    /// retrieves MAXIMUM_BUFFER bytes from the input file and 
    /// formats them into the text_box display
    /// </summary>
    /// <param name="text_box">
    /// TextBox into which file bytes will be formatted
    /// </param>
    void display_input_file ( )
        {
        byte [ ]        buffer = new byte [ MAXIMUM_BUFFER ];
        int             buffer_index = 0;
        int             byte_offset = 0;
        int             bytes_read = 0;
        StringBuilder   ch_buffer = new StringBuilder ( );
        StringBuilder   digits_buffer = new StringBuilder ( );
        int             lines_read = 0;
        int             remainder = 0;
        int             starting_byte = 0;
                                    // new_value units is lines
        new_value = Math.Max ( 
                        0,
                        Math.Min ( new_value,
                                   ( textbox_VSB.Maximum - 
                                     MAXIMUM_LINES ) ) );

        if ( display_changed )      // force redisplay
            {
            display_changed = false;
            }
        else if ( new_value == textbox_VSB.Value )
            {
            return;                 // nothing to do if both equal
            }
                                    // refill the screen
        contents_TB.Suspend ( );       // see ControlExtensions.cs
        contents_TB.Clear ( );
                                    // read a screen-full of data

                                    // byte_offset is the location 
                                    // of the first byte in the 
                                    // new_value line
        byte_offset = new_value * MAXIMUM_ENTRIES_PER_LINE;
        textbox_VSB.Value = new_value;

        read_data (     file_stream, 
                        byte_offset,
                        buffer,
                    ref bytes_read );
        lines_read = bytes_read / MAXIMUM_ENTRIES_PER_LINE;
                                    // if remainder is > 0, a 
                                    // partial line is at the end
                                    // of the buffer
        remainder = bytes_read % MAXIMUM_ENTRIES_PER_LINE;

// dd  dd  dd  dd  dd  dd  dd  dd      |         sb | chchchchchchchch
// ^------- digits_buffer ------^                     ^-- ch_buffer -^
//                                 starting_byte ^

                                    // clear the buffers
        ch_buffer.Length = 0;
        digits_buffer.Length = 0;
        buffer_index = 0;
        starting_byte = byte_offset;
        start_at_TB.Text = starting_byte.ToString ( );
                                    // process whole lines
        for ( int line = 0; ( line < lines_read ); line++ )
            {
            for ( int j = 0; 
                    ( j < MAXIMUM_ENTRIES_PER_LINE ); 
                      j++ )
                {
                insert_a_byte ( ref ch_buffer,
                                ref digits_buffer,
                                    buffer [ buffer_index++ ] );
                }
            complete_line ( ref ch_buffer,
                            ref digits_buffer,
                            ref starting_byte,
                            ref contents_TB );
            }
                                    // process the remainder
        if ( remainder > 0 )
            {
            int  empty_entries = 0;

            for ( int j = 0; ( j < remainder ); j++ )
                {
                insert_a_byte ( ref ch_buffer,
                                ref digits_buffer,
                                    buffer [ buffer_index++ ] );
                }

            empty_entries = MAXIMUM_ENTRIES_PER_LINE - 
                            remainder;
                                    // pad end of ch_buffer
            for ( int j = 0; ( j < empty_entries ); j++ )
                {
                ch_buffer.Append ( "    " );
                }
            complete_line ( ref ch_buffer,
                            ref digits_buffer,
                            ref starting_byte,
                            ref contents_TB );
            }

        contents_TB.Select( 0, 0 );    // contents_TB first byte
        contents_TB.ScrollToCaret ( ); 
        contents_TB.Resume ( );        // see ControlExtensions.cs
        contents_TB.Visible = true;
        textbox_VSB.Visible = true;

        options_and_size_GB.Visible = true;

        } // display_input_file

If the global variable display_changed is true, contents_TB will be redrawn, otherwise, the value of the global variable new_value controls the execution of display_input_file. Initially, its value is set to 0 and the value in display_changed is set to true. These values insure that display_input_file fills contents_TB with the first 160 bytes of the input file. Thereafter, new_value is modified in response to events.

Once display_input_file has retrieved the data from the file, it fills contents_TB. If the number of bytes read is not evenly divisible by the number of bytes in a contents_TB line (MAXIMUM_ENTRIES_PER_LINE). Filling contents_TB occurs in two steps: the first fills contents_TB with "whole" lines; the second fills contents_TB with the remaining bytes.

Note that the number of bytes read by display_input_file is always 160 or less (less if the end of file is encountered on filestream). Because ViewFile interacts with the user, there is no advantage to performing any kind of optimizations. Any performance gained would be offset by ViewFile's user interface.

When display_input_file completes execution, ViewFile goes idle until a keyboard key is pressed, the mouse wheel is rotated, or until the contents_TB vertical scroll bar is modified.

5.1.3. TIMER_Tick Event Handler Table of Contents

Prior to discussing the keyboard event handlers, I address the TIMER_Tick event handler because it is indirectly invoked by the TB_KeyDown event handler and its actions are modified by the TB_KeyUp event handler.

TIMER_Tick is relatively simple. It is invoked when timer.Enabled is set true and the timer.Interval expires.

C#
:
// ******************************************* class constants
:
const int       TIMER_REPEAT_DELAY = 400;
:
// ******************************************* class variables
:
bool        key_down = false;
:
int         lines_to_scroll = 0;
:
int         new_value = 0;
:
    // ************************************************ TIMER_Tick

    void TIMER_Tick ( object    sender,
                      EventArgs e )
        {

        timer.Interval = TIMER_REPEAT_DELAY;
        if ( key_down )
            {
            if ( lines_to_scroll != 0 )
                {
                new_value = textbox_VSB.Value + lines_to_scroll;
                display_input_file ( );
                }
            }

        } // TIMER_Tick

Its first action is to reset the timer.Interval to the value of TIMER_REPEAT_DELAY. I believe that 400 milliseconds are sufficient to provide a reasonable repeat rate. Two additional conditions must be met before TIMER_Tick continues its execution: key_down must be true and lines_to_scroll must be non-zero. If these conditions are met, new_value is revised and display_input_file is invoked.

5.1.4. TB_KeyDown and TB_KeyUp Event Handlers Table of Contents

Of these two event handlers, TB_KeyUp is by far the simplest.

C#
// ************************************************** TB_KeyUp

void TB_KeyUp ( object       sender,
                KeyEventArgs e )
    {

    key_down = false;
    timer.Enabled = false;
    }

When a key is released, TB_KeyUp is invoked. Its purpose is to cancel any further repeat actions by TIMER_Tick. By setting the two conditions required for TIMER_Tick to execute (i.e., timer.Enabled and key_down) to false, TIMER_Tick stops executing. In turn, further revisions to contents_TB are halted.

Although TB_KeyUp is more complex, its logic is relatively simple. Its purpose is to determine if a keyboard key of interest has been pressed and, if so, dispatch the appropriate action.

C#
// ************************************************ TB_KeyDown

void TB_KeyDown ( object       sender,
                  KeyEventArgs e )
    {
    Keys    key = e.KeyCode;
    bool    trigger_timer = true;
                                // compute lines_to_scroll
    if ( key == Keys.Down )
        {
        lines_to_scroll = textbox_VSB.SmallChange;
        }
    else if ( key == Keys.Up )
        {
        lines_to_scroll = -textbox_VSB.SmallChange;
        }
    else if ( key == Keys.PageDown )
        {
        lines_to_scroll = textbox_VSB.LargeChange;
        }
    else if ( key == Keys.PageUp )
        {
        lines_to_scroll = -textbox_VSB.LargeChange;
        }
    else if ( key == Keys.Home )
        {
        trigger_timer = false;
        lines_to_scroll = int.MinValue;
        }
    else if ( key == Keys.End )
        {
        trigger_timer = false;
        lines_to_scroll = int.MaxValue;
        }
    else
        {
        lines_to_scroll = 0;
        }
                                // either start timer or
                                // directly dispatch refill
    if ( lines_to_scroll != 0 )
        {
        new_value = textbox_VSB.Value + lines_to_scroll;
        if ( trigger_timer )
            {
            timer.Interval = 1; // cannot be zero
            key_down = true;
            timer.Enabled = true;
            }
        else
            {
            display_input_file ( );
            }
        }

    } // TB_KeyDown

The first task of TB_KeyDown is to determine what key was pressed and set lines_to_scroll to its appropriate value. With the exception of the Home and End keys, the values assigned to lines_to_scroll are those associated with the equivalent contents_TB's vertical scroll bar controls.

For the Home and End keys, lines_to_scroll is set to int.MinValue [^] and int.MaxValue [^] respectively. Setting lines_to_scroll to these values will insure that new_value will be assigned a value outside of the permissible range of the contents_TB vertical scroll bar.

Note that lines_to_scroll will be zero if the key pressed is not one of those in which we are interested; thus causing TB_KeyDown to return, taking no further action. Otherwise, TB_KeyDown will compute new_value. If a key other than Home or End was recognized (i.e., trigger_timer is true), the timer.Interval is set to one and both key_down and timer.Enabled are set true. Setting timer.Interval to one causes TIMER_Tick to execute almost immediately.

For the Home and End keys, once new_value is computed, display_input_file can be invoked.

5.1.5. TB_MouseWheel Event Handler Table of Contents

The MouseWheel Event [^] is raised when the mouse wheel moves. This event is handled by the TB_MouseWheel event handler.

C#
// ********************************************* TB_MouseWheel

void TB_MouseWheel ( object         sender,
                     MouseEventArgs e )
    {
    int lines_to_move = 0;

    lines_to_move =
        ( e.Delta * mouse_wheel_scroll_lines ) /
        DELTA_UNITS_OF_WHEEL_MOVEMENT;
    new_value = lines_to_move + textbox_VSB.Value;
    display_input_file ( );

    } // TB_MouseWheel

e.Delta contains a signed count of the number of detents the mouse wheel has rotated. A detent is one notch of the mouse wheel.

From MSDN:

The mouse wheel combines the features of a wheel and a mouse button. The wheel has discrete, evenly spaced notches. When you rotate the wheel, a wheel message is sent as each notch is encountered. One wheel notch, a detent, is defined by the windows constant WHEEL_DELTA [which in turn is found in SystemInformation.MouseWheelScrollLines], which is 120. A positive value indicates that the wheel was rotated forward, away from the user; a negative value indicates that the wheel was rotated backward, toward the user.

Currently, a value of 120 is the standard for one detent. If higher resolution mice are introduced, the definition of WHEEL_DATA might become smaller. Most applications should check for a positive or negative value rather than an aggregate total.

Because TB_MouseWheel is invoked each time that the mouse wheel moves, the computation of lines_to_move is straight-forward. The global constant DELTA_UNITS_OF_WHEEL_MOVEMENT is defined as 120, thus taking into effect the value of WHEEL_DELTA.

lines_to_move will be positive or negative, depending upon the direction in which the mouse wheel was moved. From that value, new_value can be computed and display_input_file invoked.

5.1.6. CHKBX_CheckedChanged Event Handler Table of Contents

All change events for the application's CheckBoxes are routed through the CHKBX_CheckedChanged event handler.

C#
:
// ******************************************* class variables
:
bool        display_changed = true;
:
bool        hexadecimal = true;
:
bool        keep_on_top = false;
:
bool        use_SP = false;
:
// ************************************** CHKBX_CheckedChanged

void CHKBX_CheckedChanged ( object    sender,
                            EventArgs e )
    {
    CheckBox  check_box = ( CheckBox ) sender;
    bool      is_checked = check_box.Checked;
    string    name = check_box.Name.ToString ( );

    switch ( name )
        {
        case "hexadecimal_CHKBX":
            hexadecimal = is_checked;
            display_changed = true;
            display_input_file ( );
            break;
                                // display does not change,
                                // it is just placed on top
        case "keep_on_top_CHKBX":
            keep_on_top = is_checked;
            this.TopMost = keep_on_top;
            break;

        case "use_SP_for_spaces_CHKBX":
            use_SP = is_checked;
            display_changed = true;
            display_input_file ( );
            break;

        default:
            throw new ApplicationException (
                String.Format (
                    "{0} is not a recognized CheckBox name",
                    name ) );
        }

    } // CHKBX_CheckedChanged

CheckBoxes [^] provide the user with the ability to change the way in which ViewFile displays its data. Hexadecimal and Use <SP> for Spaces change the display itself; Keep on Top causes ViewFile to be displayed as the topmost form on the desktop. All CheckBoxes are toggles: they can be checked to activate the display option or cleared to deactivate the display option.

display_changed is set when the change in an option requires that the ViewFile display be redrawn.

5.1.7. numeric_TB_KeyPress Event Handler Table of Contents

When the start_at_TB TextBox was added, the requirement to insure that only numeric values were entered was also added. The numeric_TB_KeyPress event handler met the requirement.

// *************************************** numeric_TB_KeyPress

// Matt Hamilton response in
// https://stackoverflow.com/questions/463299/
//     how-do-i-make-a-textbox-that-only-accepts-numbers

private void numeric_TB_KeyPress ( object            sender,
                                   KeyPressEventArgs e )
    {

    if ( !( char.IsControl ( e.KeyChar ) ||
            char.IsDigit ( e.KeyChar ) ) )
        {
        e.Handled = true;
        }

    } // numeric_TB_KeyPress

When a key is pressed while the start_at_TB TextBox has focus, the handler is triggered. Setting e.Handled to true informs the operating system that the event was handled. What is accepted are the numeric digits and the control keys. By accepting control keys the contents of the start_at_TB TextBox may be edited.

5.2. Initialize application global variables Table of Contents

C#
:
// ******************************************* class variables
:
bool        hexadecimal = true;
string      input_file_name = String.Empty;
bool        keep_on_top = false;
string      last_initial_directory_filename = String.Empty;
:
bool        use_SP = false;
string      user_viewfile_directory = String.Empty;
:
// ******************************* initialize_global_variables

void initialize_global_variables ( TextBox  text_box )
    {

    determine_textbox_geometry ( text_box );

    hexadecimal = true;
    input_filename = String.Empty;
    keep_on_top = false;
    start_at = 0;
    use_SP = false;

    user_viewfile_directory =
        ( String.Format (
              "{0}/{1}",
              Environment.GetEnvironmentVariable (
                              "USERPROFILE" ),
              Application.ProductName.
                              Replace ( " ", "_" ) ) ).
                              Replace ( @"\", "/" );
    if ( !Directory.Exists ( user_viewfile_directory ) )
        {
        Directory.CreateDirectory ( user_viewfile_directory );
        }
    last_initial_directory_filename =
        String.Format ( "{0}/last_directory.dat",
                        user_viewfile_directory );

    initialize_tooltips ( );

    } // initialize_global_variables

initialize_global_variables determines the contents_TB geometry so that the maximum_textbox_lines is available to the application.Then it sets global variables to their initial values. Once set, they may be changed by the user through the user interface. initialize_global_variables also establishes the last_initial_directory_filename that will be used when the Browse button is clicked. Lastly, initialize_global_variables sets a ToolTip [^] on each user accessible control.

5.2.1. determine_textbox_geometry Table of Contents

C#
:
// ******************************************* class variables
:
int         maximum_textbox_lines = 0;
:
// ******************************** determine_textbox_geometry

void determine_textbox_geometry ( TextBox text_box )
    {
    int   character_height = 0;
    Font  font = text_box.Font;
    Size  proposed_size = new Size ( int.MaxValue,
                                     int.MaxValue );
                                // for each printing character
                                // determine its size and
                                // revise character_height
    for ( int i = SPACE; ( i <= TILDE ); i++ )
        {
        char    ch = Convert.ToChar ( i );
        Size    size;
        string  str = ch.ToString ( );

        size = TextRenderer.MeasureText (
                                    str,
                                    font,
                                    proposed_size,
                                    TextFormatFlags.Default );
        if ( size.Height > character_height )
            {
            character_height = size.Height;
            }
        }

    maximum_textbox_lines = text_box.Size.Height /
                            character_height;

    } // determine_textbox_geometry

determine_textbox_geometry determines the maximum_textbox_lines of the contents_TB TextBox. It uses TextRenderer.MeasureText [^] to obtain the largest character_height of the printable characters. character_height (in pixels) is then divided into the Height of the contents_TB TextBox. Note that this is integer division, so the quotient is truncated.

5.3. signon Table of Contents

C#
// **************************************************** signon

void signon ( )
    {
    string [ ]  pieces;

    pieces = this.ProductVersion.Split (
                  new char [ ] { '.' },
                  StringSplitOptions.RemoveEmptyEntries );

    this.Text = String.Format ( "{0} V{1}.{2}",
                                this.ProductName,
                                pieces [ 0 ],
                                pieces [ 1 ] );
    this.Icon = Properties.Resources.ViewFileIcon;
    this.StartPosition = FormStartPosition.CenterScreen;
    }

The values of ProductName and ProductVersion are defined in AssemblyInfo.cs

5.4. Initialize the GUI Table of Contents

C#
:
// ******************************************* class variables
:
VScrollBar  textbox_VSB = null;
:
// ******************************************** initialize_GUI

void initialize_GUI ( TextBox  text_box )
    {

    if ( textbox_VSB != null )
        {
        text_box.Controls.Remove ( textbox_VSB );
        textbox_VSB.Dispose ( );
        textbox_VSB = null;
        }
    textbox_VSB = initialize_VScrollBar ( text_box );

    text_box.Controls.Add ( textbox_VSB );
    textbox_VSB.Visible = false;

    text_box.Visible = false;

    browse_BUT.Visible = true;

    input_file_LAB.Visible = true;
    input_file_TB.ForeColor = Color.Gray;
    input_file_TB.Text = BROWSE_PROMPT;
    input_file_TB.Visible = true;

    file_size_LAB.Visible = true;
    file_size_TB.Visible = true;
    file_bytes = 0;
    file_size_TB.Text = file_bytes.ToString ( );
    start_at_LAB.Visible = true;
    start_at_TB.Visible = true;
    start_at = 0;
    start_at_TB.Text = start_at.ToString ( );
    go_BUT.Visible = true;
    start_at_and_go_GB.Visible = false;

    hexadecimal_CHKBX.Checked = hexadecimal;
    hexadecimal_CHKBX.Visible = true;
    keep_on_top_CHKBX.Checked = keep_on_top;
    keep_on_top_CHKBX.Visible = true;
    use_SP_for_spaces_CHKBX.Checked = use_SP;
    use_SP_for_spaces_CHKBX.Visible = true;
    options_and_size_GB.Visible = false;

    exit_BUT.Visible = true;

    } // initialize_GUI

Up until now, with the exception of one, all GUI elements have been defined. This last control is the vertical scroll bar (textbox_VSB) inside the contents_TB.

5.4.1. Initialize the Vertical Scroll Bar Table of Contents

A Vertical ScrollBar [^] consists of a shaded shaft with an arrow button at each end and a scroll box (sometimes called a thumb) between the arrow buttons.

Vertical Scroll Bar

Minimum specifies the scrollbar value at the top of the scrollbar
Clicking the Line up arrow moves the thumb up the number of lines specified in the SmallChange property (defaults to 1)
Clicking in the Page up area moves the thumb up the number of lines specified in the LargeChange property (defaults to 10)
Thumb is the current position (at the property Value)
Clicking in the Page down area moves the thumb down the number of lines specified in the LargeChange property (defaults to 10)
Clicking the Line down arrow moves the thumb down the number of lines specified in the SmallChange property (defaults to 1)
Maximum specifies the scrollbar value at the bottom of the scrollbar

 

At run-time, the vertical scrollbar is placed on the right side of the contents_TB. This allows naming the scrollbar as well as specifying some of its properties. The vertical scrollbar is created by initialize_VScrollBar.

C#
// ************************************* initialize_VScrollBar

VScrollBar initialize_VScrollBar ( TextBox  text_box )
    {


    VScrollBar vsb = new VScrollBar ( )
                         {
                         Cursor = Cursors.Arrow,
                         LargeChange =
                             ( maximum_textbox_lines / 2 ),
                         Location = new Point (
                             ( text_box.Width - VSB_WIDTH ),
                             0 ),
                         Maximum = int.MaxValue,
                         Minimum = 0,
                         Name = "textbox_VSB",
                         Size = new Size (
                             VSB_WIDTH,
                             ( text_box.Height - 3 ) ),
                         SmallChange = 1,
                         Value = int.MaxValue
                         };

    vsb.Scroll += new ScrollEventHandler ( TB_VSB_Scroll );

    return ( vsb );

    } // initialize_VScrollBar

With textbox_VSB defined, all we need is to be notified of changes to the Vertical Scroll Bar controls (Line up or down, Page up or down). The event handler that provides these notifications is TB_VSB_Scroll.

5.4.2. TB_VSB_Scroll Event Handler Table of Contents

C#
// ********************************************* TB_VSB_Scroll

void TB_VSB_Scroll ( Object          sender,
                     ScrollEventArgs e )
    {

    new_value = e.NewValue;
    display_input_file ( );

    } // TB_VSB_Scroll

6. Conclusion Table of Contents

This article has presented the revision to ViewFile, a tool that provides a byte-oriented display of file contents. The following figure presents an overview.

Viewfile Overview

 

Although ViewFile is a useful tool, this article has attempted to illustrate how a number of event handlers can work together to provide a good user experience.

7. References Table of Contents

8. Development Environment Table of Contents

The software presented in this article was developed in the following environment:

Microsoft Windows 7 Professional Service Pack 1
Microsoft Visual Studio 2008 Professional
Microsoft .Net Framework Version 3.5 SP1
Microsoft Visual C# 2008

9. History Table of Contents

ViewFile V4.0 12/16/2022 Revised Tool and Deleted Head
ViewFile V3.1 08/11/2017 Revised Article and Software
ViewFile V1.1 and Head V1.2 06/23/2014 Original Article

License

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


Written By
Software Developer (Senior)
United States United States
In 1964, I was in the US Coast Guard when I wrote my first program. It was written in RPG (note no suffixing numbers). Programs and data were entered using punched cards. Turnaround was about 3 hours. So much for the "good old days!"

In 1970, when assigned to Washington DC, I started my MS in Mechanical Engineering. I specialized in Transportation. Untold hours in statistical theory and practice were required, forcing me to use the university computer and learn the FORTRAN language, still using punched cards!

In 1973, I was employed by the Norfolk VA Police Department as a crime analyst for the High Intensity Target program. There, I was still using punched cards!

In 1973, I joined Computer Sciences Corporation (CSC). There, for the first time, I was introduced to a terminal with the ability to edit, compile, link, and test my programs on-line. CSC also gave me the opportunity to discuss technical issues with some of the brightest minds I've encountered during my career.

In 1975, I moved to San Diego to head up an IR&D project, BIODAB. I returned to school (UCSD) and took up Software Engineering at the graduate level. After BIODAB, I headed up a team that fixed a stalled project. I then headed up one of the two most satisfying projects of my career, the Automated Flight Operations Center at Ft. Irwin, CA.

I left Anteon Corporation (the successor to CSC on a major contract) and moved to Pensacola, FL. For a small company I built their firewall, given free to the company's customers. An opportunity to build an air traffic controller trainer arose. This was the other most satisfying project of my career.

Today, I consider myself capable.

Comments and Discussions

 
GeneralMy vote of 5 Pin
BobHoffman29-Dec-22 10:00
BobHoffman29-Dec-22 10:00 
GeneralRe: My vote of 5 Pin
gggustafson29-Dec-22 17:18
mvagggustafson29-Dec-22 17:18 
GeneralMy vote of 5 Pin
Emre Ataseven23-Jun-14 22:40
professionalEmre Ataseven23-Jun-14 22:40 
GeneralRe: My vote of 5 Pin
gggustafson24-Jun-14 4:49
mvagggustafson24-Jun-14 4:49 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.