Develop custom controls in C # using intelligent devices
Release Date: 7/19/2004
| Update Date: 7/19/2004
Chris Tacke, Windows Embedded MVPApplied Data Systems
Applicable to: Microsoft Windows Ce .Netsmart Device Extensions for Microsoft Visual Studio .NET
Summary: Learn how to create custom controls using Smart Device Extensions for Microsoft Visual Studio .NET (SDE).
This page
Introduction to the problem object model Build a custom connector
Introduction
Smart Device Extensions for Microsoft Visual Studio .NET (SDE provides a good basic control that can be used in applications. Unfortunately, the embedded device application has a wide range, which makes the developers almost certainly lack appropriate controls in certain places. At this time, there are basically two options: re-performing the structure of the application Use the available controls or use your own custom control.
The first version of SDE does not support design-when custom controls, which means that in order to use them, you must manually write them into the form and set the size and attributes of the attribute. It only requires little extra workload and only needs to accept the fact that Form Design Support that is not available for custom controls.
Back to top
problem
Recently, I have been creating class libraries for Visual Studio .net for packaging a lot of hardware. By using a class library that can do all P / Invoking and resource management work, managed code developers use this class library to access machine micro-controllers and Microsoft Windows CE ports. I developed the I / O library for the Graphics Master device to provide the read and write functions of the pins on the two separate heads.
I need a test and sample application that allows users to easily set or read digital I / O status through the appropriate graphical interface and read analog I / O. I hope something looks like a linker on a schematic or a physical plug on the like. Since I have to deal with two physical different sizes, I need multiple controls, or it is best to define the size of the control. Obviously, there is no control I want in the toolbox of the SDE.
I can use a lot of Label, Checkbox, Picturebox and TextBox, but I think this alternative looks hard. Let us try to write your own controls.
Back to top
Object model
The first task is to determine the entire object model. What components do we need, how will these components integrate together, how do they interact with each other, how to interact with their environment?
Figure 1. My connector control concept
We will create a connector that contains a variable pin collection to connect to different sizes. Each pin must have an identification tag that can be placed on the left or right side of the "pin" displayed (depending on it is an even number or an odd pin). Each pin can also be a digital or analog I / O, so each pin requires a separate value ranging from zero to 0xFFFF. It is best to identify the type and value of each pin at a glance, so some colors will need to be used. Of course, all pins on the connector can be used for I / O, so we need to disable part of them, in addition, we hope that the pin is interacting, so when we turn on a pin, it can do some Some operations, such as changing status.
Figure 1 is a good model of an appearance displayed on the screen.
Based on these requirements, we proposed a model model as shown in Figure 2.
Figure 2. Control object model
The overall idea is that we will have a CONNECTOR base class, then derive from it from other custom Connector classes. The Connector will contain a Pins class, which is only used from CollectionBase to publicize ListAtArray using the indexer.
Implement PIN object
Because the backbone of this control is a PIN object, we first introduce it. The PIN object will process most of the display properties of the control and handle the user interaction. Once we can successfully create, display a single pin and interact with each other, build a connector is very simple together.
Pin objects have four properties that must be set when creating it. The default constructor sets each of them, but other constructor can also be used to allow creators to pass non-default values.
The most important attribute is Alignment. This property determines the location of the text and the pins when drawing objects, but more importantly, when setting properties, it will create and place rectangles for drawing pins and text. The use of these rectangles will be discussed when the OnDRAW is then explained.
Listing 1 shows the basic constructor and the code of the Alignment property. The offset and borders defined around the pin subcomponent use constants, but these constants are also easy to become other properties of the control.
Listing 1. Pin constructor and alignment properties
Public Pin ()
{
ShowValue = false;
PINVALUE = 0;
TYPE = PINTYPE.DIGITAL;
Alignment = pinalignment.pirenright;
}
Public PinalAlignment Alignment
{// determines where the pin Rectangle is placed
set
{
Align = Value;
IF (value == pinalignment.pirenright)
{
this.pinborder = new Rectangle
this.clientRectangle.width - (Pinsize.width 10),
1,
Pinsize.width 9,
THIS.CLIENTRECTANGLE.HEIGHT - 2);
this.pinbounds = New Rectangle
This.ClientRectangle.width - (Pinsize.width 5),
((this.clientRectangle.Height -
Pinsize.height) / 2) 1,
Pinsize.width,
Pinsize.height);
this.TextBounds = New Rectangle
5,
5,
this.clientRectangle.width - (Pinsize.width 10),
20);
}
Else
{
this.pinborder = new Rectangle
1,
1,
Pinsize.width 9,
THIS.CLIENTRECTANGLE.HEIGHT - 2);
this.pinbounds = New Rectangle
6,
THIS.CLIENTRECTANGLE.HEIGHT - (Pinsize.Height 4),
Pinsize.width,
Pinsize.height);
this.TextBounds = New Rectangle
Pinsize.width 10,
5,
this.clientRectangle.width - (Pinsize.width 10),
20);
}
THIS.INVALIDATE ();
get
{
Return align;
}
}
Since the PIN object does not provide a good user interaction or customizability, the core function of the pin is the drawing routine ondraw that we will rewrite, and the routine is overridden to draw the entire pin by us.
Each pin will draw three parts: The pin itself will be a circle (unless it is pin 1, when it will be a block), we will display a border rectangle around the pin, then on the left side of the pin or The right to leave a region for drawing the pins.
To draw a pin, we first determine the color used to represent the actual pin. If the pin is disabled, its color is gray. If enabled, make sure it is a type. The analog pin will be green, and the number pin is different depending on the situation. If it is low (off) is blue, if it is high (open) is orange.
Next, we use Fillellipse to draw all actual pins, but Pinnumber = 1 is except, then use the FillRectangle to draw the pin. By drawing in a rectangular (Pinbounds) instead of the control of the control, we can set the position of the pin (left or right) when you create a pin, and start from this point, we can do not have to care about the pin Drawing in the case of the case.
Next we draw labels, it will be the value of the text or pin of the pin, depending on the showValue property.
We use the strategy to draw the pins to draw text, but this time we must calculate the horizontal and vertical offset, because in the Microsoft .NET compression frame, the DrawText method does not allow TEXTALIGN parameters.
In the end, we clean up the brush object we manually used by calling the Dispose method.
Listing 2 shows a complete OON routine.
Listing 2. Ondraw () method
Protected Override Void OnPaint (Painteventargs PE)
{
Brush b;
// determine the Pin Color
IF (this.enable)
{
IF (Type == Pintype.digital)
{
// Digital Pins Have Different ON / Off Color
B = new system.drawing.solidbrush (
THIS.VALUE == 0? (DIGITALOFFCOLOR): (DIGITALONCOLOR);
}
Else
{
// Analog Pin
B = new system.drawing.solidbrush (analogcolor);
}
}
Else
{
// disabled pin
B = new system.drawing.solidbrush (DisabledColor);
}
// Draw the Pin
IF (this.pinnumber == 1)
Pe.graphics.FillRectangle (B, Pinbounds);
Else
Pe.graphics.Fillellipse (B, Pinbounds);
// Draw A Border Rectangle Around The Pin
PE.Graphics.drawRectangle (New Pen (Color.Black), Pinborder);
// Draw the text centered in the text bound
String drawstring;
// Are We showing the text or value? if (showvalue)
DrawString = Convert.toTOString (this.value);
Else
Drawstring = this.Text;
// DETERMINE THE ACTUAL STRING SIZE
Sizef fs = pe.graphics.measureString (
Drawstring,
New font (fontfamily.genericmonospace, 8f,
FontStyle.Regular);
// Draw the string
Pe.graphics.drawstring
Drawstring,
New font (fontfamily.genericmonospace, 8f,
FontStyle.Regular,
New Solidbrush ((ShowValue? AnalogColor: Color.Black),
TextBounds.x (TextBounds.width - fs.tosize (). width / 2,
TextBounds.y (TextBounds.height - fs.tosize (). HEIGHT) /
2);
// clean up the brush
b.dispose ();
}
}
The final step of building a PIN class is to add a Click handler. For our PIN, we will use custom Eventarg to pass the text and number of the pin to the event handler. To create a custom EventArg, we just created a class derived from the Eventargs class:
Public Class PinclicKeventargs: Eventargs
{
// a Pinclick Passs The Pin Number and the Pin's Text
Public int number;
Public string text;
Public PinclicKeventargs (int pinnumber, string pintext)
{
Number = pinnumber;
Text = pintext;
}
}
Next, we add a commission to the namespace:
Public Delegate Void PinclickHandler (PIN Source, PinclicKeventargs Args);
Now, we need to add code to determine when clicking, then incurred events. For our PIN class, when the MouseDown and MouseUp events occur during the border rectangle of the pin, it is a logically click-this, if the user clicks the text section of the pin, the Click event is not triggered, but if you click When the area of the actual pin is triggered, the event is triggered.
First, we need a public PinclickHandler event that is defined as follows:
Public Event PinclickHandler Pinclick;
We also need a private Boolean variable, we will set this variable when the mousedown event occurs, which is used to indicate that we are clicking. Then, we check the variable of the MouseUp event to determine if the event occurs in a continuous order:
Bool midclick;
Next, we need to add two event handles to MouseDown and Mouseup, as shown in Listing 3.
Listing 3. Event handler for implementing a PinClick event
Private void pinMousedown (Object Sender, MouseEventArgs E) {
IF (! this.enabled)
Return;
// if the user copy in the "PIN" Rectangle, Start a Click Process
MidClick = Pinborder.Contains (E.x, E.Y);
}
Private Void Pinmouseup (Object Sender, MouseEventArgs E)
{
// if we had a mousedown and kil in inside the "pin" Rectangle,
// Fire a Click
IF (MidClick && (PinBorder.Contains (E.x, E.Y))))
{
IF (Pinclick! = NULL)
Pinclick (this, new pinclickeventargs
This.pinnumber, this.text);
}
}
Finally, we need to implement an event handler for each pin. The basic constructor of the pin is a good place to add these hooks, we can do it directly to add the following code directly in the constructor:
THIS.MOUSEDOWN = New MouseEventHandler (PINMOUSEDOWN);
THIS.MOUSEUP = New MouseEventHandler (PinMouseUp);
Implement Pins class
Once there is a PIN, you can create a Pins class derived from CollectionBase. The purpose of this class is to provide an indexer so that we can easily add, delete, and manipulate the PIN class within a collection.
Listing 4. Pins class
Public Class Pins: CollectionBase: CollectionBase: CollectionBase
{
Public Void Add (Pin Pintoadd)
{
List.add (pintoadd);
}
Public Void Remove (Pin PinToremove)
{
List.Remove (Pintoremove);
}
// Indexer for Pins
Public Pin this [byte index]
{
get
{
Return (PIN) List [Index];
}
set
{
List [Index] = Value;
}
}
Public Pins () {}
}
Implement the Connector class
Since we have obtained the Pins class, we now need to build the Connector class, which will be a simple packaging class, which contains the Pins class and blocks the Pinclick event between each pin and connector container, and It has a constructor that represents the number of pins on the connector. Listing 5 shows a complete Connector class.
Listing 5. Connector class
Public Class Connector: System.Windows.Forms.Control
{
Public Event PinclickHandler Pinclick;
PROTECTED PINS PINS;
BYTE PINCOUNT;
Public Connector (Byte Totalpins)
{
Pins = new pins ();
PinCount = Totalpins;
InitializationComponent ();
}
Private vidinitiRizeComponent ()
{
For (int i = 0; i { PIN P = New Pin (Pintype.Digital, (PINALIGNMENT) ((i 1)% 2), 0); P.Pinclick = New PinclickHandler (Onpinclick); p.Pinnumber = i 1; p.Text = Convert.toString (i); P.TOP = (I / 2) * p.height; p.LEFT = (i% 2) * p.width; THIS.PINS.ADD (P); This.Controls.Add (p); } THIS.WIDTH = PINS [0] .width * 2; THISHEIGHT = Pins [0] .height * this.pins.count / 2; } Public Pins Pins { set { Pins = Value; } get { Return Pins; } } Private void Onpinclick (Pin Sender, Pinclickeventargs E) { // Pass on the event IF (Pinclick! = NULL) { Pinclick (Sender, E); IF (sender.Type == PINTYPE.DIGITAL) Sender.value = sender.value == 0? 1: 0; Else Sender.displayValue =! sender.displayvalue; } } Protected Override Void Dispose (Bool Disposing) { Base.dispose (Disposing); } } The Connector's initialization method is where you create all PIN classes that are included and added to the controls of the connector, and is where the connector itself resizes. InitializationComponent is also a method that eventually used by Form Designer to display our connector. Back to top Build custom connectors The Connector class itself is simple, it does not modify any default pin settings. However, we can now build a custom connectors through new classes from the Connector class, and modify a single pin (for example, make some pins into an analog pin, or disabled). In the sample application, I created two connectors for the Applied Data Systems Graphics Master board, one for J2, one for J7. The constructor is based on the type of connector setting pin and the text of the pin. 2 is a screen snapshot of an example application of J2 and J7 on the form. Figure 3. Form using two Connector objects Go to the original English page