本文转自http://www.codeproject.com/Articles/71961/Creating-a-Simple-Sheet-Designer-in-Csharp.aspx
Contents of this article:
Today, we are going to create a simple application, actually a simple sheet designing tool. This tool gives the user the flexibility to design his sheets, reports, bills, invoices, and receipts (whatever.)
In this writing, we will refer to reports, bills, invoices, receipts, etc. with just the name sheets. For this, we will need to give a sheet a definition.
A sheet is just like an empty page (i.e. template) that contains sheet items. For example, a resume template is a sheet that has many items (name, birth date, address, etc.) A cash receipt is a sheet that has a few items (date, amount, charged to, received by, etc.)
We will go through this tool in a nice way. We will begin by a very simple system analysis and application design. After that, we will get into coding.
Actually, I’m not an architect, and I think I will never be. So please, DO NOT blame me for this bad analysis. It is just an illustration to get you have a solid understanding of what our application is designed for.
The user is tied into the sheet designs that the developer has created for him. The user should have the ability to design his own sheets and/or to edit application defined sheets.
User Requirements
The following is list of common user requirements:
Functional Requirements
Common functional (developer) requirements are:
After going through project requirements and considering a sophisticated system design, we got a nice plan for our project that would be called Geming SISC (Sheet Infrastructure Components.)
This project would be created using C# and .NET 2.0 (or future versions of course.)
The following are final snapshots of the application. Figure 1 shows a cash receipt sheet designed by labels, boxes, and lines. Figure 2 shows a simple resume template designed by only a few labels and a picture. The left pane of the application lists current sheets grouped by their categories.
As you know other system components could fill those fields up with correct information.
Figure 3 shows the three components of our system.
The three core components of our system, Geming SISC are:
Extensibility is one of the main goals of the system. A flexible class hierarchy should be considered as well as derivation (i.e. inheritance) of controls for other external objects.
The following is the class hierarchy for the sheet and the ruler controls.
As we can see, the base class is the System.Windows.Forms.Control class. This allows the sheet and rulers controls to be inserted into a Windows form or a Windows control.
The following figure, figure 5, shows the class hierarchy of sheet items (technically called shapes.)
As we can see, the base class for all shapes is the abstract Geming.Sisc.Infrastructure.ShapeBase class which inherits from System.Windows.Forms.Control class. All other shapes are derived from ShapeBase.
Two edit shapes were created, TextBoxShape that has a look like a Windows Text Box, and LabelShape that has a look like a normal Windows Label. Both are derived from the abstract EditableShapeBase.
Other shapes are BoxShape, LineShape, ImageShape, and CheckBoxShape.
All classes are serializable (implement System.Runtime.Serialization.ISerializable interface,) so they can easily converted into XML and pushed to the database.
For simplicity, we have developed just the box, line, image, check box, and two edit shapes (or sheet items.) You can go further and create any other shapes you like. In addition, you can create more specific items like CurrencyField, DateField, etc.
For more detailed class diagrams like those shows class members, check the application code.
In its simplicity, database is defined as the following diagram illustrates:
Notice that, all data operations are carried out through stored procedures in the database.
The Shape.Value column is of the type xml to allow easier manipulation of XML data in the future.
Here are some of the characteristics (i.e. attributes) of the sheet (some are represented by properties):
In addition, next is a list of some the characteristics of a shape (sheet item):
Here are some refreshers of techniques we use in this system:
We will rely on custom painting in most situations. Therefore, well request help from System.Drawing classes specially the System.Drawing.Graphics class.
In some shapes like those mimic existing Windows controls (like the text box and the check box,) we will get help from classes in System.Windows.Forms.VisualStyles namespace to reflect the visual styles of Windows in our controls. In addition, System.Windows.Forms.ControlPaint class is used for drawing borders and selection rectangles.
Every control in our project (sheet, rulers, and shapes) has a paining quality property that determines the quality of the drawing (low, medium, and high.) For this to work we will make use of some properties of the System.Drawing.Graphics object like those related to smoothing and anti-aliasing feature.
All serialized objects should be marked with System.SerializableAttribute attribute. With help from System.Runtime.Serialization namespace we could customize the serialization process.
The core interface that would allow us to customize the serialization process is the System.Runtime.Serialization.ISerializable interface (implemented in all serializable classes.) Notice that, we should add the deserialization constructor to get correct deserialization.
The database is a SQL Server client database with all operations included as stored procedures.
The system uses two-tier architecture; means that it accesses the database directly using just three objects, a connection, a command, and a data reader.
For extending design-mode support we have created custom designer service for the Sheet class that inherits the System.Windows.Forms.Design.ParentControlDesigner class to allow nested controls in the Sheet object in the design-mode.
In this section we will talk about significant blocks of code that would be of interest.
The following is the code for OnPaint() routine overridden by only the ShapeBase class.
Listing 1 – ShapeBase.OnPaint() Method Code Listingprotected override void OnPaint(PaintEventArgs e)
{
GraphicsManager.SetQuality(e.Graphics, PaintingQuality);
ControlPaint.DrawBorder(e.Graphics, this.ClientRectangle, this.ForeColor, ButtonBorderStyle.Dotted);
PaintShape(e.Graphics);
if (Selected)
DrawSelectionFrame(e.Graphics);
ControlPaint.DrawSizeGrip(e.Graphics, this.BackColor, GetResizeGripRect());
}
This function first calls the GraphicsManager.SetQuality() function that sets the quality attributes of the Graphics object. We will get back to this function in about a moment.
After that the function paints the control border using the System.Windows.Forms.ControlPaint class.
Next comes the interesting point. The function calls the virtual function PaintShape() that a derived class overrides to provide its own drawing routines.
For example, the TextBoxShape class has this PaintShape() override:
Listing 2 – TextBoxShape.PaintShape() Method Code Listingpublic override void PaintShape(Graphics dc)
{
base.PaintShape(dc);
using (Brush b = new SolidBrush(this.BackColor))
dc.FillRectangle(b, this.ClientRectangle);
using (Pen p = new Pen(SystemColors.ActiveCaption, 1))
{
p.Alignment = System.Drawing.Drawing2D.PenAlignment.Inset;
Rectangle r = this.ClientRectangle;
dc.DrawRectangle(p, r);
}
Rectangle rect = this.ClientRectangle;
rect.Offset(2, 2);
rect.Width -= 4; rect.Height -= 4;
StringFormat format = new StringFormat();
format.LineAlignment = this.Alignment;
if (this.RightToLeft == RightToLeft.Yes)
format.FormatFlags = StringFormatFlags.DirectionRightToLeft;
using (Brush b = new SolidBrush(this.ForeColor))
dc.DrawString(this.Text, this.Font, b, rect, format);
}
After that the OnPaint() function draws selection frame if the shape is currently selected.
Just before the function closes, it calls the ControlPaint.DrawSizeGrip() function to draw the sizing grip that the user would resize the shape from.
In our library Geming.Sisc.Infrastructure, we have created a helper class, GraphicsManager, which contains only one function, SetQuality().
This function accepts two input parameters, the graphics object and the quality you wish to set to that object. The following is the code for the SetQuality() function.
Listing 3 – GraphicsManager.SetQuality() Method Code Listingpublic static void SetQuality(System.Drawing.Graphics g, PaintingQuality quality)
{
if (g == null)
throw new ArgumentNullException("g");
if (quality == PaintingQuality.High)
{
g.CompositingQuality = CompositingQuality.AssumeLinear;
g.InterpolationMode = InterpolationMode.HighQualityBicubic;
g.PixelOffsetMode = PixelOffsetMode.Half;
g.SmoothingMode = SmoothingMode.AntiAlias;
g.TextRenderingHint =
System.Drawing.Text.TextRenderingHint.ClearTypeGridFit;
}
else if (quality == PaintingQuality.Medium)
{
g.CompositingQuality = CompositingQuality.HighQuality;
g.InterpolationMode = InterpolationMode.Bilinear;
g.PixelOffsetMode = PixelOffsetMode.HighQuality;
g.SmoothingMode = SmoothingMode.HighQuality;
g.TextRenderingHint =
System.Drawing.Text.TextRenderingHint.AntiAliasGridFit;
}
else
{
g.CompositingQuality = CompositingQuality.Default;
g.InterpolationMode = InterpolationMode.Default;
g.PixelOffsetMode = PixelOffsetMode.Default;
g.SmoothingMode = SmoothingMode.Default;
g.TextRenderingHint =
System.Drawing.Text.TextRenderingHint.SystemDefault;
}
}
The application Geming.Sisc along with its code is available for download here.
This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)