使用嵌套的 Graphics 容器

GDI+ 提供了一些容器,你可以使用它们暂时替换或增加 Graphics 对象中的部分状态。 通过调用 Graphics 对象的 BeginContainer 方法创建容器。 可以反复调用 BeginContainer 来形成嵌套容器。 对 BeginContainer 的每次调用必须与对 EndContainer 的调用配对。

嵌套容器中的转换

下面的示例创建了一个 Graphics 对象,并在 Graphics 对象中创建了一个容器。 Graphics 对象的世界转换是在 x 方向上平移 100 个单位,在 y 方向上平移 80 个单位。 容器的世界转换是 30 度旋转。 代码两次调用 DrawRectangle(pen, -60, -30, 120, 60)。 第一次调用 DrawRectangle 发生在容器内部;也就是说,该调用发生在对 BeginContainerEndContainer 的调用之间。 第二次调用 DrawRectangle 发生对 EndContainer 的调用之后。

Graphics graphics = e.Graphics;
Pen pen = new Pen(Color.Red);
GraphicsContainer graphicsContainer;
graphics.FillRectangle(Brushes.Black, 100, 80, 3, 3);

graphics.TranslateTransform(100, 80);

graphicsContainer = graphics.BeginContainer();
graphics.RotateTransform(30);
graphics.DrawRectangle(pen, -60, -30, 120, 60);
graphics.EndContainer(graphicsContainer);

graphics.DrawRectangle(pen, -60, -30, 120, 60);
Dim graphics As Graphics = e.Graphics
Dim pen As New Pen(Color.Red)
Dim graphicsContainer As GraphicsContainer
graphics.FillRectangle(Brushes.Black, 100, 80, 3, 3)

graphics.TranslateTransform(100, 80)

graphicsContainer = graphics.BeginContainer()
graphics.RotateTransform(30)
graphics.DrawRectangle(pen, -60, -30, 120, 60)
graphics.EndContainer(graphicsContainer)

graphics.DrawRectangle(pen, -60, -30, 120, 60)

在前面的代码中,从容器内部绘制的矩形首先通过容器的世界转换(旋转)进行转换,然后通过 Graphics 对象的世界转换(平移)进行转换。 从容器外部绘制的矩形仅通过 Graphics 对象的世界转换(平移)进行转换。 下图显示了两个新矩形:

Illustration that shows nested containers.

嵌套容器中的剪切

以下示例演示嵌套容器如何处理剪切区域。 该代码创建了一个 Graphics 对象,并在 Graphics 对象中创建了一个容器。 Graphics 对象的剪切区域是矩形,容器的剪切区域是椭圆形。 该代码对 DrawLine 方法进行了两次调用。 第一次调用 DrawLine 发生在容器内部,第二次调用 DrawLine 发生在容器外部(调用 EndContainer 之后)。 第一行以两个剪切区域的交集进行剪切。 第二行仅以 Graphics 对象的矩形剪切区域进行剪切。

Graphics graphics = e.Graphics;
GraphicsContainer graphicsContainer;
Pen redPen = new Pen(Color.Red, 2);
Pen bluePen = new Pen(Color.Blue, 2);
SolidBrush aquaBrush = new SolidBrush(Color.FromArgb(255, 180, 255, 255));
SolidBrush greenBrush = new SolidBrush(Color.FromArgb(255, 150, 250, 130));

graphics.SetClip(new Rectangle(50, 65, 150, 120));
graphics.FillRectangle(aquaBrush, 50, 65, 150, 120);

graphicsContainer = graphics.BeginContainer();
// Create a path that consists of a single ellipse.
GraphicsPath path = new GraphicsPath();
path.AddEllipse(75, 50, 100, 150);

// Construct a region based on the path.
Region region = new Region(path);
graphics.FillRegion(greenBrush, region);

graphics.SetClip(region, CombineMode.Replace);
graphics.DrawLine(redPen, 50, 0, 350, 300);
graphics.EndContainer(graphicsContainer);

graphics.DrawLine(bluePen, 70, 0, 370, 300);
Dim graphics As Graphics = e.Graphics
Dim graphicsContainer As GraphicsContainer
Dim redPen As New Pen(Color.Red, 2)
Dim bluePen As New Pen(Color.Blue, 2)
Dim aquaBrush As New SolidBrush(Color.FromArgb(255, 180, 255, 255))
Dim greenBrush As New SolidBrush(Color.FromArgb(255, 150, 250, 130))

graphics.SetClip(New Rectangle(50, 65, 150, 120))
graphics.FillRectangle(aquaBrush, 50, 65, 150, 120)

graphicsContainer = graphics.BeginContainer()
' Create a path that consists of a single ellipse.
Dim path As New GraphicsPath()
path.AddEllipse(75, 50, 100, 150)

' Construct a region based on the path.
Dim [region] As New [Region](path)
graphics.FillRegion(greenBrush, [region])

graphics.SetClip([region], CombineMode.Replace)
graphics.DrawLine(redPen, 50, 0, 350, 300)
graphics.EndContainer(graphicsContainer)

graphics.DrawLine(bluePen, 70, 0, 370, 300)

下图显示了两个经过剪切的行:

Illustration that shows a nested container with clipped lines.

如前两个示例所示,转换和剪切区域在嵌套容器中累积。 如果设置了容器和 Graphics 对象的世界转换,则这两种转换都将应用于从容器内部绘制的项。 首先将应用容器的转换,然后再应用 Graphics 对象的转换。 如果设置了容器和 Graphics 对象的剪切区域,则将以两个剪切区域的交集剪切从容器内部绘制的项。

嵌套容器中的质量设置

嵌套容器中的质量设置(SmoothingModeTextRenderingHint 等)不是累积的;相反,容器的质量设置将暂时替换 Graphics 对象的质量设置。 新建容器时,该容器的质量设置将设置为默认值。 例如,假设你有一个 Graphics 对象采用 AntiAlias 平滑模式。 创建容器时,容器内的平滑模式是默认的平滑模式。 你可以自由设置容器的平滑模式,从容器内绘制的任何项都将根据你设置的模式进行绘制。 调用 EndContainer 之后绘制的项将根据调用 BeginContainer 之前设置的平滑模式 (AntiAlias) 进行绘制。

嵌套容器的几个层

你可以在 Graphics 对象中创建多个容器。 可以创建一系列容器,每个容器都嵌套在前面的容器中,还可以指定每个嵌套容器的世界转换、剪切区域和质量设置。 如果从最内层的容器内部调用绘图方法,则将按顺序应用转换,从最内层的容器开始,到最外层的容器结束。 从最内层的容器中绘制的项将以所有剪切区域的交集进行剪切。

以下示例创建一个 Graphics 对象并将其文本呈现提示设置为 AntiAlias。 该代码创建两个容器,一个容器嵌套在另一个容器中。 外部容器的文本呈现提示设置为 SingleBitPerPixel,内部容器的文本呈现提示设置为 AntiAlias。 该代码绘制三个字符串:一个来自内部容器,一个来自外部容器,一个来自 Graphics 对象本身。

Graphics graphics = e.Graphics;
GraphicsContainer innerContainer;
GraphicsContainer outerContainer;
SolidBrush brush = new SolidBrush(Color.Blue);
FontFamily fontFamily = new FontFamily("Times New Roman");
Font font = new Font(fontFamily, 36, FontStyle.Regular, GraphicsUnit.Pixel);

graphics.TextRenderingHint = System.Drawing.Text.TextRenderingHint.AntiAlias;

outerContainer = graphics.BeginContainer();

graphics.TextRenderingHint = System.Drawing.Text.TextRenderingHint.SingleBitPerPixel;

innerContainer = graphics.BeginContainer();
graphics.TextRenderingHint = System.Drawing.Text.TextRenderingHint.AntiAlias;
graphics.DrawString(
   "Inner Container",
   font,
   brush,
   new PointF(20, 10));
graphics.EndContainer(innerContainer);

graphics.DrawString(
   "Outer Container",
   font,
   brush,
   new PointF(20, 50));

graphics.EndContainer(outerContainer);

graphics.DrawString(
   "Graphics Object",
   font,
   brush,
   new PointF(20, 90));
Dim graphics As Graphics = e.Graphics
Dim innerContainer As GraphicsContainer
Dim outerContainer As GraphicsContainer
Dim brush As New SolidBrush(Color.Blue)
Dim fontFamily As New FontFamily("Times New Roman")
Dim font As New Font( _
   fontFamily, _
   36, _
   FontStyle.Regular, _
   GraphicsUnit.Pixel)

graphics.TextRenderingHint = _
System.Drawing.Text.TextRenderingHint.AntiAlias

outerContainer = graphics.BeginContainer()

graphics.TextRenderingHint = _
    System.Drawing.Text.TextRenderingHint.SingleBitPerPixel

innerContainer = graphics.BeginContainer()
graphics.TextRenderingHint = _
    System.Drawing.Text.TextRenderingHint.AntiAlias
graphics.DrawString( _
   "Inner Container", _
   font, _
   brush, _
   New PointF(20, 10))
graphics.EndContainer(innerContainer)

graphics.DrawString("Outer Container", font, brush, New PointF(20, 50))

graphics.EndContainer(outerContainer)

graphics.DrawString("Graphics Object", font, brush, New PointF(20, 90))

下图显示了这三个字符串。 从内部容器和 Graphics 对象中绘制的字符串通过抗锯齿技术进行平滑处理。 从外部容器绘制的字符串不会通过抗锯齿技术来进行平滑处理,因为 TextRenderingHint 属性设置为 SingleBitPerPixel

Illustration that shows the strings drawn from nested containers.

另请参阅