Graphics and C#


Introduction:

Graphics enhance the user interface of Windows Forms applications by generating graphical charts, graphical reports, and edit or create images. The .NET Framework includes tools that allow you to draw lines, shapes, patterns, and text. The focus of this paper is to help the beginner use the System.Drawing namespace. To that end, please note that this is meant for beginners. The paper will begin by, of course, using System.Windows.Forms in order to establish how a C# file can contain a Windows Form.

The objective of this article:

  • Enhance the user interface of a .NET Framework application by using brushes, pens, colors, and fonts.
  • Enhance the user interface of a .NET Framework application by using graphics, images, bitmaps, and icons.
  • Enhance the user interface of a .NET Framework application by using shapes and sizes.

This article assumes that the reader is familiar with both creating a basic Windows Forms application in Microsoft Visual Studio using either Visual Basic or C# , as well as writing to files and streams.

Consider this basic code:

//file: dialog.cs

using System;

using System.Windows.Forms;

class Program

{

    static void Main()

    {

        MessageBox.Show("This is a dialog box!");

    }
}

We compile this code on the .NET Framework.

C:\Windows\Microsoft.NET\Framework\v2.0.50727>csc.exe /target:winexe dialog.cs
C:\Windows\Microsoft.NET\Framework\v2.0.50727>dialog.exe

The output is a square dialog box containing the above text and the button control. In and of itself, however, it does not do much. In Windows, video memory is allocated in 4 kb blocks and the icons on the desktop are called Windows objects. The click of an icon of a program sends a message to the operating system; the program loader must associate the corresponding icon with the application program, as corresponded by the bitmap location of the icon. The loader, will, in turn, search the hard disk for the code and data of the program (code, data, IL, and metadata if it is a .NET application), along with any other necessary information to load and launch the program into runtime. In a similar fashion, if we drag and drop an icon we have dragged an object from its source and dropped it to its destination. The moment the mouse clicks on the object, a message is sent its window. The moment the icon is When the object is dropped at a target, a message is sent back to complete what is called a message loop. As a matter of fact, when any form of standard input - a keyboard press, a mouse click a Windows message is sent. For each event the Windows operating system creates a message which it sends to the application at hand. Each message contains:

  1. A message identifier which indicates the type of event ( mouse left click, mouse right click, a key press.
  2. The parameters whose type and number vary depending on the type of message (mouse position, a particular keyboard press.)

The following example outputs a Windows object, or actually a Windows Form with controls on it. There is an area for user input to enter a number. The callback procedure, called (in this context an event handler) will indicate if the number is prime or divisible by prime number:

//File: showprime.cs

using System;

using System.ComponentModel;

using System.Windows.Forms; // importing the namespaces that contain the classes

// that define the methods meant to work on the data

public class PrimeForm : Form
{
    // the class declaration the public part that exposes the

    public PrimeForm()
    {
        // its implementation

        InitializeComponent();

        InitializeBackgoundWorker();

    }

    private void InitializeBackgoundWorker()
    {

        backgroundWorker.DoWork += DoWork;

        backgroundWorker.RunWorkerCompleted += Complete;

        backgroundWorker.ProgressChanged += ProgressChanged;

        backgroundWorker.WorkerReportsProgress = true;

        backgroundWorker.WorkerSupportsCancellation = true;

    }

    private void DoWork( object sender,DoWorkEventArgs e )
    {

        BackgroundWorker worker = sender as BackgroundWorker;

        e.Result = IsPrime( (int)e.Argument, worker, e );

    }

    private void ProgressChanged( object sender, ProgressChangedEventArgs e )
    {

        progressBar.Value = e.ProgressPercentage;

    }

    private void Complete( object sender,RunWorkerCompletedEventArgs e )
    {

        textBoxInput.Enabled = true;

        buttonStart.Enabled = true;

        buttonCancel.Enabled = false;

        if ( e.Error != null )

        MessageBox.Show( e.Error.Message );

        else if ( e.Cancelled )

        textBoxResult.Text = "Processing cancelled!";

        else

        textBoxResult.Text = e.Result.ToString();

    }

    private void buttonStart_Click( object sender, EventArgs e )
    {

        int number = 0;

        if ( int.TryParse( textBoxInput.Text, out number) )
        {

            textBoxResult.Text = String.Empty;

            textBoxInput.Enabled = false;

            buttonStart.Enabled = false;

            buttonCancel.Enabled = true;

            progressBar.Value = 0;

            backgroundWorker.RunWorkerAsync( number );

        }
        else textBoxResult.Text = "input invalid!";

    }

    private void buttonCancel_Click( object sender, EventArgs e )
    {

        backgroundWorker.CancelAsync();

        buttonCancel.Enabled = false;

    }

    private string IsPrime( int number,BackgroundWorker worker, DoWorkEventArgs e)
    {

        int root = ( (int) System.Math.Sqrt(number) )+1;

        int highestPercentageReached = 0;

        for ( int i = 2; i < root; i++ )
        {

            if ( worker.CancellationPending )
            {

                e.Cancel = true;

                return String.Empty;

            }
            else
            {

                if (number % i == 0)

                return "can be divided by " + i.ToString();

                int percentComplete =(int)((float)i / (float)root * 100);

                if ( percentComplete > highestPercentageReached ) {

                highestPercentageReached = percentComplete;

                worker.ReportProgress(percentComplete);

            }

        }

    }

    return "is prime";

}


[STAThread]

static void Main()
{
    // program entry point after importing class libraries as

    Application.Run(new PrimeForm()); // separated by namespaces

}

private System.ComponentModel.IContainer components = null;
 

protected override void Dispose( bool disposing )
{

    if (disposing && (components != null))
    {

        components.Dispose();

    }

    base.Dispose( disposing );
}
 

#region Windows Form Designer generated code


private
void InitializeComponent()
{

    this.progressBar = new System.Windows.Forms.ProgressBar();

    this.label1 = new System.Windows.Forms.Label();

    this.textBoxInput = new System.Windows.Forms.TextBox();

    this.textBoxResult = new System.Windows.Forms.TextBox();

    this.buttonStart = new System.Windows.Forms.Button();

    this.buttonCancel = new System.Windows.Forms.Button();

    this.backgroundWorker = new System.ComponentModel.BackgroundWorker();

    this.SuspendLayout();
 

    // Location, Name, Size,TabIndex, Name and Text properties as set in Visual Studio

    this.progressBar.Location = new System.Drawing.Point(12, 33);

    this.progressBar.Name = "progressBar";

    this.progressBar.Size = new System.Drawing.Size(392, 23);

    this.progressBar.TabIndex = 0;

    this.label1.AutoSize = true; // the property for Label1 sets autosize=true

    this.label1.Location = new System.Drawing.Point(11, 9);

    this.label1.Name = "label1";

    this.label1.Size = new System.Drawing.Size(28, 13);

    this.label1.TabIndex = 1;

    this.label1.Text = "Num:"; // the text property is set to Num

    this.textBoxInput.Location = new System.Drawing.Point(48, 5);

    this.textBoxInput.Name = "textBoxInput";

    this.textBoxInput.Size = new System.Drawing.Size(181, 20);

    this.textBoxInput.TabIndex = 2;

    this.textBoxResult.Location = new System.Drawing.Point(235, 5);

    this.textBoxResult.Name = "textBoxResult";

    this.textBoxResult.ReadOnly = true;

    this.textBoxResult.Size = new System.Drawing.Size(169, 20);

    this.textBoxResult.TabIndex = 3;

    this.buttonStart.Location = new System.Drawing.Point(412, 4);

    this.buttonStart.Name = "buttonStart";

    this.buttonStart.Size = new System.Drawing.Size(75, 23);

    this.buttonStart.TabIndex = 4;

    this.buttonStart.Text = "Start";

    this.buttonStart.Click += new

    System.EventHandler(this.buttonStart_Click);

    this.buttonCancel.Location = new System.Drawing.Point(412, 33);

    this.buttonCancel.Name = "buttonCancel";

    this.buttonCancel.Size = new System.Drawing.Size(75, 23);

    this.buttonCancel.TabIndex = 5;

    this.buttonCancel.Text = "Cancel";

    this.buttonCancel.Click += new

    System.EventHandler(this.buttonCancel_Click);

    this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);

    this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;

    this.ClientSize = new System.Drawing.Size(499, 70);

    this.Controls.Add(this.buttonCancel);

    this.Controls.Add(this.buttonStart);

    this.Controls.Add(this.textBoxResult);

    this.Controls.Add(this.textBoxInput);

    this.Controls.Add(this.label1);

    this.Controls.Add(this.progressBar);

    this.Name = "PrimeForm";

    this.Text = "PrimeForm";

    this.ResumeLayout(false);

    this.PerformLayout();

}

#endregion

private System.Windows.Forms.ProgressBar progressBar;

private System.Windows.Forms.Label label1;

private System.Windows.Forms.TextBox textBoxInput;

private System.Windows.Forms.TextBox textBoxResult;

private System.Windows.Forms.Button buttonStart;

private System.Windows.Forms.Button buttonCancel;

private System.ComponentModel.BackgroundWorker backgroundWorker;


We compile this code:

C:Windows\Microsoft.NET\Framework\v2.0.50727> csc.exe /t:winexe /out:showprime.exe showprime.cs
C:Windows\Microsoft.NET\Framework\v2.0.50727> showprime.exe

And the output is a prime number indicator containing an array of boxes to indicate the number itself.

The main Windows key system components are ntdll.dll, hal.dll, kernel32.dll, csrss.exe, and two others: user32.dll and gdi32.dll. These last two are used for Windows objects and the graphics contained in them. Anyone who uses Visual Studio knows that a Windows Form can be completely contained in C# source code file. You drag and drop controls onto the user interface, inform the system of the name of the name of the control, gives names by manipulating the text properties, and then write (since the .NET Framework 2.0 enabled partial classes) write an event handler to handle the event of the mouse click on the control. GDI stands for Graphical Interface Device and the GDI+ library contained in the .NET Framework enables you to display images, render lines, curves, etc. On the Internet, the most widely used classes that comprise the System.Drawing namespace are the System.Drawing.Graphics class, the System.Drawing.Pen class, and the System.Drawing.Brush class.

Many times the user-define type is called a structure. A structure is a composite of other types that make it easier to work with related data. The simplest example is System.Drawing.Point, which contains X and Y integer properties that define the horizontal and vertical coordinates of a point. The Point structure simplifies working with coordinates by providing the constructor and members demonstrated here:

// create point
 

System.Drawing.Point p = new System.Drawing.Point(20, 30); 

// now move point diagonally:
p.Offset(-1, -1);

Console.WriteLine("Point X {0}, Y{1}", p.X, p.Y);

The System.Drawing.Graphics class encapsulates a GDI+ drawing surface. This class cannot be inherited. Use this class anytime you need to draw lines, draw shapes, or add graphical text to a control or an image.

The System.Drawing.Pen class defines an object used to draw lines, curves, and arrows. This class cannot be inherited.

The System.Drawing.Brush class describes how to fill the interiors of graphical shapes such as rectangles, ellipses, pies, polygons, and paths.

For example: when we use the System.Drawing.Pen class, as an engineer needs a mechanical pencil to draw a curve, the methods of the Graphics class used to draw lines or curves all need an instance of the System.Drawing.Pen class. This class must be instantiated in order to invoke those methods. Whether there are controls or actual curves, there locations are coordinates, even if a curve is perceived as a set of points having changes in direction.

//File: hello.cs

using System;

using System.Drawing;

using System.Drawing.Drawing2D;

public class Form1 : System.Windows.Forms.Form

{

    private void OnClick(object sender, System.EventArgs e)

    {

        using (Graphics g = CreateGraphics())

        {

            Brush brush = new HatchBrush(HatchStyle.DiagonalBrick, Color.White, Color.Black);

            g.DrawString("hello", new Font("Times", 50), brush, new Point(5, 5)); 

        }

    }

    private System.Windows.Forms.Button button;

    private System.ComponentModel.Container components = null;

    public Form1()

    {

        InitializeComponent();

    }

    protected override void Dispose( bool disposing )

    {

        if ( disposing )

        {

            if (components != null)

            {

                components.Dispose();

            }

        }

        base.Dispose( disposing );

    }

    private void InitializeComponent()

    {

        this.button = new System.Windows.Forms.Button();

        this.SuspendLayout();

        this.button.Location = new System.Drawing.Point(208, 16);

        this.button.Name = "Click";

        this.button.TabIndex = 0;

        this.button.Text = "Click";

        this.button.Click += new System.EventHandler(this.OnClick);

        this.AutoScaleDimensions = new System.Drawing.Size(5, 13);

        this.ClientSize = new System.Drawing.Size(292, 266);

        this.Controls.AddRange( new System.Windows.Forms.Control[] this.button);

        this.Name = "Form1";

        this.Text = "Form1";

        this.ResumeLayout( false );

    }

    [System.STAThread]

    static void Main()

    {

        System.Windows.Forms.Application.Run(new Form1());

    }

}

 

Compile this on the command line:

csc.exe /target:winexe /out:hello.exe hello.cs

The output is dialog box with an OK button. The click of the button control outputs hello on the user interface. Recall that the System.Drawing namespace provides the tools for drawing graphics and more importantly, editing existing images. There is a tool called the "Reflector" available for download at
www.aisto.net. This tool disassembles namespaces into their classes, which, in turn, revels their data members and methods available for use.