Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

Pro Visual C++-CLI And The .NET 2.0 Platform (2006) [eng]-1

.pdf
Скачиваний:
71
Добавлен:
16.08.2013
Размер:
24.18 Mб
Скачать

468

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 +

private:

System::Void Form1_MouseDown(System::Object^ sender, System::Windows::Forms::MouseEventArgs^ e)

{

//build a point from x,y coords of mouse click Point p = Point(e->X, e->Y);

//did we click in the intersection?

if (Rectangle::Intersect(rect1, rect2).Contains(p)) Text = "Intersection and Union";

// did we click in the union?

else if (Rectangle::Union(rect1, rect2).Contains(p)) Text = "Union";

// did we miss altogether else

Text = "Outside of Both";

}

};

}

The first thing you need to do is declare and build two rectangles that you will make the mouse check against:

Drawing::Rectangle rect1;

Drawing::Rectangle rect2;

//...

 

 

// Build the rectangles

from points and size

Drawing::Point point1 =

Drawing::Point(25,25);

Drawing::Point point2

=

Drawing::Point(100,100);

Drawing::Size size

=

Drawing::Size(200, 150);

rect1 = Drawing::Rectangle(point1, size); rect2 = Drawing::Rectangle(point2, size);

You will learn about the DrawRectangle() method later, but as you can see in the code, it takes a Pen to draw with and then the Rectangle to draw:

g->DrawRectangle(Pens::Black, rect1);

Finally, in the MouseDown event, you check to see where the mouse was clicked and place the results in the title:

//build a point from x,y coords of mouse click Point p = Point(e->X, e->Y);

//did we click in the intersection?

if (Rectangle::Intersect(rect1, rect2).Contains(p)) Text = "Intersection and Union";

// did we click in the union?

else if (Rectangle::Union(rect1, rect2).Contains(p)) Text = "Union";

// did we miss altogether? else

Text = "Outside of Both";

Figure 11-7 shows the mouse being clicked in the intersection of the two rectangles in InterOrUnion.exe.

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 +

469

Figure 11-7. It’s an intersection.

Region

The last of the utility types is the only class in the bunch. Region is a neat little class in that it alters itself with the help of other rectangles and regions into a more complex region. The alterations that the Region class does are things such as unions, intersections, exclusive or, and complements. A Region class has no properties of its own; instead, it is made up of a number of methods (see Table 11-8) that it uses to alter itself.

Table 11-8. Common Region Members

Member

Description

Complement()

Alters itself to become the complement of itself. The region of the

 

complement is restricted by a specified rectangle.

Exclude()

Alters itself to become the portion of the region that does not intersect with

 

the given rectangle or region.

GetBounds()

Specifies the smallest rectangle that the region can be contained within.

Intersect()

Alters itself to become the intersection of itself and a specified rectangle

 

or region.

IsEmpty()

Specifies whether the region is made up of an empty area.

IsInfinite()

Specifies whether the region is infinite in size.

MakeEmpty()

Sets the region to empty.

MakeInfinite()

Sets the region to infinite.

Transform()

Transforms itself using a matrix.

Translate()

Translates itself by a specified amount.

Union()

Alters itself to become the union of itself and a specified rectangle or region.

Xor()

Alters itself to become the exclusive or (the union minus the intersection) of

 

itself and a specified rectangle or region.

 

 

470

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 +

Listing 11-8 shows some of these methods in action.

Listing 11-8. Displaying a Region

namespace

RegionEx

{

 

using

namespace System;

using

namespace System::ComponentModel;

using

namespace System::Collections;

using

namespace System::Windows::Forms;

using

namespace System::Data;

using

namespace System::Drawing;

public ref class Form1 : public System::Windows::Forms::Form

{

public:

Form1(void)

{

InitializeComponent();

Drawing::Point point1 = Drawing::Point(25,25);

Drawing::Point point2

= Drawing::Point(100,100);

Drawing::Size size

= Drawing::Size(200, 150);

Rectangle

rect1

=

Drawing::Rectangle(point1, size);

Rectangle

rect2

=

Drawing::Rectangle(point2, size);

region = gcnew Drawing::Region(rect1); region->Xor(rect2);

}

protected:

~Form1()

{

if (components)

{

delete components;

}

}

private:

System::ComponentModel::Container ^components; Drawing::Region ^region;

#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(322, 273);

this->Name = L"Form1";

this->Text = L"Filling A Region";

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 +

471

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)

{

e->Graphics->FillRegion(Brushes::Blue, region);

}

};

}

To save typing, I decided to cut and paste the code to build the rectangle from the previous example.

To build a Region class, you start with an empty Region and then add a rectangle or a Region to it:

Drawing::Region ^region;

region = gcnew Drawing::Region(rect1);

Now you can start to alter the Region. Notice that the Region methods return void. In other words, the Region actually gets changed with each method call to itself. To Xor it with another rectangle, call the Xor() method:

region->Xor(rect2);

You will cover filling regions later, but so that you know, the FillRegion() method takes a Brush to specify the color to fill it with and then the Region to fill.

Figure 11-8 shows the area that makes up the region that you built with RegionEx.exe from two rectangles.

Figure 11-8. Displaying a region

472

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 +

Drawing Strings

Drawing strings almost doesn’t require a section of its own—all it involves is a single call to the DrawString() method found in the Graphics class. The more difficult part of drawing strings is setting up the font and color you want to print with. (I cover both topics later.)

Now you’ll take a quick peek at the DrawString() method. If you were to look at the .NET Framework documentation, you’d find a plethora of overloads. When you examine them more closely, you’ll discover that they all start with the parameters String, Font, and Brush. From there, it gets a little tricky because you have to decide if you just want to specify the starting upper-left corner of where you want the string displayed, using either (x, y) coordinates or a Point, or specify the entire rectangle that you want to restrict the string to.

g->DrawString(string, font, brush, xF, yF); g->DrawString(string, font, brush, pointF); g->DrawString(string, font, brush, rectangleF);

When you restrict the string to a rectangle, the text automatically word-wraps, as Listing 11-9 shows. It unfortunately will also show half of a line of text if the vertical height is not enough.

Listing 11-9. Drawing a String to a Rectangle

namespace

StringRect

{

 

using

namespace System;

using

namespace System::ComponentModel;

using

namespace System::Collections;

using

namespace System::Windows::Forms;

using

namespace System::Data;

using

namespace System::Drawing;

public ref class Form1 : public System::Windows::Forms::Form

{

public:

Form1(void)

{

InitializeComponent();

}

protected:

~Form1()

{

if (components)

{

delete components;

}

}

private:

System::ComponentModel::Container ^components;

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 +

473

#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"String in a Rectangle"; 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)

{

// Draw the string e->Graphics->DrawString(

"Let's draw a string to a rectangle and go a little " "overboard on the size of the string that we place " "inside of it",

gcnew Drawing::Font(gcnew FontFamily("Arial"), 12), Brushes::Black, Drawing::RectangleF(20.0, 40.0, 260.0, 50.0));

}

};

}

Figure 11-9 shows that StringRect.exe draws a string to a rectangle that is too small.

Figure 11-9. A string restricted to a too-small rectangle

In reality, each of the overloads for the DrawString() method listed previously has one more parameter of type StringFormat, which has been defaulted to GenericDefault.

g.DrawString(string, font, brush, xF, yF, stringformat); g.DrawString(string, font, brush, pointF, stringformat); g.DrawString(string, font, brush, rectangleF, stringformat);

474

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 +

StringFormat is a class containing several properties (see Table 11-9) that allow the DrawString() method to do things such as draw the text vertically and left-, right-, or center-align it.

Table 11-9. Common StringFormat Properties

Property

Description

Alignment

Specifies alignment of the text

FormatFlags

Specifies StringFormatFlags such as DirectionVertical and NoWrap

GenericDefault

A static method that gets the generic default StringFormat object

GenericTypographic

A static method that gets the generic typographic StringFormat object

LineAlignment

Specifies line alignment

Trimming

Specifies how to trim a string that doesn’t fit completely within a

 

display area

 

 

Listing 11-10 shows the same text as shown earlier, but this time it is written in a downward direction and centered on each line.

Listing 11-10. Drawing Strings Downward in a Rectangle

namespace

DownwardStringRect

{

 

using

namespace System;

using

namespace System::ComponentModel;

using

namespace System::Collections;

using

namespace System::Windows::Forms;

using

namespace System::Data;

using

namespace System::Drawing;

public ref class Form1 : public System::Windows::Forms::Form

{

public:

Form1(void)

{

InitializeComponent();

}

protected:

~Form1()

{

if (components)

{

delete components;

}

}

private:

System::ComponentModel::Container ^components;

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 +

475

#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(300, 145);

this->Name = L"Form1";

this->Text = L"Downward String in a Rectangle"; 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)

{

// create and configure the StringFormat object

StringFormat ^stringformat

= gcnew StringFormat();

stringformat->FormatFlags

=

StringFormatFlags::DirectionVertical;

stringformat->Alignment

=

StringAlignment::Center;

// Draw the string e->Graphics->DrawString(

"Let's draw a string to a rectangle and go a little " "overboard on the size of the string that we place " "inside of it",

gcnew Drawing::Font(gcnew FontFamily("Arial"), 13), Brushes::Black, Drawing::RectangleF(20.0, 40.0, 250.0, 80.0), stringformat);

}

};

}

Figure 11-10 shows that DownwardStringRect.exe draws a string in a downward direction and centers it in a rectangle that is too small. This causes the string to be clipped on the final line.

Figure 11-10. A string drawn downward and restricted to a too-small rectangle

476

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 +

Fonts

It seems that many people seem overly concerned about the differences between GDI+’s Font class and FontFamily class. Here’s my take on it: A Font class represents a single font and a FontFamily class represents a group of fonts that share many characteristics. You might think of a font family as “Arial” and a font as “Arial, 10-point, italic.”

When you draw strings with GDI+, you don’t have much of a choice. You have to build a Font class. You can’t draw a string with a FontFamily class.

When you build a Font class, you have the choice of starting with a FontFamily class or a String containing the name of a font family. You’ll probably use a String if you’re planning on building a Font class from one of the standard fonts found on a computer (e.g., Arial, Courier, and Times New Roman). On the other hand, if your font is a little less common, you probably will search the computer for a list of font families currently loaded on your computer. If you find the FontFamily class in the list of font families, then it’s a simple matter of using the FontFamily class instead of the String containing the font family’s name. In general, I don’t find the FontFamily class that useful as I tend to use the more common fonts in my programs, but you might have more exotic tastes. Basically, to use the FontFamily class, just replace the String in the first parameter of the Font constructor with the FontFamily class.

The process of building a font is quite easy. You do it using the Font constructors. You will use three constructors most often. They are really the same except that parameters are defaulted for two of them.

The first constructor has no defaulted values and takes the name of the font family and the unit size, the font style, and the graphics unit:

Font ^f = gcnew Drawing::Font("Arial", 16, FontStyle::Bold,GraphicsUnit::Point);

In most cases, fonts default to a graphics unit of pixels. Therefore, Font provides a constructor with the graphics unit defaulted to pixels:

Font ^f = gcnew Drawing::Font("Arial", 16, FontStyle::Bold);

In addition, most of the time you are going to work with the font in the regular font style (not boldface, italic, or underline). So, again, Font provides a default for this:

Font ^f = gcnew Drawing::Font("Arial", 16);

Even though the Font class has several properties (see Table 11-10), they are all read-only. In other words, you can’t change a font once you have constructed it.

Table 11-10. Common Font Properties

Property

Description

Bold

true if the font is boldface

FontFamily

Gets the font family

Height

Gets the height of the font in the current graphics unit

Italic

true if font is italicized

Name

Gets the name of the font

Size

Gets the size of the font in the current graphics unit

SizeInPoints

Gets the size of the font in points (1/72 inch)

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 +

477

Table 11-10. Common Font Properties

Property

Description

Strikeout

true if the font is struck out

Style

Gets the style information

Underline

true if the font is underlined

Unit

Gets the graphics unit

 

 

The code in Listing 11-11 creates ten random fonts and then displays them.

Listing 11-11. Generating Random Fonts

namespace

FontsGalore

{

 

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::Text;

public ref class Form1 : public System::Windows::Forms::Form

{

public:

Form1(void)

{

InitializeComponent();

fonts = gcnew array<Drawing::Font^>(10); fontstr = gcnew array<String^>(10);

// Used to generate random fonts array<float>^ sizes = gcnew array<float> {

10.0, 12.5, 16.0

};

array<FontStyle>^ fontstyles = gcnew array<FontStyle> { FontStyle::Regular, FontStyle::Bold, FontStyle::Italic,

(FontStyle)(FontStyle::Underline|FontStyle::Bold|FontStyle::Italic)

};

array<GraphicsUnit>^ units = gcnew array<GraphicsUnit> { GraphicsUnit::Point, GraphicsUnit::Pixel

};

// Get all fonts on computer InstalledFontCollection ^availFonts =

gcnew InstalledFontCollection();