I. Overview
Windows Forms controls are reusable components that encapsulate user interface functions and can be used in Windows applications on the client. "Windows Forms" not only provides many ready-to-use controls, but also provides the basic structure for self-developed controls. You can combine existing controls, expand existing controls, or create your own custom controls. Windows Form Controls are directly or indirectly derived from system. Windows. Forms. Control. The following lists common solutions for developing Windows Forms controls:
· Combine existing controls to create a composite control.
Composite controls encapsulate a user interface that can be reused as controls. The visualization designer provides powerful support for creating composite controls. Create a composite control derived from system. Windows. Forms. usercontrol. The base class usercontrol provides a keyboard route for the Child control and enables the child control to work as a group.
· Extend existing controls to customize them or add functions to them.
You can customize Windows Forms controls by deriving controls from any Windows Forms control and overwriting or adding properties, methods, and events.
· Create a control that is not formed by combining or extending existing controls.
In this solution, the control needs to be derived from the base class system. Windows. Forms. Control. You can add and override the attributes, methods, and events of the base class to create controls that are powerful and meet your needs.
The basic class system. Windows. Forms. Control of Windows Forms controls provides a required way to display the appearance of Windows applications on the client. Control provides a window handle to process message routing and provides mouse and keyboard events and many other user interface events. It also provides advanced la s with specific attributes for appearance display, such as forecolor, backcolor, height, width, and many other attributes. In addition, it provides security, thread support, and interaction with ActiveX controls. The base class provides many basic structures, making it easier to develop your own Windows Forms controls.
2. Write simple custom controls
2.1 simple control example
The following example creates a simple control that displays the value of its text attribute by processing the paint event. To create this control and process events, you must create a class inherited from control and create a method to override the onpaint method.
Public class helloworldcontrol: Control
{
Protected override void onpaint (painteventargs E)
{
Rectanglef rect = new rectanglef (clientrectangle. X,
Clientrectangle. y,
Clientrectangle. Width,
Clientrectangle. Height );
E. Graphics. drawstring (this. Text, Font, new solidbrush (forecolor), rect );
}
}
2.2. Procedure for creating a custom control in vs2005:
1. Open vs2005, file/New/project.
2. The "new project" dialog box appears.
3. In the "name" box, type the project name and select the location to store in the "location" box.
4. Select the programming language to use from the "language" list.
5. Click "add". A custom control project has been built and generated. A simple custom control is created without any function.
6. Add any tags and controls to the new user control and add code for all tasks executed by the control, such as processing control events or reading data from the data source.
2.3. Check the design behavior of the control.
1. Start vs2005.
2. Click/New/project/Windows application from the File menu to add a new form.
3. Right-click "toolbox/select item ...", In the pop-up "select toolbox items" dialog box, click the Browse button below to select the DLL to use the control. After confirmation, the control appears at the bottom of the Toolbox.
4. Select the control and add it to the form. The control appears on the form.
5. If you add controls from the previous example, you will notice that even simple controls have a complete set of attributes and extensive design-time behavior. This default action is inherited from the control class.
3. Add properties for controls
The control should define properties rather than public fields, because the visualization designer displays properties in the property browser rather than fields. Properties are like smart fields. A property usually has a dedicated data member with an access function. In syntax, the property is accessed as a field of the class. (Although attributes can have different access levels, the discussion here focuses on public access, which is more common.
Attribute definitions are generally composed of the following two parts:
1. Definition of dedicated data members.
Private int number = 0;
2. Define public attributes using the attribute declaration syntax.
This syntax associates private data members with public attributes through get and set access functions.
Public int mynumber
{
Get
{
Return number;
}
Set
{
Number = value;
}
}
Value is a keyword in the attribute definition syntax. In the call code, assign the variable value to the attribute. The value type must be the same as the declared type of the attribute it is assigned.
Although attribute definitions usually contain dedicated data members, this is not required. Get accessors can return values without accessing private data members. This is the case where the get method returns the system time attribute. Enable data hiding. The accessor method hides attributes.
When defining attributes, consider the following important considerations:
1. Attributes must be applied to defined attributes. Attribute is used to specify how the designer displays the attribute.
2. If you change the properties, the display of the control will be affected. Call the invalidate method from the set accessors (inherit the method from the control ). Invalidate then calls the onpaint method, which will re-draw the control. To improve efficiency, multiple calls to invalidate will generate one call to onpaint.
3. the. NET Framework Class Library provides a type converter for common data types (such as integers, decimals, Boolean values, and other data. A type converter is usually used to convert strings to numeric values (from string data to other data types ). Common data types are associated with the default type converter (which converts numeric values to strings and converts strings to corresponding data types. If you have defined a custom (I .e., non-standard) Data Type attribute, the application attribute must be specified as associated with the type converter. You can also use attributes to associate the custom UI editor with an attribute. The UI Editor provides a user interface for editing attributes or data types. The color selector is an example of the UI type editor.
For example, create a simple enumeration named drawingmode.
Public Enum drawingmode
{
Happy = 0,
Sad = 1,
Angry = 2
}
Then, add the mydrawingmode attribute to the control.
Private drawingmode mydrawingmode;
[Browsable (true), category ("appearance")]
Public drawingmode mydrawingmode
{
Get
{
Return mydrawingmode;
}
Set
{
Mydrawingmode = value;
Setcolors ();
}
}
The setcolors method is called to set the backcolor and forecolor of the control based on the value of mydrawingmode. Add the following code to the control.
Private void setcolors ()
{
Switch (mydrawingmode)
{
Case drawingmode. Happy:
This. backcolor = color. Yellow;
This. forecolor = color. Green;
Break;
Case drawingmode. Sad:
This. backcolor = color. lightslategray;
This. forecolor = color. White;
Break;
Case drawingmode. Angry:
This. backcolor = color. Red;
This. forecolor = color. Teal;
Break;
Default:
This. backcolor = color. Black;
This. forecolor = color. White;
Break;
}
}
Now you can add code to the paint method of the control to draw the style of the control, or add existing controls to combine and implement the desired functions (in this example ).
Private void usercontrol+paint (Object sender, painteventargs E)
{
Graphics curg = E. graphics;
Pen curpen = new pen (color. Black );
Rectangle currect = new rectangle (0, 0, width-2, height-3 );
Curg. drawrectangle (curpen, currect );
Curg. drawellipse (curpen, currect );
}
4. Add events for controls
Event)
An event is a message sent by an object to send a notification. Operations may be caused by user interaction (such as mouse clicking), or by some other program logic. The object that triggers the event is called the event sender. The object that captures an event and responds to it is called the event receiver.
In event communication, the event sender class does not know which object or method will receive (process) the event it raises. It is necessary to have a media (or pointer-like mechanism) between the source and the receiver ).. Net Framework defines a special type (delegate), which provides the function pointer function.
Proxy (delegate)
Delegate is a type in C #. It is actually a class that can hold a reference to a method. Unlike other classes, the delegate class can have a signature and can only hold references to methods that match its signature. In this way, the proxy is equivalent to a type security function pointer or a callback. It allows you to pass the method M of Class A to the object of Class B, so that the object of Class B can call this method M. But compared with function pointers, delegate has many advantages that function pointers do not possess. First, the function pointer can only point to static functions, while delegate can reference both static functions and non-static member functions. When referencing a non-static member function, delegate not only saves the reference to this function entry pointer, but also saves the reference to the class instance that calls this function. Second, compared with function pointers, delegate is an object-oriented, secure, and reliable managed object. That is to say, the runtime can ensure that the delegate points to a valid method. You do not need to worry that the delegate will point to an invalid or out-of-bounds address.
It is easy to implement a delegate. You can use the following three steps to implement a delegate:
1. Declare a delegate object. It should have the same parameter and return value type as the method you want to pass.
2. Create a delegate object and pass in the functions you want to pass as parameters.
3. Use the object created in the previous step to call the method where an asynchronous call is to be implemented.
The following is a simple example:
Public class mydelegatetest
{
// Step 1: declare the delegate object
Public Delegate void mydelegate (string name );
// This is the method we want to pass. It has the same parameter and return value type as mydelegate.
Public static void mydelegatefunc (string name)
{
Console. writeline ("Hello, {0}", name );
}
Public static void main ()
{
// Step 2: Create a delegate object
Mydelegate MD = new mydelegate (mydelegatetest. mydelegatefunc );
// Step 3: Call delegate
MD ("sam1111 ");
}
}
Event Processing
In C #, event processing is actually a delegate with a special signature, as shown below:
Public Delegate void myeventhandler (Object sender, myeventargs E );
The two parameters, sender represents the event sender, and E is the event parameter class. The myeventargs class is used to contain event-related data. All event parameter classes must be derived from the system. eventargs class. Of course, if your event does not contain special parameters, you can directly use the system. eventargs class as the parameter.
With the implementation of Delegate, We can summarize the implementation of custom events into the following steps:
1: defines the delegate object type. It has two parameters. The first parameter is the event sender object, and the second parameter is the event parameter class object.
2: Define the event parameter class. This class should be derived from the system. eventargs class. This step can be omitted if the event does not contain parameters.
3: Define the event processing method. It should have the same parameter and return value type as the delegate object.
4: Use the event keyword to define the event object. It is also a delegate object.
5: Use the + = Operator to add events to the event queue (the-= operator can delete events from the queue ).
6: Call the delegate method to write the event trigger method where an event needs to be triggered. In general, this method should be a protected access restriction. It cannot be called in public mode, but can be inherited by the quilt class. The name can be oneventname.
7: Call the event trigger method to trigger the event in an appropriate place.
The following is an example. The example imitates the container and control mode. An event is triggered by the control to capture and process the event in the container.
Event trigger:
/** // <Summary>
/// Event trigger
/// </Summary>
Public class control
{
Public Delegate void somehandler (Object sender, system. eventargs E );
Public event somehandler someevent;
Public Control ()
{
// The delegate used here must be consistent with the name of the event.
This. someevent + = new somehandler (this. processsomeevent );
}
Public void raisesomeevent ()
{
Eventargs E = new eventargs ();
Console. Write ("Please input 'A ':");
String S = console. Readline ();
// Trigger the event when the user inputs a small A; otherwise, the event is not triggered.
If (S = "")
{
Someevent (this, e );
}
}
// The Event trigger processes the event by himself. The parameters of this method must be consistent with the sound name in the proxy.
Private void processsomeevent (Object sender, eventargs E)
{
Console. writeline ("hello ");
}
}
Event receiver:
/** // <Summary>
/// Event receiver and handler
/// </Summary>
Class container
{
Private control CTRL = new control ();
Public container ()
{
// The delegate used here must be consistent with the name of the event.
CTRL. someevent + = new control. somehandler (this. responsesomeevent );
CTRL. raisesomeevent ();
}
Public static void main ()
{
Container pane = new container ();
Console. Readline ();
}
// This is the response of the event recipient to the event
Private void responsesomeevent (Object sender, eventargs E)
{
Console. writeline ("some event occur! ");
}
}
The running result is as follows:
Please input 'A':
Hello
Some event occur!
V. Other property settings
After the custom control project is created, the control settings, and attribute events are added, the project is generated and the control has been created. It can be used in the test project. There are many other settings, we need to know that, for example, the icons displayed in the controls we create are always a gear.
Each widget in the toolbox has its own icon. You can use the following statement to add an icon for the widget.
[Toolboxbitmap (@ "D: \ Program Files \ QQ \ airdlicon \ 1381love. ICO")]
Public partial class usercontrol1: usercontrol
{.............}
That is to say, add the toolboxbitmap Attribute before the control class, and the attribute parameter points to the address of an image. After this program is added, the generated control, for example, becomes a beautiful heart.
For another example, I have defined an attribute. If you do not set it, it will not be displayed in the attribute window, that is, when using a control, we cannot set it through a visual interface. to display it in the attribute window, we need to use the browsable attribute, as shown in the following example.
Public Enum drawingmode {happy = 0, sad = 1, angry = 2}
Private drawingmode mydrawingmode;
[Browsable (true)]
Public drawingmode mydrawingmode
{
Get
{
Return mydrawingmode;
}
Set
{
Mydrawingmode = value;
}
}
Attributes like this can also be used in combination. For example, in the preceding example, I add a category attribute after the browsable attribute so that its parameter is equal to appearance, at this time, the attributes we defined will be transferred from miscellaneous in the attribute box to the appearance items,
[Browsable (true), category ("appearance")]
Public drawingmode mydrawingmode
{
Get
{
Return mydrawingmode;
}
Set
{
Mydrawingmode = value;
}
}
There are many attributes like this, which I mainly list below. You can refer to them when using them.
Browsable
Applies to attributes and events. It specifies whether attributes or events should be displayed in the property browser.
Category
Applies to attributes and events. It specifies the name of a category. In this category, attributes or events are grouped. When a category is used, component attributes and events can be displayed in the property browser by logical grouping.
Description
Applies to attributes and events. defines a small piece of text that is displayed at the bottom of the property browser when you select a property or event.
Bindable
Applies to specifying whether to bind a property.
Defaultproperty
Applies to attributes (before inserting this feature into class Declaration .) Specify the default attributes of the component. When you click a control, this attribute is selected in the property browser.
Defaultvalue
Applies to properties and sets a simple default value for properties.
Editor
Applies to attributes. It specifies the editor to be used to edit (Change) attributes in the visual designer.
Localizable
Applicable to attributes. The specified attribute can be localized. When you want to localize a form, any attribute with this feature will automatically and permanently reside in the resource file.
Designerserializationvisibility
Applies to properties, specifying whether or not the properties displayed in the property browser should (and how) permanently reside in the code.
Typeconverter
Applies to attributes. It specifies the type converter used to convert the attribute type to another data type.
Defaultevent
Applicable to events (before inserting this feature into the class Declaration .) Specifies the default event of the component. This is the event selected in the property browser when you click a component.
[From Chris Liu http://www.worktool.cn/c-jishu/2008-10/291.htm]