Develop a custom control in C # using smart Device Extensions

Source: Internet
Author: User
Tags bool constructor object model visual studio
Control controls
Release Date: 7/19/2004 | Renew Date: 7/19/2004
Chris Tacke, Windows Embedded MVP
Applied Data Systems

Apply to:
Microsoft Windows CE. NET
Smart Device Extensions for Microsoft Visual Studio. NET

Summary: Learn how to create a custom control using Smart Device Extensions for Microsoft Visual Studio. NET (SDE).



Content of this page
Brief introduction
Problem
Object model
Build a custom Connector

Brief introduction
Smart Device Extensions for Microsoft Visual Studio. NET (SDE) provides a good basic control that you can use in your application. Unfortunately, the scope of embedded device applications is so broad that developers almost certainly lack the right controls in some places, when there are basically two choices: re-structure the application to use the available controls, or use your own custom controls.

The first version of SDE does not support design-time custom controls, which means that in order to use them, you must manually write code that places them into a form and sets their size and properties. It takes a little extra effort and only requires you to accept the fact that there is no Form design Support available for custom controls.

Back to the top of the page
Problem
Recently, I've been creating class libraries for Visual Studio. NET to wrap a lot of hardware. By using a class library that allows them to complete all p/invoking and resource management work, it is much easier for managed code developers to use this class library to access airborne microcontrollers and Microsoft Windows CE ports. I developed a class library for I/O to Graphics Master devices to provide read and write capabilities for pins on two separate headers.

I need a test and sample application that makes it easy for users to set up or read digital I/O status through the appropriate graphics interface and read analog I/O. I want something to look like a connector on a schematic or a physical plug in a similar panel. Because I'm dealing with two physically different sizes of connectors, I need more than one control, or preferably a control that can define size. Obviously, SDE's toolbox doesn't have the controls I want.

I could have used a lot of Label, CheckBox, PictureBox, and TextBox, but I think this alternative looks ugly. Let's try to write our own controls.

Back to the top of the page
Object model
The first task is to determine the entire object model. What components do we need, how will these components be fused together, how do they interact with each other, and how do they interact with their environment?



Figure 1. My connector Control concepts

We will create a connector to contain a variable size pin set so that connectors of different sizes can be connected. Each PIN must have an identification label that can be placed on the left or right side of the displayed "pin" (depending on whether it is an even or odd pin). Each pin can also be a digital or analog I/O, so each pin needs a separate value ranging from zero to 0xFFFF. It is best to recognize the type and value of each pin at a glance, so you will need to use some color. Of course, not all pins on the connector are available for I/O, so we need to be able to disable some of them, and we want the pins to be interactive so that when we connect to a pin, it can do something like change the state.

Figure 1 is a good model for the appearance of a control on the screen.

Based on these requirements, we present an object model as shown in Figure 2.



Figure 2. Control object Model

The whole idea is that we will have a Connector base class and then derive several other custom Connector classes from it. The Connector will contain a Pins class that exposes the Listarray of a Pin object simply by deriving from CollectionBase and using indexers.

Implementing a Pin Object
Because the backbone of this control is a Pin object, we'll introduce it first. The Pin object handles most of the display properties of the control and handles user interaction. Once we can successfully create, display, and interact with a single pin on a form, it is easy to build a connector that combines them together.

The Pin object has four properties that must be set when it is created. The default constructors set each of them, but other constructors can also be used to allow the creator to pass Non-default values.

The most important attribute is alignment. This property determines the position of the text and pins when the object is drawn, but more importantly, when you set the property, it creates and places a rectangle for drawing pins and text. The use of these rectangles will be discussed later when the OnDraw is interpreted.

Listing 1 shows the code for the basic constructor and the Alignment property. Constants are used for offsets and borders defined around the pin subassembly, but these constants are also easy to become other properties of the control.

Listing 1. Pin Constructors and Alignment properties

Public Pin ()
{
Showvalue = false;
Pinvalue = 0;
Type = Pintype.digital;
Alignment = Pinalignment.pinonright;
}
Public pinalignment Alignment
{//determines where the pin rectangle is placed
Set
{
align = value;
if (value = = pinalignment.pinonright)
{
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 good user interaction or customization, the core function of the PIN is that we will rewrite the drawing routine OnDraw, which is rewritten to allow us to draw the entire pin.

Each pin will draw three parts: the pin itself will be a circle (unless it is a Pin 1, then it will be a square), we will draw the border rectangle around the pin, and then leave a region on the left or right side of the pin to draw the text of the pin.

To draw the pins, we first determine the color used by the circle that represents the actual pin. If the PIN is disabled, its color is gray. If enabled, determine what type it is. The analog pins will be green, while the digital pins vary according to the situation, if the low (off) is blue, if it is high (open) it is orange.

Next, we use FillEllipse to draw all the actual pins, except when pinnumber=1, and then use FillRectangle to draw the pins. By drawing in the rectangle (pinbounds) rather than the bounds of the control, we are able to set the pin position (left or right) when we create the pin, and from this point we can draw without having to care about the position of the pin.

Next we draw the label, which will be the value of the pin's text or PIN, depending on the Showvalue property.

We use a strategy similar to drawing pins to draw text, but this time we have to compute the horizontal and vertical offsets, because the DrawText method does not allow TextAlign parameters in the Microsoft. NET compression Framework.

Finally, we clean up the Brush object we manually use by calling the Dispose method.

Listing 2 shows the complete OnDraw routine.

Listing 2. OnDraw () method

protected override void OnPaint (PaintEventArgs pe)
{
Brush b;
Determine the Pin color
if (this. Enabled)
{
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.ToString (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 in building a Pin class is to add a Click handler. For our Pin class, we will use a custom eventarg so that the text and number of the pins can be passed to the event handler. To create a custom eventarg, we just created a class that derives from the EventArgs class:

public class Pinclickeventargs:eventargs
{
A pinclick passes 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 delegate to the namespace:

public delegate void Pinclickhandler (Pin source, Pinclickeventargs args);

Now we need to add code to determine when the click occurs and then raise the event. For our Pin class, a logical click Occurs when the MouseDown and MouseUp events occur inside the bounding rectangle of the pin-so that if the user clicks the text portion of the PIN, the Click event is not triggered, but the event is triggered if the area that represents the actual pin is clicked.

First, we need a public Pinclickhandler event, which is defined as follows:

public event Pinclickhandler Pinclick;

We also need a private Boolean variable, which we will set when the MouseDown event occurs, to indicate that we are in the process of clicking. We then examine the variable for the MouseUp event to determine whether the event occurred in sequential order:

BOOL Midclick;

Next, we need to add two event handlers for MouseDown and MouseUp, as shown in Listing 3.

Listing 3. Event handlers for implementing the Pinclick event

private void Pinmousedown (object sender, MouseEventArgs e)
{
if (!this. Enabled)
Return
If the user clicked 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 then up 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, and we can do this by adding the following code directly to the constructor:

This. MouseDown + = new MouseEventHandler (Pinmousedown);
This. MouseUp + = new MouseEventHandler (pinmouseup);

Implement Pins Class
Once you have a Pin class, you can create a Pins class that derives from CollectionBase. The purpose of this class is to provide indexers so that we can easily add, delete, and manipulate Pin classes within the collection.

Listing 4. Pins class

public class Pins: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 Connector Class
Now that we've got the Pins class, we now need to build the Connector class, which will be a simple wrapper class that contains the Pins class and marshals the Pinclick event between each pin and the connector container, and it has a constructor that represents the number of pins on the connector. Listing 5 shows the 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;
InitializeComponent ();
}
private void InitializeComponent ()
{
for (int i = 0; i < Pincount; 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;
This. Height = 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 InitializeComponent method of Connector is to create all the included Pin classes and add them to the control of the connector, where the connector itself is resized. InitializeComponent is also the method that eventually is used by Form Designer to display our connectors.

Back to the top of the page
Build a custom Connector
The Connector class itself is simple, and it does not modify any of the default PIN settings. However, we can now build a custom connector by deriving a new class from the Connector class, and modify a single pin (for example, make some pins a mock pin or disable it).

In the sample application, I created two connectors for the Graphics Master Board of Applied Data Systems, one for J2, and one for J7. The constructor sets the type of pin and the text of the PIN based on the connector. Figure 2 is a screenshot of the sample application with J2 and J7 on the form.



Figure 3. A form with two Connector objects


Related Article

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.