Pro Visual C++-CLI And The .NET 2.0 Platform (2006) [eng]-1
.pdf478 |
C H A P T E R 1 1 ■ G R A P H I C S U S I N G G D I + |
array<FontFamily^>^ fontfamilies = availFonts->Families;
Random ^rand = gcnew Random(); int ff, s, fs, u;
for (int i = 0; i < fonts->Length; i++)
{
s = rand->Next(0,3); fs = rand->Next(0,3);
u= rand->Next(0,2);
//Not all fonts support every style do {
ff = rand->Next(0,fontfamilies->Length);
}
while (!fontfamilies[ff]->IsStyleAvailable( (FontStyle)fontstyles[fs]));
//Display string of font
fontstr[i] = String::Format("{0} {1} {2}", fontfamilies[ff]->Name,
sizes[s], String::Concat(fontstyles[fs], " ",
units[u]));
// Create the font
fonts[i] = gcnew Drawing::Font(fontfamilies[ff], sizes[s], (FontStyle)fontstyles[fs], (GraphicsUnit)units[u]);
}
}
protected:
~Form1()
{
if (components)
{
delete components;
}
}
private:
System::ComponentModel::Container ^components; array<Drawing::Font^>^ fonts;
array<String^>^ fontstr;
#pragma region Windows Form Designer generated code
void InitializeComponent(void)
{
this->SuspendLayout();
this->AutoScaleDimensions = System::Drawing::SizeF(6, 13); this->AutoScaleMode = System::Windows::Forms::AutoScaleMode::Font; this->ClientSize = System::Drawing::Size(292, 273);
480 |
C H A P T E R 1 1 ■ G R A P H I C S U S I N G G D I + |
Colors
Most current display device technology defines colors by breaking them up into their three basic components: red, green, and blue. Depending on the configuration of the display device, these components usually will have a value that ranges from 0 to 255. The principle is that by combining different amounts of red, green, and blue, you can generate any color. Thus, many of today’s display devices can display up to 16,777,216 (256 cubed) unique colors.
But the story doesn’t end there. Colors also provide an alpha component. This component represents how transparent the color is. If the alpha value is 0, then the color is completely transparent (a kind of useless color), and a value of 255 is completely opaque. In between these two points are varying degrees of transparency that will, when drawn to the screen, merge with any color already existing at that location. You see this effect used most often in computer games.
Many of the Graphics class’s Drawing methods need a System::Drawing::Color structure containing one of the colors built from the values described previously before they can be used. The Color class has a number of members (see Table 11-11) available to get color information from. You can use only three common methods to place color information into a Color structure:
•FromArgb() returns a Color class based on the alpha, red, green, and blue values passed to it.
•FromKnownColor() returns a Color class based on a predefined color.
•FromName() returns a Color class based on the string color name passed.
You must use one of these three methods to create your color because there is no Color constructor.
Table 11-11. Common Color Members
Member |
Description |
A |
Gets the alpha component |
B |
Gets the blue component |
G |
Gets the green component |
GetBrightness() |
Gets the brightness of the color based on the hue-saturation- |
|
brightness (HSB) value of the color |
GetHue() |
Gets the hue of the color, based on the HSB value of the color |
GetSaturation() |
Gets the saturation of the color, based on the HSB value of the color |
IsKnownColor() |
true if it is a known color |
IsNamedColor() |
true if it is a named color |
IsSystemColor() |
true if it is a system color |
Name |
Gets the name of a “named” color |
R |
Gets the red component |
ToArgb() |
Gets the 32-bit ARGB value of the color |
ToKnownColor() |
Gets the KnownColor value of the color |
|
|
There are two basic methods of defining a Color class: defining it using a combination of red, green, blue, and alpha component values or selecting the color from a list of predefined colors.
C H A P T E R 1 1 ■ G R A P H I C S U S I N G G D I + |
481 |
Custom Colors
To build your own custom color, you need to use the Color class’s FromArgb() method. There are several overloads of the method, but you will most likely use two of them. The first method takes only the red, green, and blue components and defaults the alpha component to opaque (255). The second method allows you to specify the alpha component.
// Pure red
Color red1 = Color::FromArgb(255, 0, 0);
Color red2 = Color:: FromArgb(255, 255, 0, 0); //Pure green
Color green1 = Color::FromArgb(0, 255, 0); Color green2 = Color::FromArgb(255, 0, 255, 0); //Pure blue
Color blue1 = Color::FromArgb(0, 0, 255); Color blue2 = Color::FromArgb(255, 0, 0, 255);
You can make transparent or semitransparent colors by adjusting the alpha component passed to the FromArgb() method:
Color transparentgray = Color::FromArgb(127, 127, 127, 127);
Named Colors
The Color class provides a large number of predefined, or named, colors. There are two types of named colors. The first is a name that describes the color. These types of colors range (alphabetically) from AliceBlue to YellowGreen. The second type of color uses a name that describes its role in the Windows standard interface, such as ControlText, ScrollBar, and Window.
The three ways of creating named colors are using the FromKnownColor() method, using the static named color method directly, or using the string name of the color.
Color c1 = Color::FromKnownColor(KnownColor::AliceBlue);
Color c2 = Color::AliceBlue;
Color c3 = Color::FromName("AliceBlue");
Pens and Brushes
When you render images to a drawing surface, you need an object to actually do the drawing. GDI+ provides two objects: the Pen and the Brush. The Pen type is used to draw the outline of a shape, and the Brush type fills in an enclosed shape. (Makes sense, don’t you think?)
Pens
You’ve all worked with a pen, so the idea of what a pen does shouldn’t be hard to visualize. Normally, you use a pen to draw the outline of the object. Most likely, you draw a solid line, but sometimes you might use a sequence of a bunch of dots and dashes. When you’re drawing a line between two objects, you probably will put an arrow on one or both ends. If you like variety, you might even use a red or blue pen along with your black one.
The Pen type provided by GDI+ provides basically the same functionality.
482 |
C H A P T E R 1 1 ■ G R A P H I C S U S I N G G D I + |
Custom Pens
You use the Pen constructor to create a Pen object, and then you use its properties (see Table 11-12) to indicate how you want the Pen used. There are several constructors to create a Pen, but in most cases the simple color and width constructors do the trick:
Pen^ pen1 = gcnew Pen(Color::Blue, 3.0);
Or if you want the Pen to be only 1 graphics unit thick, you could use this even easier code:
Pen^ pen2 = gcnew Pen(Color::Blue);
Notice I used the term “graphics unit.” The Pen type’s thickness is based on the graphics unit, not pixels, though the default is pixels.
Table 11-12. Common Pen Properties
Property |
Description |
Color |
Specifies the color of the Pen |
CompoundArray |
Specifies the splitting of the width of a line into multiple parallel lines |
CustomEndCap |
Specifies a custom cap for the end of the line |
CustomStartCap |
Specifies a custom cap for the start of the line |
DashCap |
Specifies the dash-dot-space pattern used at the cap of a line |
DashOffset |
Specifies the distance from the start of the line to the beginning of the |
|
dash-dot-space pattern |
DashPattern |
Specifies a predefined dash-dot-space pattern to be used for a line |
DashStyle |
Specifies the style of the dash lines |
EndCap |
Specifies a predefined cap to be used for the end of the line |
LineJoin |
Specifies the style of the join between two consecutive lines |
PenType |
Specifies the style of the line generated by the Pen |
StartCap |
Specifies a predefined cap to be used for the start of the line |
Width |
Specifies the width of the Pen |
|
|
Named Pens
If you are creating a pen that is only 1 graphics unit thick and uses a named color, then you can use one of the pens found in the Pens class. The name of the pen is the same as the name of the named color it is using:
Pen^ pen = Pens::AliceBlue;
C H A P T E R 1 1 ■ G R A P H I C S U S I N G G D I + |
483 |
System Pens
System pens are virtually the same as named pens, except that instead of a pen being named after a color, it is named after the role that the Pen would use on the Windows GUI interface. Also, you will find system pens in the SystemPens class and not in the Pens class:
Pen^ pen = SystemPens::MenuText;
Listing 11-12 presents an example program that draws a few lines using the CompoundArray,
DashStyle, StartCap, and EndCap properties.
Listing 11-12. Creating Some Random Lines
namespace |
DrawingLines |
{ |
|
using |
namespace System; |
using |
namespace System::ComponentModel; |
using |
namespace System::Collections; |
using |
namespace System::Windows::Forms; |
using |
namespace System::Data; |
using |
namespace System::Drawing; |
using |
namespace System::Drawing::Drawing2D; |
public ref class Form1 : public System::Windows::Forms::Form
{
public:
Form1(void)
{
InitializeComponent();
pen = gcnew array<Pen^>(5);
//a one unit width black pen pen[0] = Pens::Black;
//a one unit with purple pen broken with dashes pen[1] = gcnew Pen(Color::Purple); pen[1]->DashStyle = DashStyle::Dash;
//a 4 unit width chocolate pen
pen[2] = gcnew Pen(Color::Chocolate, 4);
//An 8 width royalblue pen made of three lines narrow wide narrow pen[3] = gcnew Pen(Color::RoyalBlue, 10);
array<float>^ cArray = gcnew array<float> { 0.0f, 0.1f, 0.3f, 0.7f, 0.9f, 1.0f
};
pen[3]->CompoundArray = cArray;
//a 5 width tomato pen with diamond start and round end anchors pen[4] = gcnew Pen(Color::Tomato, 5);
pen[4]->StartCap = LineCap::DiamondAnchor; pen[4]->EndCap = LineCap::RoundAnchor;
}
484 |
C H A P T E R 1 1 ■ G R A P H I C S U S I N G G D I + |
protected:
~Form1()
{
if (components)
{
delete components;
}
}
private:
System::ComponentModel::Container ^components; array<Pen^>^ pen;
#pragma region Windows Form Designer generated code
void InitializeComponent(void)
{
this->SuspendLayout();
this->AutoScaleDimensions = System::Drawing::SizeF(6, 13); this->AutoScaleMode = System::Windows::Forms::AutoScaleMode::Font; this->ClientSize = System::Drawing::Size(292, 273);
this->Name = L"Form1";
this->Text = L"Drawing Some lines"; this->Paint +=
gcnew System::Windows::Forms::PaintEventHandler(this, &Form1::Form1_Paint);
this->ResumeLayout(false);
}
#pragma endregion
private:
System::Void Form1_Paint(System::Object^ sender, System::Windows::Forms::PaintEventArgs^ e)
{
Random ^rand = gcnew Random();
for (int i = 0; i < 10; i++)
{
e->Graphics->DrawLine(pen[i%5], rand->Next(0,299), rand->Next(0,299), rand->Next(0,299), rand->Next(0,299));
}
}
};
}
Figure 11-12 shows one instance of DrawingLines.exe running. I doubt you will ever see the same combination of lines being displayed twice.
The preceding code is pretty self-explanatory, with the help of the embedded comments, except for two things. The first is that you need to add the System::Drawing::Drawing2D namespace. This namespace defines both the DashStyle and LineCap classes.
486 |
C H A P T E R 1 1 ■ G R A P H I C S U S I N G G D I + |
pen[3] = gcnew Pen(Color::RoyalBlue, 10); array<float>^ cArray = gcnew array<float> {
0.0f, 0.1f, 0.3f, 0.7f, 0.9f, 1.0f
};
pen[3]->CompoundArray = cArray;
Brushes
You use brushes to fill in the objects that you drew with the pens you defined in the previous section. Unlike the Pen class, the Brush class is an abstract class. You don’t create objects directly from the Brush class; instead, brushes are created from classes derived from the Brush class such as SolidBrush,
HatchBrush, and TextureBrush.
You can also create named brushes and SystemBrushes. The Brushes class will fill a shape like the SolidBrush class. The only difference is that the brushes are predefined with names based on named colors.
Brush^ brush = Brushes::AliceBlue;
SystemBrushes are like the Brushes class, but instead of colors, the SystemBrushes are named based on the Windows role they would represent.
Brush^ brush = SystemBrushes:: ActiveBorder;
SolidBrush, HatchBrush, and TextureBrush are not the only brushes available, but I cover only them to give you some ideas on how to work with brushes.
Solid Brushes
The SolidBrush class is the easiest of the brushes. All it takes in its constructor is the color that you want to fill the shape with. Its only property with any relevance is the color you used in the constructor:
SolidBrush^ brush = gcnew SolidBrush(Color::Black);
Hatch Brushes
The HatchBrush class is a little more complicated than the SolidBrush class. First, you need to add the namespace System::Drawing::Drawing2D so that you can access the both the HatchBrush class and the HatchStyle enumeration. The HatchBrush uses the HatchStyle enumeration (see Table 11-13) to define the look of the brush. GDI+ provides numerous hatch styles.
Table 11-13. Ten of the Many HatchStyle Enumerations
Enumeration |
Description |
BackwardDiagonal |
Specifies a pattern of diagonal lines from the upper right to lower left |
Cross |
Specifies a pattern of vertical and horizontal lines |
DiagonalBrick |
Specifies a pattern that looks like slanted bricks |
Divots |
Specifies a pattern that looks like divots (a golfer’s nightmare) |
Horizontal |
Specifies a pattern of horizontal lines |
Plaid |
Specifies a pattern that looks like plaid |
SmallConfetti |
Specifies a pattern that looks like small confetti |
C H A P T E R 1 1 ■ G R A P H I C S U S I N G G D I + |
487 |
Table 11-13. Ten of the Many HatchStyle Enumerations
Enumeration |
Description |
Sphere |
Specifies a pattern of spheres laid adjacent to each other |
Vertical |
Specifies a pattern of vertical lines |
ZigZag |
Specifies a pattern of horizontal lines that looks like zigzags |
|
|
The constructor is a little more complicated too, as you need to pass the HatchStyle and two colors, the first being the foreground hatch color and the second being the background color.
using namespace System::Drawing::Drawing2D;
HatchBrush^ b = gcnew HatchBrush(HatchStyle::Divots,
Color::Brown, Color::Green);
Textured Brushes
A TextureBrush class allows you to place an image in the brush and then use it to fill in shapes. The best part of TextureBrush is how little code is needed to get it to work. The basic tasks behind the creation of a TextureBrush are loading the image and then placing it in the brush:
Image^ brushimage = gcnew Bitmap("MyImage.bmp");
TextureBrush^ tbrush = gcnew TextureBrush(brushimage);
Because I haven’t covered images yet, I defer their explanation until later in the chapter. But as you can see in the preceding constructor, once you have an image available, it is a simple process to place it into a TextureBrush.
But that is not where the story ends. What happens if the brush is smaller than the shape it is trying to fill? The TextureBrush provides a WrapMode parameter (see Table 11-14) in the constructor (and also a property) to determine what to do—either clamp it or tile it. Clamping means that only one copy of the image is drawn, and tiling means that the image is repeatedly drawn until the area is filled.
Table 11-14. WrapModes Enumeration
Enumeration |
Description |
Clamp |
Clamp the image to the object boundary |
Tile |
Tile the shape |
TileFlipX |
Tile the shape, flipping horizontally on each column |
TileFlipXY |
Tile the shape, flipping horizontally and vertically |
TileFlipY |
Tile the shape, flipping vertically on each row |
|
|
There is one more piece of the puzzle. The first brush starts in the upper-left corner of the control you are drawing in. Thus, if you are filling a rectangle, for instance, and you want the brush to start in the upper-left corner of the rectangle, then you need to call the Brush class’s TranslateTransform() method to translate the brush to start at that location: