Create a "five-star" control

Source: Internet
Author: User

Download the code used in this article:AdvancedBasics0501.exe(144kb)

Content on this page
Getting started
Property routines with specific default values.
Develop custom and standard Images
Process and start events
Design inheritance
Code Organization
Auxiliary Functions
Ease of use
An example of triangle class inheritance
Summary

I must admit that most of my Windows Forms controls attempt to copy existing content. In my October 2004 column, I demonstrated how to create a progress bar that imitates Windows XP installation routines. I discussed it again this month. The objective of this project is the cool "five-star" Control in Windows mediaplayer (see figure 1 ).

Figure 1"5-Star" controls

This control looks great and provides a good way to view the level in a graphical way, but based on editing experience, I found it very cool. When you hover your mouse over this column, Windows Media Player highlights the stars to show the values of the columns on which you are currently floating. This provides a good graphic feedback. In a variety of web sites (including Netflix and Amazon), you can find the same type of user interface, and I want to have this function in my own applications, so, I decided to create my own controls. I will use the Windows Forms control to simulate this User Interface Element and try to make it have enough custom capabilities to use it in various environments.

Getting started

The first step is to create a new class library project to hold controls and empty Windows applications as a test project. Although the Windows Control Library Project template seems more suitable and can work well, by default, this project includes user controls (User Controls are generally used for composite controls-Windows Form Controls containing one or more controls), and all I need is an empty class file. Next, you must inherit the current and new empty classes from System. Windows. Forms. Control. You only need to add a single row after the class declaration:

Public Class Ratings    Inherits System.Windows.Forms.ControlEnd Class

If you try to use only intelliisense? When you add an Inherits statement, you will encounter a small problem: using a class library template to start your project does not add references to the System. Windows. Forms assembly. Therefore, you need to manually add the statement. In this case, you can add a reference to System. Drawing. dll, because you need to use a custom Drawing Control.

From now on, I generally follow the following steps to develop all controls:

1.

Add a standard constructor to the custom Drawing Control and set all the control styles required by the control to correctly draw and smooth it as much as possible.

Public Sub New()    Me.SetStyle(ControlStyles.AllPaintingInWmPaint, True)    Me.SetStyle(ControlStyles.DoubleBuffer, True)    Me.SetStyle(ControlStyles.ResizeRedraw, True)    Me.SetStyle(ControlStyles.UserPaint, True)    ... 'add any additional initialization code hereEnd Sub

2.

Write down the list of common attributes that may be required to configure the behavior and appearance of controls.

3.

Add all these attributes as private member variables (I like to use Hungary notation to add the prefix "m _" before each variable to represent internal variables) and include appropriate default values, as shown inFigure 2.

4.

Put these variables into the attribute process. Although most of the processes are quite simple (getting values and setting values), some of these processes require additional code, which will be discussed later.

5.

Start designing and writing custom drawing code.

6.

Finally, add new events, such as clicking to process or the specific events required by the specific control.

Back to Top

Property routines with specific default values.

I want some of my own properties-those that process colors-to reflect the default values of other properties related to controls (such as ForeColor) and other values that reflect the user's system color. For example, let's use only one of the colors of HoverColor to view the different methods that can generate default values.

The first method is obvious. You only need to set the default value in the variable declaration (or constructor:

Private m_HoverColor As Color = _    Color.FromKnownColor(KnownColor.Highlight)

In most cases, this can work well, but it has two problems. The first question is, what if a user changes the system color when the application is running? After restarting the program, the control will reflect the correct color, but not at the time. The second question is, what if you want to set the color as the default color programmatically? There is no practical way to clear the color settings and set them to the appropriate system color. The user will naturally set it to the appropriate system color, but then return to the first question again.

Another method is to set a trap when the user's system color changes and change your property value accordingly:

Protected Overrides Sub OnSystemColorsChanged( _        ByVal e As System.EventArgs)    Me.HoverColor = Color.FromKnownColor(KnownColor.Highlight)    Me.Invalidate()End Sub

This solution does not actually solve the problem, unless you have a way to use the control by developers to check whether the property is set to the default value or to a specific color. The system overhead for tracking this information may not be worth the effort. As an alternative, I decided to use the default null value and return the appropriate default value in the property routine itself, as shown inFigure 3.

In this way, the problem I have raised so far has been solved, including the handling of system color changes; understanding when it will return the default value and when the user will set it; and allow the user to reset this value to the default value (in mycontrol. hovercontrol = color. empty ).

Back to Top

Develop custom and standard Images

In the controls I am building, I decided to allow two main image categories: standard images and images provided by users. Although the original control only supports two standard images (circle and square), I will discuss a way to add a custom image to the list later.

All the drawings of this control are processed in the onpaint routine. I have rewritten this routine to provide my own rendering code (seeFigure 4). In this routine, calculate the position of each image (use the imagecount attribute to determine the number of images to be drawn), and then call drawstandardimage (draw a circle or square) or drawusersuppliedimage ).

These routines are not the most effective (for example, I always re-draw the complete control, instead of setting the areas affected by specific updates as invalid ), however, when necessary, they will consider drawing an appropriate image (or drawing an appropriate color image with standard options ). In other code of the control, the complete re-drawing is triggered by calling me. invalidate whenever the property or declaration changes and the control appearance changes. The override routine of onmousemove is an example of this type of code:

Protected Overrides Sub OnMouseMove(ByVal e As MouseEventArgs)    For i As Integer = 0 To Me.ImageCount-1        If Me.ItemAreas(i).Contains(e.X, e.Y) Then            Me.m_hoverItem = i + 1            Me.Invalidate()            Exit For        End If    Next    MyBase.OnMouseMove(e)End Sub
Back to Top

Process and start events

At this point, the control runs normally, mainly because it inherits all outstanding functions from system. Windows. Forms. Control. This inheritance relationship provides a set of available functions for the control, including the "click" event and the ability to drag to the design surface of a Windows form. But what is needed is not just those standard features, so I will add new events and code to several important domains (seeFigure 5).

This new SelectedItemChanged event provides a lot of convenience, it also has a good auxiliary function, that is, to improve the data binding performance. If an event is found in Windows form data binding code, the event has a name in the Changed format and is defined as System. eventHandler signature, it will use this event as a notification of changes to the binding property. Compared with polling for any changed attribute, it takes much less effort to monitor the event. Therefore, more effective data binding will be obtained.

Other routines I need to add to the control are only the override of OnMouseEnter and OnMouseLeave routines to ensure that the control is correctly displayed when the user suspends it. For exampleFigure 6As shown in, I also need to override the OnClick routine so that when the user selects a new level value, I can correctly update the currently selected project.

At this point, although I can add many "decorations", such as specifying a toolbox bitmap and classifying attributes of my attributes, the control has basically been completed and can work well. Nevertheless, the next trick is to allow other developers to scale my work to support other graphics.

Back to Top

Design inheritance

It takes effect only when the class is designed to inherit from it. Therefore, it may be a bit like a strong statement. Inheritance always works (as long as the class from which you inherit is not marked as NotInheritable), but only takes future inheritance into account during the base class design process, it makes it possible for other developers to add the required features in the future. To design classes that are easy to inherit from, the first step is to determine how other developers will expand them. Although you cannot predict what other developers may do, you can certainly guess which of the most obvious changes they may make. Remember this, you can now view the code for organizing problems, auxiliary functions, and ease of use.

Note: Do you want to divide the code into various functions to better encapsulate multiple domains (some of them want to expand the class), or do other developers only want to add some new features, instead, many irrelevant codes must be rewritten. Note that you have properly set the access modifiers (Public, Private, and Protected) for variables and routines ). Remember that your goal is to make the user experience as perfect as possible.

Although you need to pay attention to other things when designing a class to participate in inheritance, you have considered these issues when viewing this special example. Let us handle these problems in an orderly manner. I will discuss the changes that have been made to the "base" class (level) for ease of extension.

Back to Top

Code Organization

To organize code most effectively (although code may have been organized in some way to make it clear), I have not put the image and drawing code into onpaint. By using them as their own routines, developers can rewrite one of them without worrying about all positioning and graphical installation items in onpaint. Furthermore, I also carefully mark the two plot routines (and most of the routines in this class) as overridable because they may seem to need to be extended.

Back to Top

Auxiliary Functions

To enable some helper functions of my code, I set these two plot routines to protected (rather than private) so that the classes inherited from my class can use them, but they are still hidden from public interfaces. I also mark several additional routines (including onselecteditemchanged) as protected so that child classes can call those routines if necessary.

Back to Top

Ease of use

Among the three considerations, usability is the most vague one. It instructs the extended version of the class to be as easy to use as the base class. Of course, you cannot control the operations that the inherited developer will perform on your class, so you cannot guarantee that it will be easy to use at last, but you can try to improve the chances. For my class, I initially created imagetype as Enum (including userdefined, square, and circle), which generates the following code:

sr.ImageToDraw = ImageType.Circle

When I try to imagine it as an inherited environment (where the subclass has added a new imagetype), there will be a problem. Enumeration cannot be extended. Therefore, the result is:

sr.ImageToDraw = 3 'some number not in our original enum

According to the strong type, this may be a problem (although you may make it work, because Enum is generally the int32 type behind the scenes), and it is not suitable. To solve this problem, I discard the enumeration and define my imagetype as a public constraint on the control class. The Code is as follows:

sr.ImageToDraw = Ratings.Circle

The Code is as follows:

sr.ImageToDraw = myNewClass.NewShape
Back to Top

An example of triangle class inheritance

Thanks to the work that has been done so far, just a few minutes of coding, I can inherit from my controls and extend it using the new image type. Figure 7 shows the final result-a class that supports a single new graph type (triangle.

Figure 7MTS

The code I need is only the rewriting of DrawStandardImage and a new constant, as shown in 8.

Control Development for Windows and Web is an excellent way to make code snippets reusable. However, if you want to allow other developers to build on your own, you need to plan inheritance with caution.

Back to Top

Summary

The product completed this month is a simple level control, but it may be very useful if you use it as a column in the DataGrid. In the subsequent sections of the Advanced Basics column, I will use this level control as an example to write a custom control for the DataGrid column with you. Then we will try again.

Send your questions and commentsBasics@microsoft.com.

Duncan macenzieIt is the MSDN content planning of Visual Basic and C #, and the author of the Coding 4 Fun column of MSDN online. For more information about Duncan, visit his blog website weblogs.asp.net/duncanma or his personal website www.duncanmacenzie.net.

Citation http://www.microsoft.com/china/msdn/library/langtool/vbnet/RefactorVB2005.mspx

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.