§ May 30, 2005

A Simple Line Control

Some time ago, I was working on a clone of Solitaire (the one that comes with windows), and I needed a line control (for... surprise surprise... the about box) like the one shown in its about window:

image of the about window in solitaire


after a brisk 2 seconds I came up with the following class:
/******************************************************/
/*          NULLFX FREE SOFTWARE LICENSE              */
/******************************************************/
/*  Line Control                                      */
/*  by: Steve Whitley                                 */
/*  © 2005 NullFX Software                            */
/*                                                    */
/* NULLFX SOFTWARE DISCLAIMS ALL WARRANTIES,          */
/* RESPONSIBILITIES, AND LIABILITIES ASSOCIATED WITH  */
/* USE OF THIS CODE IN ANY WAY, SHAPE, OR FORM        */
/* REGARDLESS HOW IMPLICIT, EXPLICIT, OR OBSCURE IT   */
/* IS. IF THERE IS ANYTHING QUESTIONABLE WITH REGARDS */
/* TO THIS SOFTWARE BREAKING AND YOU GAIN A LOSS OF   */
/* ANY NATURE, WE ARE NOT THE RESPONSIBLE PARTY. USE  */
/* OF THIS SOFTWARE CREATES ACCEPTANCE OF THESE TERMS */
/*                                                    */
/* USE OF THIS CODE MUST RETAIN ALL COPYRIGHT NOTICES */
/* AND LICENSES (MEANING THIS TEXT).                  */
/*                                                    */
/******************************************************/

namespace NullFX.Controls {
    using System;
    using System.Collections;
    using System.ComponentModel;
    using System.Drawing;
    using System.Data;
    using System.Windows.Forms;
    using System.Windows.Forms.Design;
    [Designer(typeof(Line.LineDesigner))]
    public class Line : Control {
        internal class LineDesigner : ControlDesigner {
            public override SelectionRules SelectionRules {
                get {
                    return SelectionRules.LeftSizeable |
                        SelectionRules.RightSizeable |
                        SelectionRules.Visible |
                        SelectionRules.Moveable;
                }
            }

        }
        private FlatStyle _flatStyle;
        private Pen _systemPen;
        private Container components = null;
        [Category("Appearance"), 
        Description(
        "Determines the display of the control" +
        "when the users move the mouse over the control" +
        "and click")]
        public FlatStyle FlatStyle {
            get{return _flatStyle;}
            set{
                _flatStyle = value;
                Invalidate();
            }
        }
        public Line() {
            _flatStyle = FlatStyle.Standard;
            _systemPen = 
                  new Pen(SystemColors.ControlDark, 2);
            SetStyle(ControlStyles.AllPaintingInWmPaint |
                ControlStyles.DoubleBuffer|
                ControlStyles.ResizeRedraw|
                ControlStyles.UserPaint, true);
        }		 
        protected override void Dispose( bool disposing ) {
            if( disposing ) {
                if( components != null ) {
                    components.Dispose();
                }
            }
            base.Dispose( disposing );
        }
        protected override Size DefaultSize {
            get {
                return new System.Drawing.Size(120, 2);
            }
        } 
        protected override void OnPaint(PaintEventArgs pe) {
            switch(_flatStyle) { 
                case FlatStyle.Flat:
                case FlatStyle.Popup:
                case FlatStyle.Standard: {
                    pe.Graphics.FillRectangle(
                       SystemBrushes.ControlLightLight, 
                       0, 0, Width, 2);
                    pe.Graphics.FillRectangle(
                       SystemBrushes.ControlDark, 
                       0, 0, Width - 1, Height / 2); 
                }break;
                case System.Windows.Forms.FlatStyle.System: {
                    pe.Graphics.DrawLine(_systemPen, 
                                             0, 1, Width, 1);
                }break;
                default:
                    goto case FlatStyle.Standard; 
            }
        }
    }
}


Now to talk over a few points in this very simple control.

There were 2 requirements I had for the control:
  1. It had to have an inherent size when I dragged it onto the canvass
  2. it had to keep its aspect ratio (meaning I couldn't make it look inconsistent)
I was able to accomplish this using a class designer, and by overriding the DefaultSize property of the control. I could have gotten much more fancy with the control, but since I was going to be the only one using it (well... that was the plan anyway :D), I kept it super simple.

protected override Size DefaultSize {
    get {
        return new System.Drawing.Size(120, 2);
    }
}


This allows the control to default to this size. Anytime the control needs its base size, it uses the DefaultSize property. My first iteration of this control acutally set the size property in the InitializeComponent method, but after reading up on it, using the DefaultSize property and specifying its size is the better solution.

The next thing was to keep its aspect ratio. This is achieved by providing my control with a designer. There are lots of articles out there on how to implement a designer, so I'll leave that for another topic, and just discuss what I used in the designer to acomplish this.
There is a property in the ControlDesigner called SelectionRules. This tells the control host at design time, how it can be used. Here's our designer class. It's actually an inner class of the Line control.
internal class LineDesigner : ControlDesigner {
	public override SelectionRules SelectionRules {
		get {
			return SelectionRules.LeftSizeable |
				SelectionRules.RightSizeable |
				SelectionRules.Visible |
				SelectionRules.Moveable;
		}
	}

}

Notice we're only overriding the SelectionRules property. It returns a bit flag value of which types of selection are possible in the designer. I didn't want the control to be sized up or down, so I left those selection rules out. I did want it to be able to be sized right or left, and movable (and obvisously visible), so these are the rules I return.

Other than this, all we need is to draw the line. This is achieved by overriding the OnPaint function in the control. I have provided functionality (however ugly it may be) for flat style, but for simplicity, I'll speak to the normal line.
The line in the about box has a highlight line and a shadow line. In our OnPaint method, I draw both lines using Control.Dark and Control.LightLight colors (this is so these colors can match whatever theme is being used).
case FlatStyle.Standard: {
	pe.Graphics.FillRectangle(
	   SystemBrushes.ControlLightLight, 
	   0, 0, Width, 2);
	pe.Graphics.FillRectangle(
	   SystemBrushes.ControlDark, 
	   0, 0, Width - 1, Height / 2); 
}break;


Anyway, this is my simple line control.

download the dll binary here.
Posted 1 week, 6 days ago on May 30, 2005
Re: A Simple Line Control      [ posted December 8, 2005, 11:48 am ]
Author Comment
Skuss
Did you know that you can use the label control included with VS as a line control? All you need to do is set the border style to Fixed or 3D either one, and set the height property to 1. This converts the control into a line control. Depending on which border style you use you'll get a different line control.
[ Reply in the Forums ]
Re: A Simple Line Control      [ posted December 8, 2005, 2:25 pm ]
Author Comment
Steve
[ Email     ]
[ Website ]
yea, that was the trick I had read about prior to writing this. I wanted something that wasn independently a line control though. this control was a poor excuse for me to look through some of the control designer interfaces...



gonna have to do something similar in 2.0 pretty soon as well.
[ Reply in the Forums ]

 Comments can be posted in the forums.

© 2003 - 2024 NullFX
Creative Commons Attribution-NonCommercial-ShareAlike 3.0 License