Graphics, Multimedia, and Printing Recipes—Recipe 8 4
Recipe 8-4. Create a Movable Sprite
Problem
You need to create a shape the user can manipulate on a form, perhaps by dragging it, resizing it, or otherwise interacting with it.
Solution
Create a custom control, and override the painting logic to draw a shape. Assign your shape to the Control.Region property. You can then use this Region to perform hit testing.
How It Works
If you need to create a complex user interface that incorporates many custom-drawn elements, you need a way to track these elements and allow the user to interact with them. The easiest approach in .NET is to create a dedicated control by deriving a class from System.Windows.Forms.Control. You can then customize the way this control is painted in the way its basic set of events is raised.
The Code
The following example shows a control that represents a simple ellipse shape on a form. All controls are associated with a rectangular region on a form, so the EllipseShape control generates an ellipse that fills these boundaries (provided through the Control.ClientRectangle property). Once the shape has been generated, the Control.Region property is set according to the bounds on the ellipse. This ensures events such as MouseMove, MouseDown, Click, and so on, will occur only if the mouse is over the ellipse, not the entire client rectangle.
The following code shows the full EllipseShape code:
using System; using System.Drawing; using System.Windows.Forms; using System.Drawing.Drawing2D; namespace Apress.VisualCSharpRecipes.Chapter08 { public partial class EllipseShape : Control { public EllipseShape() { InitializeComponent(); } private GraphicsPath path = null; private void RefreshPath() { // Create the GraphicsPath for the shape (in this case // an ellipse that fits inside the full control area) // and apply it to the control by setting // the Region property. path = new GraphicsPath(); path.AddEllipse(this.ClientRectangle); this.Region = new Region(path); } protected override void OnPaint(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); } } protected override void OnResize(System.EventArgs e) { base.OnResize(e); RefreshPath(); this.Invalidate(); } } }
You could define the EllipseShape control in a separate class library assembly so you could add it to the Microsoft Visual Studio .NET Toolbox and use it at design time. However, even without taking this step, it is easy to create a simple test application. The following Windows Forms application creates two ellipses and allows the user to drag both of them around the form, simply by holding the mouse down and moving the pointer.
using System; using System.Drawing; using System.Windows.Forms; namespace Apress.VisualCSharpRecipes.Chapter08 { public partial class Recipe08_04 : Form { public Recipe08_04() { InitializeComponent(); } // Tracks when drag mode is on. private bool isDraggingA = false; private bool isDraggingB = false; // The ellipse shape controls. private EllipseShape ellipseA, ellipseB; private void Recipe08_04_Load(object sender, EventArgs e) { // Create and configure both ellipses. ellipseA = new EllipseShape(); ellipseA.Width = ellipseA.Height = 100; ellipseA.Top = ellipseA.Left = 30; ellipseA.BackColor = Color.Red; this.Controls.Add(ellipseA); ellipseB = new EllipseShape(); ellipseB.Width = ellipseB.Height = 100; ellipseB.Top = ellipseB.Left = 130; ellipseB.BackColor = Color.Azure; this.Controls.Add(ellipseB); // Attach both ellipses to the same set of event handlers. ellipseA.MouseDown += Ellipse_MouseDown; ellipseA.MouseUp += Ellipse_MouseUp; ellipseA.MouseMove += Ellipse_MouseMove; ellipseB.MouseDown += Ellipse_MouseDown; ellipseB.MouseUp += Ellipse_MouseUp; ellipseB.MouseMove += Ellipse_MouseMove; } private void Ellipse_MouseDown(object sender, MouseEventArgs e) { // Get the ellipse that triggered this event. Control control = (Control)sender; if (e.Button == MouseButtons.Left) { control.Tag = new Point(e.X, e.Y); if (control == ellipseA) { isDraggingA = true; } else { isDraggingB = true; } } } private void Ellipse_MouseUp(object sender, MouseEventArgs e) { isDraggingA = false; isDraggingB = false; } private void Ellipse_MouseMove(object sender, MouseEventArgs e) { // Get the ellipse that triggered this event. Control control = (Control)sender; if ((isDraggingA && control == ellipseA) || (isDraggingB && control == ellipseB)) { // Get the offset. Point point = (Point)control.Tag; // Move the control. control.Left = e.X + control.Left - point.X; control.Top = e.Y + control.Top - point.Y; } } } }
Figure 8-4 shows the user about to drag an ellipse.
Figure 8-4. Dragging custom shape controls on a form
|


