Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / multimedia / GDI+

Gradient Color Picker V2

3.57/5 (4 votes)
28 Mar 2021CPOL2 min read 7K   214  
Revises an earlier implementation of the Gradient Color Picker
Gradient Color Picker V2 revises the earlier implementation by revising the user interface and eliminating a need to determine the color of a pixel at a specific screen location.

Table of Contents

Introduction Table of Contents

This article revises an earlier implementation of the Gradient Color Picker [^]. The incentive for the revision were reader comments regarding the earlier implementation.

gradient_color_picker

The only areas of change were in the positioning of the user interface components and the implementation of the gradient_PAN and the button_PAN panels. The original article should be referenced for details.

Implementation Table of Contents

gradient_PAN Table of Contents

When either the start or end color is chosen, the fill_gradient_PAN method is invoked.

C#
// ***************************************** fill_gradient_PAN

bool fill_gradient_PAN ( )
    {

    if ( have_end_color && have_start_color )
        {

        gradient_PAN.Visible = true;
        gradient_PAN.Invalidate ( );


        number_of_colors_LAB.Visible = true;
        number_of_colors_NUD.Value = number_of_colors;
        number_of_colors_NUD.Visible = true;

        generate_BUT.Visible = true;
        }

    return ( true );

    } // fill_gradient_PAN

This method insures that both the start and end colors have been chosen. If so, the method executes the following:

C#
gradient_PAN.Visible = true;
gradient_PAN.Invalidate ( );

The gradient_PAN has the PAN_OnPaint event handler attached.

C#
// *********************************************** PAN_OnPaint

void PAN_OnPaint ( object         sender,
                   PaintEventArgs e )
    {

    base.OnPaint ( e );

    e.Graphics.FillRectangle (
                            new LinearGradientBrush (
                                gradient_PAN.ClientRectangle,
                                start_color,
                                end_color,
                                0.0F ),
                            gradient_PAN.ClientRectangle );

    } // PAN_OnPaint

The PAN_OnPaint event hander is very simple. All it does is create a LinearGradientBrush [^] and uses it to fill the gradient_PAN client rectangle.

In this revision the left side of the gradient_PAN is aligned horizontally with the horizontal center of the start_color_BUT and the right side of the gradient_PAN is aligned horizontally with the horizontal center of the end_color_BUT. The number of colors label and the numeric up/down control have been moved to midway between the start- and end-color buttons.

gradient_color_picker

button_PAN Table of Contents

When the Generate button is clicked, the balance of the tool's GUI is rendered. In most cases this entails making various objects visible. However, generating the button_PAN panel is somewhat more complex.

C#
// ******************************************* fill_button_PAN

bool fill_button_PAN ( )
    {
    int     right_most = 0;
    int     spacing = 0;
    int     top = 2;
                                // empty and regenerate the
                                // colors list
    colors.Clear ( );
    colors = get_linear_gradient_colors ( start_color,
                                          end_color,
                                          number_of_colors );
                                // remove existing event
                                // handlers from buttons in
                                // the button_PAN
    foreach ( Control control in button_PAN.Controls )
        {
        if ( control is Button )
            {
            control.Click -= new EventHandler (
                                        gradient_BUT_Click );
            }
        }
                                // remove any existing buttons
                                // from the button_PAN
    button_PAN.Controls.Clear ( );
                                // clear the buttons list
    buttons.Clear ( );
                                // compute initial spacing
                                // between buttons
    spacing = ( button_PAN.Size.Width -
                ( BUTTON_WIDTH * number_of_colors ) ) /
              ( number_of_colors - 1 );
                                // create gradient buttons and
                                // add them to buttons list
    for ( int i = 0; ( i < number_of_colors ); i++ )
        {
        Button  button = new Button ( );
        int     left = ( i * ( spacing + BUTTON_WIDTH ) );
                                // want no borders
        button.FlatStyle = FlatStyle.Popup;
        button.Location = new Point ( left, top );
        button.Size = BUTTON_SIZE;
        button.Click += new EventHandler (
                                        gradient_BUT_Click );
                                // save the position of the
                                // right side of the button
        right_most = button.Location.X + button.Size.Width;

        button.BackColor = colors [ i ];
        button.UseVisualStyleBackColor = false;

        buttons.Add ( button );
        }
                                // the spacing may not be
                                // large enough to cause the
                                // buttons to completely fill
                                // the button panel; here we
                                // correct the inter-button
                                // spacing; EPSILON is
                                // currently 3
    if ( right_most < ( button_PAN.Size.Width - EPSILON ) )
        {
        int pixels = 1;
        int start = 0;
                                // start is expected to be
                                // greater than zero
        start = buttons.Count -
                ( button_PAN.Size.Width - right_most );

        for ( int i = start; ( i < buttons.Count ); i++ )
            {
            Point location = buttons [ i ].Location;

            location.X += pixels++;
            buttons [ i ].Location = location;
            }
        }
                                // copy the button from the
                                // buttons List to the
                                // button_PAN
    for ( int i = 0; ( i < buttons.Count ); i++ )
        {
        Button  button = buttons [ i ];
                                // place button in button_PAN
        button_PAN.Controls.Add ( button );
        }

    button_PAN.Visible = true;

    reset_BUT.Visible = true;

    copy_format_GB.Visible = true;
    ascending_PB.Visible = true;
    copy_left_to_right_BUT.Visible = true;
    descending_PB.Visible = true;
    copy_right_to_left_BUT.Visible = true;

    return ( true );

    } // fill_button_PAN

The major change in fill_button_PAN is the elimination of the dependency on the contents of the gradient_PAN to determine the background color of the buttons in the button_PAN. To achieve this, the get_linear_gradient_colors method is invoked to fill a list of colors whose members will be assigned to each button as its background color.

C#
// ******************************** get_linear_gradient_colors

// See https://www.codeproject.com/Articles/5267129/
//   Gradient-Color-Picker Bill Woodward comment modified

List < Color > get_linear_gradient_colors (
                   Color   start_color,
                   Color   end_color,
                   int     number_of_colors )
    {
    List < Color > list = new List < Color > ( 0 );

    float          count = ( float ) number_of_colors - 1.0F;

    float          start_R = ( float ) start_color.R;
    float          difference_R = ( start_R -
                                    ( float ) end_color.R ) /
                                    count;

    float          start_G = ( float ) start_color.G;
    float          difference_G = ( start_G -
                                    ( float ) end_color.G ) /
                                    count;

    float          start_B = ( float ) start_color.B;
    float          difference_B = ( start_B -
                                    ( float ) end_color.B ) /
                                    count;

    for ( int i = 0; ( i < number_of_colors ); i++ )
        {
        int B = MinMax ( start_B, difference_B, ( float ) i );
        int G = MinMax ( start_G, difference_G, ( float ) i );
        int R = MinMax ( start_R, difference_R, ( float ) i );

        list.Add ( Color.FromArgb ( R, G, B ) );

        }

    return ( list );

    } // get_linear_gradient_colors

The method MinMax was introduced to avoid repeating the same logic over and over again.

C#
// **************************************************** MinMax

int MinMax ( float start,
             float difference,
             float i )
    {
    int result = ( int ) ( start - ( i * difference ) + 0.5F );

    return ( Math.Max ( 0, Math.Min ( 255, result ) ) );

    } // MinMax

In this revision, the colors list and get_linear_gradient_colors replace generate_back_color.

Acknowlegments Table of Contents

The authors of the readers comments are:

  • steve-redTrans
  • BillWoodruff

I thank both for their comments that caused this article to be written.

References Table of Contents

Conclusion Table of Contents

This article has revised a tool that provides developers with the ability to pick colors from a linear color gradient.

Development Environment Table of Contents

The Gradient Color Picker was developed in the following environment:

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

History Table of Contents

05/05/2020 Original article
05/28/2021 Revised article

License

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