Resources

Creating custom controls with C#

Lesson 1 - GDI+

  • GCI+ = .net managed implementation of GDI

System.Drawing Namespace

  • Summary
    • System.Drawing - most of classes actually involved with rendering to screen
    • System.Drawing.Design - extend design time UI
    • System.Drawing.2D - advanced visual effects
    • System.Drawing.Imaging - manipulation of image files
    • System.Drawing.Printing - print support
    • System.Drawing.Text - font manipulation

System.Drawing.Graphics Object

  • Principle object in rendering graphics
  • Represents drawing surface of visual element, e.g. form, control, Image, etc.
  • Can not directly instantiate - call CreateGraphics method on class deriving from Control
  • When working with Image can obtain from static Graphics.FromImage method
System.Drawing.Graphics myFormGraphics;

myFormGraphics = myForm.CreateGraphics();

Bitmap myImage = new Bitmap("C:\\myImage.bmp");

System.Drawing.Graphics myBmpGraphics;

myBmpGraphics = Graphics.FromImage(myImage);

Coordinates

  • Rendering occurs in region set by control bounds

  • Origin = upper left hand corner

  • Measured in screen pixels

  • Variety of structures describe location in region

    • Point - single point with int values for X and Y
    • PointF - single point with float values for X and Y
    • Size - rectangular size consisting of paired height and width values as integers
    • SizeF - rectangular size consisting of paired height and width values as float
    • Rectangle - top, bottom, left and right edges specified by int values
    • RectangleF - top, bottom, left and right edges specified by float values
  • Integral locations can be implicitly converted to floating-point counterparts

  • To convert floating-point to integer locations must explicitly convert each point

  • Note size structure indicate size, but not position

  • Create rectangle by supplying Size and Point (upper left hand corner)

Point myOrigin = new Point(10,10);

Size mySize = new Size(20,20);

Rectangle myRectangle = new Rectangle(myOrigin, mySize);

Drawing Shapes

  • Graphics object contains many methods to render shapes on screen
  • Those beginning Draw, e.g. DrawArc, DrawPie, draw line structures, outlines, etc.
  • Those beginning Fill, e.g. FillRegion, FillRectangle, render solid shapes
  • Methods take params specifying coordinates and location of shape to draw. Also require object to perform rendering - a Pen for Draw... and Brush for Fill...

Colors, Brushes, Pens

  • System.Drawing.Color represent single color. Specified by 4 values - alpha which specifies transparency and Red, Green and blue values. Each value in range 0 to 255

  • Can also specify named colours, e.g. Color.Tomato

  • All brushes derive from abstract Brush class

    • System.Drawing.SolidBrush - single solid colour
    • System.Drawing.TextureBrush - fills object with an image
    • System.Drawing.Drawing2D.HatchBrush - fills with hatch pattern
    • System.Drawing.Drawing2D.LinearGradientBrush - blends 2 colours along a gradient
    • System.Drawing.Drawing2D.PathGradientBrush - render complex gradient
  • Only one Pen class and it cannot be inherited. When create specify colour and width

  • Use SystemColors, SystemPens and SystemBrushes class to ensure UI has same look and feel as rest of system, e.g. Highlight text colour available from System.Colors.HighlightText

Rendering

  • Always Dispose of graphics objects when finished - use a lot of resources, failure to do so -> application degredation
SolidBrush myBrush = new SolidBrush(Color.MintCream);

Graphics g = this.CreateGraphics();

Rectangle myRectangle = new Rectangle(0, 0, 30, 20);

g.FillElipse(myBrush, myRectangle);

g.Dispose();

myBrush.Dispose();
  • To render text

    • Create Font and Brush objects
    • Obtain reference to Graphics object
    • Call Graphics.DrawString specifying string, font, brush and location
    • Dispose of objects
  • Complex Shapes

    • Obtain reference to Graphics object
    • Create instance of GraphicsPath class
    • Add figures to GraphicsPath
    • Call Graphics.DrawPath to draw path outline or Graphics.FillPath to fill shape
    • Dispaose of Graphics object
  • The GraphicsPath object describes any complex closed shape or set of shapes

GraphicsPath myPath = new GraphicsPath(new Point[] {new Point(1,1), new Point(32,54), new Point(33,5)}, new byte[] {(byte)PathPointType.Start, (byte)PathPointType.Line, (byte)PathPoint.Bezier});
  • Add figures to path using methods such as

    • GraphicsPath.AddClosedCurve
    • GraphicsPath.AddElipse
    • GraphicsPath.AddPie
    • GraphicsPath.AddString
    • ...
  • Can create figures by adding lines, arcs, curves, etc.

    • Begin by calling GraphicsPath.StartFigure

    • Add lines

      • AddArc
      • AddCurve
      • AddLine
    • Optionally call GraphicsPath.CloseFigure (if not called figure is closed at run time by drawing line from first point to last)

Lesson 2 - Authoring Controls

Inheritance

  • All controls inherit (directly or indirectly) from Control class

    • Provides low level logic for UI and common control methods
    • No code to render control
    • No code to provide unique functionality
  • Inherit from existing control = easiest way create new control

    • Inherits functionality and appearance
    • Can inherit from most Windows Forms controls (other than those marked sealed)
    • Use if retaining look and functionality of existing control but with some custom additions
    • Use if wish to retain functionality of existing control but a new look
    • Do not use if need radically different functionality
  • Inherit from UserControl

    • Single control may not provide all functionality required, e.g. want control bound to data source that shows first name, last name and phone number in separate TextBox

    • Rather than implement logic in form can group multiple Windows Form controls into single unit (User Control)

    • Sometimes called Composite Control

    • UserControl provides base functionality

      • UserControl designer allows other Windows Form controls to be added and custom functionality implemented
      • Limited customisation of UI - just configuring ands positioning constituent controls
    • Use when need to combine functionality of multiple existing controls and additional logic as single unit

  • Inherit from Control

    • Create custom control if rich UI or functionality unachievable via other inheritance mencahisms
    • Must program logic specific to control and code required to render visual representation
  • Additional members

    • Add to control in same way as add to class (at any access level)
    • Form hosting control has access to public members
    • Public properties are automatically displayed in Properties window (unless Browsable attribute is false)
[System.ComponentModel.Browsable(false)]
public int StockNumber

{

}

Create Inherited Control

  • Can either add new functionality or override members exposed by base class
public class NumberBox : System.Windows.Forms.TextBox

{
protected override void OnKeyPress(KeyPressEventArgs e)
{
if(char.IsNumber(e.KeyChar) == false)
e.Handled = true;
}
}
  • Override OnPaint method to modify control appearance, e.g to create button rendered as string of text
public class Wowbutton : System.Windows.Forms.Button  
{
protected override void OnPaint(PaintEventArgs pe)
{
System.Drawing.Drawing2D.GraphicsPath myPath = new System.Drawing.Drawing2D.GraphicsPath();

myPath.AddString("Wow!", Font.Family, (int)Font.Style, 72, new PointF(0,0), StringFormat.GenericDefault);

Region myRegion = new Region(myPath);

this.Region = myRegion;
}
}
  • Note some controls, e.g. TextBox, drawn by form they exist on and not by the control itself, thus Paint() never called.

Creating UserControl

  • Create class derived from UserControl

  • Add Windows Form controls via UserControl designer

  • Expose any appropriate properties from constituent controls

  • Write custom functionality for user control

  • Constituent controls are treated as private

  • If want to allow other developers to change properties of constituent controls must selectively expose them through properties of the user control

public color ButtonColor  
{
get
{
return Button1.BackColor;
}

set
{
Button1.BackColor = value;
}
}
  • Can expose entire constituent control by changing Modifiers property to desired access level. Property only available in Properties window - does not exist at runtime.

Creating Custom Control

  • Most customisable and configurable, but most time consuming to develop
  • Must write all code to render visual representation - most time-intensive part of control development
  • Default handler for Paint event is OnPaint() method
  • Coordinates measured relative to upper left corner of control
protected override void OnPaint(PaintEventArgs e)  
{
Brush aBrush = new SolidBrush(Color.Red);

Rectangle clientRectangle = new Rectangle(new Point(0,0), this.Size);

e.Graphics.FillEllipse(aBrush, clientRectangle);
}
  • When control resized ClipRectangle resized but control not necessarily redrawn. Can force redraw by using Control.SetStyle to set ResizeRedraw flag to true
  • Can manually cause redraw by calling Refesh method

Lesson 3 - Using Controls

Adding to Toolbox

  • Right click toolbox, choose Customize
  • Choose .NET Framework Components Tab and click Browse
  • Select DLL or EXE containing control
  • Control is added to toolbox

Providing Toolbox Bitmap

  • VisualStudio provide default icon bitmap for custom controls in toolbox
  • Specify custom bitmap viaToolboxBitmapAttribute class
    • Specify 16 by 16 pixel bitmap to use or
[ToolboxBitmap(@"C:\Pasta.bmp")  
public class PastaMaker : Control
{

}

Specify a type - custom control will have same Toolbox bitmap as that of specified type

[ToolboxBitmpa(typeof(Button))]  
public class myButton : Button
{

}

Debugging Control

  • Controls are not stand-alone projects - must be hosted within Windows Form project while debugging
  • If control part of executable project - add new Windows Form to project to hold control
  • If control part of non-executable project (e.g. class library) - add additional project to solution to test control

Licensing

  • .NET provides built-in license management

  • Default licensing model requires any control to be licensed to have LicenseProviderAttribute applied to it

    • Specifies LicenseProvider to use for validating license
    • LicenseProvider = abstract class providing standard interface for validation
    • In control constructor call LicenseManager.Validate to return reference to valid license - if control not licensed it will fail to load
  • LicenseManager.Validate validates license by calling LicenseProvider.GetLicense - retrieves license and checks validity by calling LicenseProvider.IsKeyvalid

  • Validation scheme depends on LicenseProvider implementation

    • .NET Framework includes implementation of LicesneProvider called LicFileLicenseProvider
    • GetLicense method of LicFileLicenseProvider searches for text file named <FullName>.LIC (where FullName = fully qualified control name)
    • Can override to provide own validation logic
    • Implement dispose for every licensed control
[LicenseProvider(typeof(LicFileLicenseProvider))]  
public class Widget : System.Windows.Forms.Control
{
private License myLicense;

public Widget()
{
myLicense = LicenseManager.Validate(typeof(Widget), this);
}

protected override void Dispose(bool Disposing)
{
if(myLicense != null)
{
myLicense.Dispose();
myLicense = null;
}
}
}

Hosting in IE

  • Every Windows Forms control can be hosted within IE

  • To host in IE, control must be installed in

    • Global Assembly cache
    • same virtual directory as HTML page it is declared in
  • Add to HTML page via <OBJECT> tag which specifies controls classid

  • Classid = 2 parts

    • Path to file containing control
    • Fully qualified name of control
<OBJECT id="myControl" classid="http:ControlLibrary.dll#ControlLibrary1.myControl" VIEWASTEXT>

</OBJECT>

Downloads