Extend the Button class

Jump to: navigation, search

Although this is not part of the exam, this section shows what can be achieved by extending the Button class. The following image displays an application that has four shaped buttons within it.


Image:simon_game.jpg


To demonstrate how this was achieved, follow the steps below:


  1. Add a new UserControl to your application.

  2. View the code and change the inherited type from UserControl to Button.
    public partial class simonButton : Button
    {
      public simonButton()
      {
        InitializeComponent();
      }
    }
  3. The shape of the button is achieved through the use of two GraphicsPath objects. Declare these in the top of the button class.
    public partial class simonButton : Button
    {
      private GraphicsPath path;
      private GraphicsPath innerPath;
     
      ...
    The path denotes the outline of the button, while the innerPath denotes the shape of the flat surface of the button.

  4. Now override the OnPaint method of the button, to take care of the drawing, so that is looks like the following code.
    protected override void OnPaint(PaintEventArgs pevent)
    {
      Graphics g = pevent.Graphics;
      g.SmoothingMode = SmoothingMode.AntiAlias;
    }
    So that we do not get jagged curves, set the SmoothingMode to AntiAlias.

  5. The path object is filled with a LinearGradientBrush. This is simple to achieve by supplying a rectangle and a new LinearGradientBrush.
    // Create Rectangle To Limit brush area.
    Rectangle rect = new Rectangle(0, 0, 150, 150);
          
    LinearGradientBrush linearBrush =
      new LinearGradientBrush(rect,
      Color.FromArgb(40,40,40),
      this.ForeColor,
      225);

    Next we instantiate the path object.

    path = new GraphicsPath();

    Add items to the path object to create the required shape

    path.AddArc(0, 0, 270, 270, 180, 90);
    path.AddArc(120, 0, 30, 30, 270, 90);
    path.AddLine(150, 0, 150, 85);
    path.AddArc(100, 100, 100, 100, -90, -90);
    path.AddLine(100, 150, 0, 150);
    path.AddArc(0, 120, 30, 30, 90, 90);
    path.AddArc(0, 0, 270, 270, 180, 90);

    Fill the path

    g.FillPath(linearBrush, path);

    Finally, dispose of the brush to free memory

    linearBrush.Dispose();
  6. The innerPath object is filled with a SolidBrush.
    Brush b = new SolidBrush(this.ForeColor);

    Next we instantiate the innerPath object.

    innerPath = new GraphicsPath();

    Add items to the innerPath object to create the required shape

    innerPath.AddArc(10, 10, 250, 250, 180, 90);
    innerPath.AddArc(130, 10, 10, 10, 270, 90);
    innerPath.AddLine(140, 0, 140, 90);
    innerPath.AddArc(90, 90, 100, 100, -90, -90);
    innerPath.AddLine(90, 140, 10, 140);
    innerPath.AddArc(10, 130, 10, 10, 90, 90);

    Fill the innerPath

    g.FillPath(b, innerPath);

    Finally, dispose of the brush to free memory

    b.Dispose();
  7. So far, this draws our shaped button, but you can click anywhere in the associated rectangle, so to stop this, simply set the Region property of the button to path.
    this.Region = new Region(path);
    The button will now only accept clicks within the region of the shape.

  8. So that the user has some feedback that they are over a button, changing the cursor is a simple thing to do. Override the OnMouseEnter and OnMouseLeave methods and change the cursor within them:
    protected override void OnMouseEnter(EventArgs e)
    {
      this.Cursor = Cursors.Hand;
      base.OnMouseEnter(e);
    }
     
    protected override void OnMouseLeave(EventArgs e)
    {
      this.Cursor = Cursors.Arrow;
      base.OnMouseLeave(e);
    }
  9. The button also needs visual feedback that it has been pressed. In this case, the button wants to appear as if a light has come on behind it. This requires several new items and some changes to the code. First off, a boolean value is required to store if the click has happened so that it can be drawn in the correct state.
    private bool _clicked = false;
    public bool Clicked
    {
      get { return _clicked; }
      set
      {
        _clicked = value;
        Invalidate();
      }
    }

    Now override the OnMouseDown and OnMouseUp to set the Clicked state accordingly.

    protected override void OnMouseDown(MouseEventArgs mevent)
    {
      _clicked = true;
      base.OnMouseDown(mevent);
    }
     
    protected override void OnMouseUp(MouseEventArgs mevent)
    {
      _clicked = false;
      base.OnMouseUp(mevent);
    }

    Add a new brush.

    PathGradientBrush pgbrush = new PathGradientBrush(innerPath);
      pgbrush.CenterPoint = new Point(75, 75);
      pgbrush.CenterColor = Color.White;
      pgbrush.SurroundColors = new Color[] { this.ForeColor };

    Redraw the button according to the Clicked state.

    if (_clicked == false)
    {
      g.FillPath(linearBrush, path);
      g.FillPath(b, innerPath);
    }
    else
    {
      g.FillPath(linearBrush, path);
      g.FillPath(pgbrush, innerPath);
    }
    Remember to dispose of the brush correctly.

  10. For a full code listing click here.

C# Online.NET