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

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

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

488

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 +

//Translate brush to same start location as rectangle tbrush->TranslateTransform(25,25);

//Fill rectangle with brush

g->FillRectangle(tbrush, 25, 25, 250, 250);

Listing 11-13 shows the tiling of the TextureBrush using WrapMode::TileFlipXY. It also shows how to translate the starting point of the tiling to the upper-left corner of the shape you are trying to fill.

Listing 11-13. Filling with a TextureBrush

namespace

TextureBrushEx

{

 

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();

}

protected:

~Form1()

{

if (components)

{

delete components;

}

}

private:

System::ComponentModel::Container ^components;

#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"Texture Brush"; this->Paint +=

gcnew System::Windows::Forms::PaintEventHandler(this, &Form1::Form1_Paint);

this->ResumeLayout(false);

}

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 +

489

#pragma endregion

private:

System::Void Form1_Paint(System::Object^ sender, System::Windows::Forms::PaintEventArgs^ e)

{

// Load Image

Image^ bimage = gcnew Bitmap("Images\\CLICppCover.gif"); // Create brush

TextureBrush^ tbsh = gcnew TextureBrush(bimage, WrapMode::TileFlipXY);

//Translate brush to same start location as rectangle tbsh->TranslateTransform(25,25);

//Fill rectangle with brush

e->Graphics->FillRectangle(tbsh, 25, 25, 250, 250);

}

};

}

Figure 11-13 shows TextureBrushEx.exe in action. Remember to make sure that the bitmap file is in the Images directory off the current executable starting directory so the program can find it. If it is not, then the program will abort.

Figure 11-13. Displaying the tiled TextureBrush

Rendering Prebuilt Images

If you are implementing GDI+, you are probably planning to do one of two things: Render an existing image or draw your own image. You will cover rendering an existing image first, as it is the easier of the two processes.

Here’s the process in a nutshell. Load the image. Draw the image. That’s it. And it can be done in one line, too!

g->DrawImageUnscaled(Image::FromFile("Images\\CLICppCover.jpg"), 0.0, 0.0);

490

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 +

Of course, if you want a little more control, there is another DrawImage() method that you can work with. The Image class has a few members (see Table 11-15) with which you can manipulate the image.

Table 11-15. Common Image Class Members

Member

Description

FromFile()

Static method to load an image from a file

FromHbitmap()

Static method to load a bitmap from a Windows handle

FromStream()

Static method to load an image from a stream

GetBounds()

Returns a bounding rectangle for the image

Height

Specifies the height of the image

HorizontalResolution

Specifies the horizontal resolution of the image in pixels per inch

PhysicalDimensions

Specifies the size of the image

RotateFlip()

Rotates, flips, or rotates and flips the image

Save()

Saves the file to a stream

Size

Specifies the size of the image

VerticalResolution

Specifies the vertical resolution of the image in pixels per inch

Width

Specifies the width of the image

 

 

Before you can render an image, you need to load it from some source, either from a file as shown previously or a data stream (maybe the Internet?). Once the image is loaded, the Image class provides you the ability to flip and rotate the image.

Note The Image class doesn’t use the GraphicsUnit, as you might expect. Instead, it uses pixels per inch.

Once you have an image, you’re ready to render it. You’ve seen the Graphics class’s DrawImageUnscaled() method. That is about the extent of the functionality it provides. It can take an image and the location where you want to place it. A more flexible rendering method is DrawImage(). It takes myriad overloads (you can examine them at your leisure within the .NET Framework documentation), but the most useful overload takes the image and stretches it to the size you want (see Listing 11-14).

Listing 11-14. Stretching an Image

namespace

DrawImageEx

{

 

using

namespace System;

using

namespace System::ComponentModel;

using

namespace System::Collections;

using

namespace System::Windows::Forms;

using

namespace System::Data;

using

namespace System::Drawing;

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 +

491

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

{

public:

Form1(void)

{

InitializeComponent();

}

protected:

~Form1()

{

if (components)

{

delete components;

}

}

private:

System::ComponentModel::Container ^components;

#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"Draw Image"; 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)

{

Image^ img = Image::FromFile("Images\\CLICppCover.gif"); e->Graphics->DrawImage(img, 0, 0, img->Width*2, img->Height*2);

}

};

}

Figure 11-14 shows the end result of DrawImageEx.exe, which doubles the image with the DrawImage() method. It is a little blurry but not too bad.

492

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 +

Figure 11-14. Doubling an image’s size

One last note about rendering images. So far you have only loaded images from files of type .gif, but you can actually load .bmp, .jpg, .png, and .tif image files without having to change a single line of code other than the name of the file.

Drawing Your Own Shapes and Lines

Now you can finally get to the fun part of GDI+: drawing your own images. You saw some of this in action earlier in the chapter. The steps involved are quite easy: Grab the Graphics class and then draw or fill the objects you want using the appropriate method. I listed all the methods you will likely use back in Table 11-3, so you might want to take a quick peek back there to refresh your memory.

Because all it really takes to draw an image is calling methods, let’s create a simple piece of artwork with the example in Listing 11-15.

Listing 11-15. A Piece of Art

namespace

HappyFace

{

 

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();

}

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 +

493

protected:

~Form1()

{

if (components)

{

delete components;

}

}

private:

System::ComponentModel::Container ^components;

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

this->Name = L"Form1"; this->Text = L"Happy Face"; 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)

{

Graphics^ g = e->Graphics;

Pen^ b4pen = gcnew Pen(Color::Black, 4);

// Head

Rectangle rect = Drawing::Rectangle(25, 25, 250, 250); g->FillEllipse(Brushes::Yellow, rect); g->DrawEllipse(b4pen, rect);

// Mouth

g->FillPie(Brushes::White, 100, 175, 100, 50, 0, 180); g->DrawPie(b4pen, 100, 175, 100, 50, 0, 180);

// Left Eye

rect = Drawing::Rectangle(100, 100, 25, 25); g->FillEllipse(Brushes::White, rect); g->DrawEllipse(b4pen, rect);

494

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 +

// Right Eye

rect = Drawing::Rectangle(175, 100, 25, 25); g->FillEllipse(Brushes::White, rect); g->DrawEllipse(b4pen, rect);

// Get rid of pen Created delete b4pen;

}

};

}

Figure 11-15 shows the results of HappyFace.exe, which is about the limit of my artistic abilities.

Figure 11-15. A happy face

Advanced GDI+

I kind of like the happy face I created in the last section, so I’ll get a little more mileage out of it by using it to demonstrate a few more advanced GDI+ topics: scrollable windows, optimizing, and double buffering. By “advanced,” I don’t mean difficult—rather, I mean less obvious in how to implement. All three topics aren’t that hard to implement.

Scrollable Windows

In the previous chapter on Win Forms, you didn’t have to worry about a scrolling window as the Win Form handled it itself. With GDI+, on the other hand, it’s up to you to add the necessary two lines in your code to get the scrollable window to work. Yep, you read correctly: two lines of code.

For those of you who aren’t sure what a scrollable window is, it’s a window that automatically attaches scroll bars to itself when the display information extends beyond its width. You use the scroll bar to shift the display area over so you can view this obscured displayed information.

To enable automatic scroll bars in a form, you need to update the AutoScrollMinSize property for the form:

this->AutoScrollMinSize = System::Drawing::Size(400, 400);

The size that you need to specify is the smallest area needed to display all the information. In my case, I was a little overzealous on the size so that you can see the scrolling better.

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 +

495

When you add the preceding line to your previous happy face example, you get scroll bars as shown in Figure 11-16, and everything seems hunky-dory.

Figure 11-16. A happy face in a scrollable window

Or is it? When you try to scroll the window, you get nothing but garbage, as you can see in Figure 11-17.

Figure 11-17. A not-so-happy happy face in a scrollable window

What’s happening here? Believe it or not, the program is functioning perfectly—just not how you want it to. You can find the problem in the Paint event handler. The following steps show how the current program is working:

1.You click the scroll bar.

2.The window scrolls.

3.The Invalidate event is triggered for the clip area of the newly exposed window.

4.The Paint event handler executes.

5.The newly exposed window is replaced with any display data that belongs in it.

Sounds like it’s working correctly to me, except for one minor detail. How does the program know what belongs in the newly exposed clip area? Notice that all the points in each of the drawing

496

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 +

routines haven’t been notified that the scroll took place. They’re still drawing the same information at the same locations. Thus, the window is just repainting the newly exposed clip area with the original and wrong display information.

You have two (at least) ways of solving this problem. You might try adjusting each of the drawing routines by the amount of the scroll so that when they’re called they render correctly. This solution isn’t so bad when you’re dealing with a handful of drawing and filling routines, but it’s not good for a large number of routines.

An easier solution is to translate the origin of the Graphics class using the TranslateTransform() method (which I discussed earlier) to reflect the scroll. This solution has the same effect as the previous solution. The best part is that you have to add only one line of code, instead of changing every draw and fill routine. (Told you it would take two lines of code!)

g->TranslateTransform((float)AutoScrollPosition.X,(float)AutoScrollPosition.Y);

It’s also fortunate that the Form class provides a property, AutoScrollPosition, which indicates how much was scrolled.

Listing 11-16 shows the happy face program modified to handle scroll bars.

Listing 11-16. A Scrolling Happy Face

namespace

ScrollingHappyFace

{

 

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;

#pragma region Windows Form Designer generated code void InitializeComponent(void)

{

this->SuspendLayout();

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 +

497

this->AutoScrollMinSize = System::Drawing::Size(400,400);

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"Scrolling Happy Face"; 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)

{

Graphics^ g = e->Graphics; g->TranslateTransform((float)AutoScrollPosition.X,

(float)AutoScrollPosition.Y);

Pen^ b4pen = gcnew Pen(Color::Black, 4);

// Head

Rectangle rect = Drawing::Rectangle(25, 25, 250, 250); g->FillEllipse(Brushes::Yellow, rect); g->DrawEllipse(b4pen, rect);

// Mouth

g->FillPie(Brushes::White, 100, 175, 100, 50, 0, 180); g->DrawPie(b4pen, 100, 175, 100, 50, 0, 180);

// Left Eye

rect = Drawing::Rectangle(100, 100, 25, 25); g->FillEllipse(Brushes::White, rect); g->DrawEllipse(b4pen, rect);

// Right Eye

rect = Drawing::Rectangle(175, 100, 25, 25); g->FillEllipse(Brushes::White, rect); g->DrawEllipse(b4pen, rect);

// Get rid of pen Created delete b4pen;

}

};

}

Figure 11-18 shows a happily scrolled happy face.