📄 ch07.htm
字号:
}
void __fastcall TForm1::FormMouseUp(TObject *Sender, TMouseButton Button,
TShiftState Shift, int X, int Y)
{
if (FDrawing)
{
FDrawing = False;
FShapeRect.Right = X;
FShapeRect.Bottom = Y;
PaintBox1->Canvas->Pen->Mode = pmCopy;
MyCanvas->Pen->Mode = pmCopy;
DrawShape();
}
}
void __fastcall TForm1::DrawShape()
{
PaintBox1->Canvas->Brush->Color = FBrushColor;
PaintBox1->Canvas->Brush->Style = bsSolid;
PaintBox1->Canvas->Pen->Color = FPenColor;
PaintBox1->Canvas->Pen->Width = FPenThickness;
MyCanvas->Brush->Color = FBrushColor;
MyCanvas->Brush->Style = bsSolid;
MyCanvas->Pen->Color = FPenColor;
MyCanvas->Pen->Width = FPenThickness;
switch
(FCurrentShape)
{
case csLine:
MyCanvas->MoveTo(FShapeRect.Left, FShapeRect.Top);
MyCanvas->LineTo(FShapeRect.Right, FShapeRect.Bottom);
PaintBox1->Canvas->MoveTo(FShapeRect.Left, FShapeRect.Top);
PaintBox1->Canvas->LineTo(FShapeRect.Right, FShapeRect.Bottom);
break;
case csRectangle:
PaintBox1->Canvas->Rectangle(FShapeRect.Left, FShapeRect.Top,
FShapeRect.Right,
FShapeRect.Bottom);
MyCanvas->Rectangle(FShapeRect.Left, FShapeRect.Top,
FShapeRect.Right, FShapeRect.Bottom);
break;
case csEllipse:
PaintBox1->Canvas->Ellipse(FShapeRect.Left,
FShapeRect.Top,
FShapeRect.Right, FShapeRect.Bottom);
MyCanvas->Ellipse(FShapeRect.Left, FShapeRect.Top,
FShapeRect.Right, FShapeRect.Bottom);
break;
default:
;
}
}
void __fastcall TForm1::FormMouseMove(TObject *Sender,
TShiftState Shift, int X, int Y)
{
if (FDrawing)
{
PaintBox1->Canvas->Pen->Mode = pmNotXor;
MyCanvas->Pen->Mode =
pmNotXor;
if (FShapeRect.Right != -32000)
DrawShape();
FShapeRect.Right = X;
FShapeRect.Bottom = Y;
DrawShape();
}
}
void __fastcall TForm1::SpeedButton1Click(TObject *Sender)
{
FCurrentShape =
TCurrentShape(dynamic_cast<TSpeedButton *>(Sender)->Tag);
}
void __fastcall TForm1::Brush1Click(TObject *Sender)
{
ColorDialog1->Color = FBrushColor;
if (ColorDialog1->Execute())
FBrushColor = ColorDialog1->Color;
}
void __fastcall TForm1::Pen1Click(TObject *Sender)
{
ColorDialog1->Color = FPenColor;
if (ColorDialog1->Execute())
FPenColor = ColorDialog1->Color;
}
void __fastcall TForm1::SpeedButton5Click(TObject *Sender)
{
FPenThickness = dynamic_cast<TSpeedButton *>(Sender)->Tag;
}
void __fastcall TForm1::New1Click(TObject *Sender)
{
delete MyCanvas;
delete MyMetafile;
MyMetafile = new TMetafile;
MyCanvas = new TMetafileCanvas(MyMetafile,
0);
InvalidateRect(Handle, NULL, True);
}
void __fastcall TForm1::PaintBox1Paint(TObject *Sender)
{
delete MyCanvas;
if (MyMetafile)
PaintBox1->Canvas->Draw(0,0,MyMetafile);
MyCanvas = new TMetafileCanvas(MyMetafile,
PaintBox1->Canvas->Handle);
MyCanvas->Draw(0, 0, MyMetafile);
}
void __fastcall TForm1::FormDestroy(TObject *Sender)
{
delete MyCanvas;
}
void __fastcall TForm1::Exit1Click(TObject *Sender)
{
Close();
}
void __fastcall
TForm1::Save1Click(TObject *Sender)
{
if (SaveDialog1->Execute())
{
delete MyCanvas;
MyMetafile->SaveToFile(SaveDialog1->FileName);
MyCanvas = new TMetafileCanvas(MyMetafile, 0);
MyCanvas->Draw(0, 0, MyMetafile);
}
}
void __fastcall TForm1::Open1Click(TObject *Sender)
{
if (OpenDialog1->Execute())
{
FShapeRect = Rect(0, 0, 0, 0);
delete MyCanvas;
TFileStream *Stream = new TFileStream(OpenDialog1->FileName, fmOpenRead);
MyMetafile->LoadFromStream(Stream);
MyCanvas = new TMetafileCanvas(MyMetafile, PaintBox1->Canvas->Handle);
MyCanvas->Draw(0, 0, MyMetafile);
PaintBox1->Canvas->Draw(0, 0, MyMetafile);
Stream->Free();
}
}
</FONT></PRE>
<P>This program works just like the BitmapShapes program shown previously, in that
it lets you draw colored shapes to the screen and then save them to file. A screenshot
of the program is shown in Figure 7.3.<BR>
<BR>
<A
NAME="Heading21"></A><A HREF="07ebu03.jpg" tppabs="http://pbs.mcp.com/ebooks/0672310228/art/07/07ebu03.jpg">FIGURE 7.3.</A><FONT COLOR="#000077">
</FONT><I>The MetaShapes program in action.</I></P>
<P>When working with metafiles, you need to create both a <TT>TMetafile</TT> object
and a
<TT>TMetafileCanvas</TT> object.</P>
<PRE><FONT COLOR="#0066FF">MyMetafile = new TMetafile;
MyCanvas = new TMetafileCanvas(MyMetafile, PaintBox1->Canvas->Handle);
</FONT></PRE>
<P>Notice that the <TT>TMetafileCanvas</TT> is explicitly
associated with a particular
<TT>TMetafile</TT> object. Specifically, the <TT>TMetaFile</TT> object is passed
in as the first parameter of the <TT>TMetaFileCanvas</TT> object's constructor. These
two classes are designed to work in tandem, and both
pieces must be present if you
want to create a metafile.</P>
<P>Here is a simple example of how to draw into a <TT>TMetafile</TT> canvas:</P>
<PRE><FONT COLOR="#0066FF">MyCanvas->Ellipse(FShapeRect.Left, FShapeRect.Top,
FShapeRect.Right, FShapeRect.Bottom);
</FONT></PRE>
<P>This code looks exactly like any other call to the <TT>TCanvas::Ellipse</TT>,
only this time I am writing into a <TT>TMetafileCanvas</TT> rather than a form's
<TT>Canvas</TT>.
<DL>
<DT></DT>
</DL>
<BLOCKQUOTE>
<P>
<HR>
<FONT COLOR="#000077"><B>NOTE:</B></FONT><B> </B>The VCL preserves the <TT>Canvas</TT>
metaphor in a wide variety of contexts, such as when you are working with metafiles
or bitmaps. This enables you to learn one set
of commands, and to then apply them
to a wide variety of objects. As you will see later in the book, this type of functionality
derives in part from a judicious and intelligent use of polymorphism. <BR>
<BR>
Basing a suite of objects on one model
enables users to quickly get up to speed on
new technologies. The underlying code for creating bitmaps is very different from
the code for creating metafiles. But the <TT>TCanvas</TT> object enables you to treat
each operation as if it were nearly
identical. People who are interested in object
design should contemplate the elegance of this implementation.
<HR>
</BLOCKQUOTE>
<P>If you only want to display a metafile, you can work solely with the <TT>TMetafile</TT>
object. However, if you
want to create metafiles, to draw images into a metafile,
you need a <TT>TMetafileCanvas</TT> object. You can draw directly into a <TT>TMetafileCanvas</TT>,
but when you want to display the image to the user, you need to delete the object
so that its
contents will be transferred into a metafile that can be displayed for
the user:</P>
<PRE><FONT COLOR="#0066FF">delete MyCanvas;
PaintBox1->Canvas->Draw(0,0,MyMetafile);
</FONT></PRE>
<P>In this code I am using a <TT>PaintBox</TT> as the
surface on which to display
the metafile. The code first deletes the <TT>TMetafileCanvas</TT>, thereby transferring
the painting from the <TT>TMetafileCanvas</TT> to the <TT>TMetafile</TT>. This implementation
is, I suppose, a bit awkward, but once
you understand the principle involved, it
should not cause you any serious difficulty.</P>
<P>There is no particular connection between metafiles and the <TT>TPaintBox</TT>
object. In fact, I could just as easily have painted directly into a
<TT>TForm</TT>.
The reason I chose <TT>TPaintBox</TT> is that it enables me to easily define a subsection
of a form that can be used as a surface on which to paint. In particular, part of
the form in the <TT>MetaShapes</TT> program is covered by a
<TT>TPanel</TT> object.
To make sure that the user can see the entire canvas on which he will be painting,
I used a <TT>TPaintBox</TT>.</P>
<P>If you are interested in these matters, you might want to open up <TT>EXTCTRLS.HPP</TT>
and compare the
declarations for <TT>TPaintBox</TT> and <TT>TImage</TT>. Both of
these controls are descendants of <TT>TGraphicControl</TT>, and both offer similar
functionality. The big difference between them is that <TT>TImage</TT> has an underlying
bitmap, while
<TT>TPaintBox</TT> has a simpler, sparer architecture.</P>
<P>By now it has probably occurred to you that there is no simple means for displaying
a metafile to the user at the time it is being created. In particular, you can't
show it to the user
without first deleting the <TT>TMetafileCanvas</TT>. To avoid
performing this action too often, I simply record the user's motions into both the
<TT>TMetafileCanvas</TT> and the <TT>Canvas</TT> for the main form:</P>
<PRE><FONT
COLOR="#0066FF">PaintBox1->Canvas->Brush->Color = FBrushColor;
... // Code ommitted
MyCanvas->Brush->Color = FBrushColor;
...// Code ommitted
switch (FCurrentShape)
{
case csLine:
MyCanvas->MoveTo(FShapeRect.Left,
FShapeRect.Top);
MyCanvas->LineTo(FShapeRect.Right, FShapeRect.Bottom);
PaintBox1->Canvas->MoveTo(FShapeRect.Left, FShapeRect.Top);
PaintBox1->Canvas->LineTo(FShapeRect.Right, FShapeRect.Bottom);
break;
... //
etc
</FONT></PRE>
<P>This duplication of code, while bothersome, is not really terribly costly in terms
of what it is adding to the size of my executable. Needless to say, I use this technique
so that the user can see what he or she is painting.</P>
<P>If the user flips away from the program and covers it with another application,
I need to repaint the image when the user flips back to <TT>MetaShapes</TT>:</P>
<PRE><FONT COLOR="#0066FF">void __fastcall TForm1::PaintBox1Paint(TObject *Sender)
{
delete MyCanvas;
PaintBox1->Canvas->Draw(0,0,MyMetafile);
MyCanvas = new TMetafileCanvas(MyMetafile, PaintBox1->Canvas->Handle);
MyCanvas->Draw(0, 0, MyMetafile);
}
</FONT></PR
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -