Creating the Custom Control Project
Create a new project by selecting New, Project from the File menu. Select Visual C++ Projects from the list of project types and then select the Managed C++ Class Library from the list of templates. Selecting this template instructs the linker to create a DLL assembly rather than an executable. Give your project the name ShapeControl and click OK to create the project.
One thing that is different in the Managed C++ Class Library Project Wizard compared to the Managed C++ Application Wizard is the fact that a namespace and a managed class is automatically created for you. However, the default name for the class is Class1, which obviously isn't very descriptive. Open the ShapeControl.h file and change the name of the class from Class1 to ShapeControl. As mentioned earlier, your control will use the System::Windows::Form::Control class for its base functionality, so derive your class from the Control class, as shown in Listing 23.1.
As you may have guessed, the control you are going to create will draw a certain shape. The control will be able to draw one of two different shapes: a rectangle or an ellipse. Create an enumerated data type in the public section of your class. Give this data type the name Shape. Also, because the enumerated data type is defined within your class, you must preface it with the __value keyword as is the rule with any .NET enumerated types, as can be seen on line 26 of Listing 23.1. Next, create a private member variable that will hold the current value of a Shape object that corresponds to the shape currently being drawn by the control.
Just as you have done with the Windows Forms projects throughout this book, create a function that will be used to initialize your class each time a new instance of that class is created. Create a function named InitControl that accepts no parameters and returns void. Within that function set your Shape member variable to Shape::Rectangle to set the default shape for your control, as shown on line 55 of Listing 23.1. Furthermore, call the InitializeControl function within your default constructor.
Listing 23.1 Creating a .NET Custom Control
1: // ShapeControl.h 2: 3: #pragma once 4: 5: #using <System.dll> 6: #using <System.Drawing.dll> 7: #using <System.Windows.Forms.dll> 8: 9: using namespace System; 10: using namespace System::Drawing; 11: using namespace System::Windows::Forms; 12: 13: namespace ShapeControl 14: { 15: public __gc class ShapeControl : 16: public System::Windows::Forms::UserControl 17: { 18: public: 19: 20: ShapeControl() 21: { 22: InitializeControl(); 23: SetStyle(ControlStyles::ResizeRedraw, true); 24: } 25: 26: __value enum Shape 27: { 28: Rectangle = 0, 29: Ellipse = 1 30: }; 31: 32: protected: 33: void PaintHandler( Object* sender, PaintEventArgs* e ) 34: { 35: System::Drawing::Rectangle rcRect = get_ClientRectangle(); 36: 37: // deflate rect so circle isn't cut off 38: rcRect.Inflate( -5, -5 ); 39: 40: // Draw the rectangle or circle 41: if( m_eDrawShape == Shape::Rectangle ) 42: e->Graphics->DrawRectangle( 43: new Pen(this->ForeColor, 1), rcRect ); 44: 45: else if( m_eDrawShape == Shape::Ellipse ) 46: e->Graphics->DrawEllipse( 47: new Pen(this->ForeColor, 1), rcRect ); 48: } 49: 50: private: 51: Shape m_eDrawShape; 52: 53: void InitializeControl() 54: { 55: m_eDrawShape = Shape::Rectangle; 56: 57: add_Paint( new PaintEventHandler(this, PaintHandler )); 58: } 59: }; 60: }
At this point, your control has the necessary information it needs to function, but it is still missing one key piece: the OnPaint event handler. Because you are creating a custom control and not deriving from an already established control, it is your responsibility to provide the code to draw the user interface of the control. In the InitControl function, add an event handler for the OnPaint event by calling the add_Paint member function provided by the base class, as shown on line 57 of Listing 23.1. Next, create a protected member function for the PaintEventHandler delegate. This function will first get the current drawing rectangle by creating a local Rectangle variable. Following this, deflate the rectangle by a small amount. If you don't do this, the border of the ellipse you draw will be clipped in some places. Finally, based on the current value of your Shape member variable, draw a rectangle or an ellipse, as shown on line 41 of Listing 23.1.
You now have a working control. However, as mentioned earlier, you don't have any way of testing it to see whether it works. For now, compile the project. If there are any errors, double-check your code against the preceding listing.