【IT168 技术文档】
来自于微软.net技术的C++托管扩展所包含的GDI+技术功能十分强大,本文将介绍如何使用GDI+的画刷来绘制文本。
一、使用画刷绘制文本的基本技术
本文程序允许用户定义所要显示的文本、字体尺寸、显示文本所用的画刷(网格画刷或渐变画刷)以及绘制文本的颜色等。下面是GDI+中使用渐变画刷或网格画刷绘制文本的基本步骤:
1、 在控件的绘制(Paint)事件中添加一个事件处理函数。在这个处理函数中进行绘制文本的相关处理,这样控件才能正确地进行重绘。
2、 获取一个图形(Graphics)对象。
正如我们所熟悉的设备上下文一样,图形对象是NET封装的一个绘制平面,例如,当在一个PictureBox控件上进行绘制时,可以调用PictureBox::CreateGraphics方法来获取一个Graphics对象,并在控件上绘图时使用这个图形(Graphics)对象。互联网上有很多例子都是这么做的,但是,有一个问题是,这样得到的Graphics对象不是永久对象,如果用户从当前程序转到另一个应用程序并再次返回时,这个控件将无法正确地进行重绘。所以,要得当图形对象应当使用传递给控件Paint方法的PaintEventArgs对象中的Graphics对象,代码如下所示:
private: System::Void picText_Paint(System::Object * sender,
System::Windows::Forms::PaintEventArgs * e)
{
...
Graphics* g = e->Graphics;
3、实例化一个字体对象
在字体类的13个构造函数中,最基本的一个构造函数只需要你提供字体名及字体的大小。在下面的例子中,创建了一个20点,"Times New Roman"类的常规字体:
using namespace System::Drawing;
...
Font* font = new Font(S"Times new Roman", 20, FontStyle::Regular);
4、测量将被绘制的文本尺寸
为了绘制文本需要使用Graphics::MeasureString方法来测量文本尺寸。可以使用Graphics::MeasureString方法来完成这个任务。这个方法需要提供被测量的文本及字体对象,并返回SizeF结构对象,这个结构包含了将要进行绘制文本的尺寸。
SizeF textSize = g->MeasureString(S"My Sample Text", font);
5、实例画刷对象
可以使用各种各样的画刷进行绘制文本,包括网格画刷、线性渐变画刷、路径渐变画刷、实体画刷及纹理画刷等,只是在创建各个不同的实例画刷时传递的参数有一些小小的不同而已。对各种画刷进行探讨不是本文的内容,在本文的实例中只使用两种画刷(网格画刷及线性渐变画刷)。
// HatchBrush example
Brush* brush = new HatchBrush(HatchStyle::Cross,
Color::Black, Color::Blue);
// LinearGradientBrush example
RectangleF* rect = __nogc new RectangleF(PointF(0, 0), textSize);
brush= new LinearGradientBrush(*rect, Color::Black, Color::Blue,
LinearGradientMode::ForwardDiagonal);
6、(选项)填充背景
为了使应用程序有特色,可以在绘制文本前对背景进行颜色填充,这有两个标准的方法。较简单的方法是调用Graphics::Clear方法并定义将使用的颜色;但是有时需要更高级的控制,这时候需要使用Graphics::FillRectange方法。
Graphics::FillRectange方法允许开发人员规定所选择的画刷对象并定义确切的矩形坐标位置。关于画刷对象,可以使用实例化的自定义画刷或者是系统画刷SystemBrushes,系统画刷定义了若干属性成员,它们是实心画刷,各自用来表现窗口的不同的元素,包括激活的边框及标题条等。
// Use the Windows-defined color for controls
// and explicitly state the rectangle coordinates
g->FillRectangle(SystemBrushes::Control, picText->Left, picText->Top,
picText->Right - picText->Left, picText->Bottom - picText->Top);
// Color the entire drawing surface using White
g->Clear(Color::White);
7、绘制文本
一旦将所有的GDI+对象实例化后,下面所需要做的事就是调用Graphics::DrawString方法。下面的例子使用了这个方法,在这个方法里规定了需要显示的文本、画刷和字体及显示文本的位置。
// Center the text on the drawing surface
g->DrawString(txtToDisplay->Text, font, brush,
(picText->Width - textSize.Width) / 2,
(picText->Height - textSize.Height) / 2);
二、实现文本的3D效果
这部分我们探讨如何实现阴影、块状、浮雕、雕刻文本效果。简单的说,上述文字效果是通过对文本的多次绘制来实现的。首先在背景上绘制最远的文本(也即是阴影),然后逐渐向最外层文本过度。换句话说,绘制3D文本就是多次绘制2D文本。这里,我将重点集中在如何实现3D效果的代码上。
注意:为了测试这些代码片段,首先要在托管扩展程序的窗体上放置一个PictureBox控件,并将该控件命名为picText,然后将代码拷贝到你的应用程序中,以此实现在PictureBox控件上绘制3D效果的文本。
1、阴影效果文本
为了取得阴影效果,你只需要绘制文本两次。首先是阴影文本然后是最前面的文本。例如,下面的代码绘制了一个阴影文本,阴影文本在前端文本后方5个像素处。
// Assumes a PictureBox on the form named picText
// with this code being the picText object’s
// Paint method
private:
System::Void picText_Paint( System::Object * sender,
System::Windows::Forms::PaintEventArgs * e)
{
// Test string
String* textToDisplay = S"Test string";
// Obtain Graphics object
Graphics* g = e->Graphics;
// Create a Font object, Times New Roman, 25pt
System::Drawing::Font* font = new System::Drawing::Font("Times New Roman",
Convert::ToSingle(25),FontStyle::Regular);
// Obtain the size of the text to be rendered
SizeF textSize = g->MeasureString(textToDisplay, font);
// Text will be centered on PictureBox control
Single x = (picText->Width - textSize.Width) / 2;
Single y = (picText->Height - textSize.Height) / 2;
// Clear background
g->Clear(Color::White);
// 注意:使用系统"光照"画刷绘制阴影文本
g->DrawString(textToDisplay, font, SystemBrushes::ControlLight, x + 5, y + 5);
// 使用系统默认的文本画刷绘制前端文本。
g->DrawString(textToDisplay, font, SystemBrushes::ControlText, x, y);
}
2、实现块状文本
为了实现块状文本效果,从希望的背景出开始一个像素一个像素地移动位置,并绘制文本。非常明显,这时需要确定重复绘制文本的方向。本文示例程序绘制文本时将光源设置在右上角,这意味着要使用for循环,并在X方面上减少偏移量。如果将光源移动到左上角,只需要反过来增加偏移量就可以了。
// Assumes a PictureBox on the form named picText
// with this code being the picText object’s
// Paint method
private:
System::Void picText_Paint( System::Object * sender,
System::Windows::Forms::PaintEventArgs * e)

...{
// Test string
String* textToDisplay = S"Test string";
// Get drawing surface for PictureBox and clear background
Graphics* g = e->Graphics;
// Create a Font object
System::Drawing::Font* font = new System::Drawing::Font("Times New Roman",
Convert::ToSingle(25), FontStyle::Regular);
// Obtain the size of the text to be rendered
SizeF textSize = g->MeasureString(textToDisplay, font);
// Text will be centered on Picture Box control
Single x = (picText->Width - textSize.Width) / 2;
Single y = (picText->Height - textSize.Height) / 2;
// Clear background
g->Clear(Color::White);
// 从背景处开始反复绘制阴影文本
for (int i = Convert::ToInt32(5); i >= 0; i--)

...{
g->DrawString(textToDisplay, font, SystemBrushes::ControlLight, x - i, y + i);
}
// 绘制前端文本
g->DrawString(textToDisplay, font, SystemBrushes::ControlText, x, y);
}
3、浮雕及雕刻文本
由于浮雕及雕刻是一种相反的效果,所以本文将这两种效果放在一节中进行解释。浮雕效果常常通过阴影文本技术来实现,深度设置为1个像素,前景文本的颜色设置为背景颜色。阴影的文本的颜色选择黑色或灰色。雕刻效果的步骤相反,阴影文本相对于前面的文本向左上方偏移一个像素。
// Assumes a PictureBox on the form named picText
// with this code being the picText object’s
// Paint method
private:
System::Void picText_Paint( System::Object * sender,
System::Windows::Forms::PaintEventArgs * e)
{
// Test string
String* textToDisplay = S"Test string";
// Get drawing surface for PictureBox and clear background
Graphics* g = e->Graphics;
// Create a Font object
System::Drawing::Font* font = new System::Drawing::Font("Times New Roman",
Convert::ToSingle(25), FontStyle::Regular);
// Obtain the size of the text to be rendered
SizeF textSize = g->MeasureString(textToDisplay, font);
// Text will be centered on Picture Box control
Single x = (picText->Width - textSize.Width) / 2;
Single y = (picText->Height - textSize.Height) / 2;
// Clear background
g->Clear(Color::White);
// isEmbossed变量用来决定浮雕或雕刻效果
bool isEmbossed = false;
g->DrawString(textToDisplay, font, SystemBrushes::ControlText,
x + Convert::ToSingle( (isEmbossed? 1 : -1)),
y + Convert::ToSingle( (isEmbossed ? 1 : -1)));
// Draw the foreground text
g->DrawString(textToDisplay, font, new SolidBrush(Color::White), x, y);
三、倾斜文本
1、存储倾斜参数
这里存在最终用户定义文本倾斜参数的问题,在这种情况下,定义一个变量存储这个值,然后调用PictureBox 控件的Paint 方法(例子代码中,是通过"显示文本"按钮的单击响应函数btnDisplayText_Click来调用这个方法)。有时你需要使用用户键入的其他值,如字体大小等。在例子程序中,代码如下:
public __gc class Form1 : public System::Windows::Forms::Form
{
...
protected:
String* textToDisplay;
Decimal fontSize;
Decimal shearSize;
...
private: System::Void btnDisplayText_Click(System::Object * sender,
System::EventArgs * e)
{
// Set up internal display values
textToDisplay = txtToDisplay->Text;
fontSize = spnFontSize->Value;
shearSize = spnShear->Value;
// Invalidate the control
picText->Invalidate();
}
2、实现PictureBox控件的Paint方法
文本将在PictureBox控件上进行绘制,所以实现Paint方法可以确保在需要的时候控件进行重绘。下面代码的中的条件语句确保用户已经输入了需要显示的文本。
private: System::Void picText_Paint(System::Object * sender,
System::Windows::Forms::PaintEventArgs * e)
{
if (textToDisplay)
{
}
}
下面步骤中的代码全部放入到PictureBox对象的Paint方法中。
3、获取picture控件的Graphics对象
可以通过PictureBox控件的CreateGraphics方法获取Graphics对象。然而,当Graphics对象超出作用域范围时,垃圾收集器将回收它。这时候,这个对象是不稳定的,所以,你需要的Graphics对象要从Paint 方法得到(经由PaintEventArgs::Graphics方法)。
Graphics* g = e->Graphics;
4、在用户提供的字体大小基础上实例化字体
这个程序使用默认的"Times New Roman"字体,所以下面的代码使用用户规定的字体大小创建Times New Roman"字体。
System::Drawing::Font* font = new System::Drawing::Font("Times New Roman",
Convert::ToSingle(fontSize), FontStyle::Regular);
5、获取所要显示的文本的尺寸
Graphics::MeasureString方法通常来测量字符串的尺寸,它将返回所要按某种字体显示的文本的尺寸。
SizeF textSize = g->MeasureString(textToDisplay, font);
6、对PictureBox控件进行清除操作
通过PictureBox::Clear方法来初始化PictureBox控件并定义所需要的颜色,下面的代码使用了用户为控件定义的值。
g->Clear(SystemColors::Control);
7、计算文本在PictureBox控件上显示的位置
下面的代码决定了居中显示文本坐标位置(X,Y)。
Single x = (picText->Width - textSize.Width) / 2;
Single y = (picText->Height - textSize.Height) / 2;
8、对PictureBox控件的平移矩阵进行平移
为了正确衡量文本,你必须重新衡量整个graphics对象,所以,首先必须重新定位最初的Graphics对象到(X,Y)处,你通过Graphics::TranslateTransform方法来完成上述任务。
g->TranslateTransform(x, y);
9、返回新产生的变换矩阵
一旦你对一个给定的PictureBox控件产生了一个"世界坐标的转换",它可以通过Graphics::Transform属性返回一个矩阵对象,这个矩阵将用来倾斜文本。
Matrix* transform = g->Transform;
10、使用用户定义的值倾斜转换矩阵
矩阵对象有一个Shear方法,这个方法就可以来实现文本倾斜。它使用水平及垂直因子,这个例子中只使用了水平倾斜值,但是这两个值可以非常简单地进行定义。然而需要注意的是,这种平移技术只有在一个值为零时才能实现真正的倾斜。
transform->Shear(Convert::ToSingle(shearSize), 0);
11、将新得到的转换矩阵赋于图形对象的转换矩阵
一旦本地的矩阵经过Shear方法进行了修改,就需要将Graphic对象的transform设置为这个新值。
g->Transform = transform;
12、绘制文本
既然已经设置了PictureBox控件的绘制区域,最后就可以调用Graphics::DrawString方法来绘制文本了。
g->DrawString(textToDisplay, font, Brushes::Black, 0, 0);
四、绘制反射文本
首先,实现PictureBox 控件的Paint事件。注意,在实现这种显示效果时,它前面的5个步骤与上例中一样,读者朋友可以参考上例。
1、对PictureBox控件的平移矩阵进行平移
不久,你就需要测量Graphics对象,这样才能绘制反向的文本。然而,测量影响到了整个Graphics对象,不仅仅是需要绘制的文本,所以,你需要将Graphics对象重新定位到你希望显示文本的位置。
g->TranslateTransform(x, y);
2、计算显示文本的高度
你需要在最初文本的下面显示反射文本,虽然你通过Measurestring方法得到文本的高度,但这个高度包括空白高度,而我们需要的是纯字体的高度,下面的代码得到了纯文本的高度。
int lineAscent = font->FontFamily->GetCellAscent(font->Style);
int lineSpacing = font->FontFamily->GetLineSpacing(font->Style);
Single lineHeight = font->GetHeight(g);
Single cy = lineHeight * lineAscent / lineSpacing;
3、绘制最初的文本
注意XY的坐标值在(0,0)处,这是由于你前期调用TranslateTransform方法产生的结果。
g->DrawString(textToDisplay, font, Brushes::Black, 0, 0);
4、对graphic对象的transformation 矩阵使用必须的比例因子
为了反射文本,你需要使用ScaleTransform方法,在这个方法中使用-1,使文本按次序进行反射。
g->ScaleTransform(1, -1.0F);
5、绘制反射文本
一旦比例因子建立以后,你只要简单地绘制反射文本就可以了,注意Y值被设置为文本高度的-2倍,这样它才能显示在文本的下方。
g->DrawString(textToDisplay, font, Brushes::Gray, 0, -(cy*2));