NET User Interfaces in Csharp Windows Forms and Custom Controls [Electronic resources] نسخه متنی

اینجــــا یک کتابخانه دیجیتالی است

با بیش از 100000 منبع الکترونیکی رایگان به زبان فارسی ، عربی و انگلیسی

NET User Interfaces in Csharp Windows Forms and Custom Controls [Electronic resources] - نسخه متنی

Matthew MacDonald

| نمايش فراداده ، افزودن یک نقد و بررسی
افزودن به کتابخانه شخصی
ارسال به دوستان
جستجو در متن کتاب
بیشتر
تنظیمات قلم

فونت

اندازه قلم

+ - پیش فرض

حالت نمایش

روز نیمروز شب
جستجو در لغت نامه
بیشتر
لیست موضوعات
توضیحات
افزودن یادداشت جدید



























Reconsidering the Vector Drawing Program


Chapter 11 developed the basic framework for a control-based drawing program using the label control. The program worked well and introduced a basic framework that could accommodate any type of control. The only problem is that the
.NET Framework doesn't include controls for common shapes like circles, triangles, and squares. Instead, the program "faked" a square by using a label control with a border.

Now that you've covered GDI+, there's a far better solution available, and it only takes a little bit more coding. The basic concept is to create an owner-drawn control that paints the appropriate shape. Substitute this control in the place of the bordered label. The drawing framework handles the dragging, resizing, and coloring automatically by setting properties like Location, Size, and Font.

There is a possible problem. If the user draws a circle, you want the circle shape to act like a circle for all mouse operations. In other words, the user shouldn't be able to click on a part of the control outside the circle and use that to move the control. Similarly, this "invisible" portion of the control shouldn't overwrite other controls on the drawing surface.

Figure 13-5 shows a drawing program with shapes that doesn't take this into account.


Figure 13-5: A flawed drawing program


Solving the Bounding Problem


Luckily, .NET makes it easy to create a control that has a nonrectangular bounding area. In fact, you saw this technique in Chapter 5 with shaped forms. All that's required is to set the control's Region property, which defines a new clipping region.

Figure 13-6 shows a drawing program that uses the region property to define control borders. Note that this does have a side effect: the control cannot be as effectively antialiased (or blended with the background). As a result, the border appears more jagged.


Figure 13-6: A corrected drawing program

This new drawing program supports rectangles, circles, and squares, but it could easily support any arbitrary or unusual shape. The program works by dynamically creating an instance of a custom shape control.

The shape control has the following features:



It provides a ShapeType enumeration that defines the shapes it can represent. The programmer chooses a shape by setting the Shape property.



The shape control uses a private path member variable that references a GraphicsPath object with the associated shape. Whenever the Shape property is modified, the control creates a new GraphicsPath, and adds the appropriate shape to it. It then sets the control's Region property, effectively setting its clipping bounds to match the shape.



The painting logic is the easiest part. It simply uses the FillPath() method to draw the shape and the DrawPath() method to outline it.



Here's the complete Shape class code:


public class Shape : System.Windows.Forms.UserControl
{
// The types of shapes supported by this control.
public enum ShapeType
{
Rectangle,
Ellipse,
Triangle
}
private ShapeType shape = ShapeType.Rectangle;
private GraphicsPath path = null;
public ShapeType Type
{
get
{
return shape;
}
set
{
shape = value;
RefreshPath();
this.Invalidate();
}
}
// Create the corresponding GraphicsPath for the shape, and apply
// it to the control by setting the Region property.
private void RefreshPath()
{
path = new GraphicsPath();
switch (shape)
{
case ShapeType.Rectangle:
path.AddRectangle(this.ClientRectangle);
break;
case ShapeType.Ellipse:
path.AddEllipse(this.ClientRectangle);
break;
case ShapeType.Triangle:
Point pt1 = new Point(this.Width / 2, 0);
Point pt2 = new Point(0, this.Height);
Point pt3 = new Point(this.Width, this.Height);
path.AddPolygon(new Point[] {pt1, pt2, pt3});
break;
}
this.Region = new Region(path);
}
protected override void OnResize(System.EventArgs e)
{
base.OnResize(e);
RefreshPath();
this.Invalidate();
}
protected override void OnPaint(System.Windows.Forms.PaintEventArgs e)
{
base.OnPaint(e);
if (path != null)
}
e.Graphics.SmoothingMode = SmoothingMode.AntiAlias;
e.Graphics.FillPath(new SolidBrush(this.BackColor), path);
e.Graphics.DrawPath(new Pen(this.ForeColor, 4), path);
}
}
}

The drawing framework needs a slight modification: instead of creating a label control, it creates a Shape object and sets the Shape property depending on the user's menu selection. The menu options ("New Rectangle," "New Ellipse," and "New Triangle") are represented by three menu objects (mnuRectangle, mnuEllipse, and mnuTriangle), and the click event for each of these objects triggers the same event handler:



private void mnuNewShape_Click(object sender, System.EventArgs e)
{
// Create and configure the shape with some defaults.
Shape newShape = new Shape();
newShape.Size = new Size(40, 40);
newShape.ForeColor = Color.Coral;
// Configure the appropriate shape depending on the menu option selected.
if (sender == mnuRectangle)
{
newShape.Type = Shape.ShapeType.Rectangle;
}
else if (sender == mnuEllipse)
{
newShape.Type = Shape.ShapeType.Ellipse;
}
else if (sender == mnuTriangle)
{
newShape.Type = Shape.ShapeType.Triangle;
}

// To determine where to place the shape, you need to convert the
// current screen-based mouse coordinates into relative form coordinates.
newShape.Location = this.PointToClient(Control.MousePosition);
// Attach a context menu to the shape.
newShape.ContextMenu = mnuLabel;
// Connect the shape to all its event handlers.
newShape.MouseDown += new MouseEventHandler(lbl_MouseDown);
newShape.MouseMove += new MouseEventHandler(lbl_MouseMove);
newShape.MouseUp += new MouseEventHandler(lbl_MouseUp);
// Add the shape to the form.
this.Controls.Add(newShape);
}

With this minor modification, the drawing program now handles the ownerdrawn controls seamlessly, supporting the same features for resizing, dragging, and changing the fill color.

One minor quirk appears with the ellipse shape. The drawing program only allows a shape to be resized using its bottom right corner. However, the ellipse's corners are clipped off to fit the circular region.

A simple workaround is to add the ability to resize the shape from any of its sides. The revised MouseMove event handler is shown as follows.


private void lbl_MouseMove(object sender, System.Windows.Forms.MouseEventArgs e)
{
// Retrieve a reference to the active shape.
Control currentCtrl;
currentCtrl = (Control)sender;
if (isDragging)
{
// Move the control.
currentCtrl.Left = e.X + currentCtrl.Left - clickOffsetX;
currentCtrl.Top = e.Y + currentCtrl.Top - clickOffsetY;
}
else if (isResizing)
{
// Resize the control, according to the resize mode.
if (currentCtrl.Cursor == Cursors.SizeNWSE)
{
currentCtrl.Width = e.X;
currentCtrl.Height = e.Y;
}
else if (currentCtrl.Cursor == Cursors.SizeNS)
{
currentCtrl.Height = e.Y;
}
else if (currentCtrl.Cursor == Cursors.SizeWE)
{
currentCtrl.Width = e.X;
}
}
else
{
// Change the cursor if the mouse pointer is on one of the edges
// of the control.
if (((e.X + 5) > currentCtrl.Width) &&
((e.Y + 5) > currentCtrl.Height))
{
currentCtrl.Cursor = Cursors.SizeNWSE;
}
else if ((e.X + 5) > currentCtrl.Width)
{
currentCtrl.Cursor = Cursors.SizeWE;
}
else if ((e.Y + 5) > currentCtrl.Height)
{
currentCtrl.Cursor = Cursors.SizeNS;
}
else
{
currentCtrl.Cursor = Cursors.Arrow;
}
}
}


Note

Be sure to check out the DrawingShapes example with this chapter.With remarkably few lines of code, it implements a drawing program that lets you add, move, resize, and remove shapes. Using these principles, you could create something more practical for your organization, like a custom diagramming tool.




/ 142