Использование вложенных графических контейнеров

GDI+ предоставляет контейнеры, которые можно использовать для временной замены или дополнения части состояния в объекте Graphics. Контейнер создается путем вызова метода BeginContainer объекта Graphics. Для формирования вложенных контейнеров можно многократно вызывать BeginContainer. Каждый вызов BeginContainer должен быть сопряжен с вызовом EndContainer.

Преобразования во вложенных контейнерах

В приведенном ниже примере создается объект Graphics и контейнер внутри этого объекта Graphics. Преобразование мировых координат для объекта Graphics заключается в смещении на 100 единиц вдоль оси X и на 80 единиц вдоль оси Y. Преобразование мировых координат для контейнера — поворот на 30 градусов. Код вызывает DrawRectangle(pen, -60, -30, 120, 60) дважды. Первый вызов DrawRectangle находится внутри контейнера, то есть он осуществляется между вызовами BeginContainer и EndContainer. Второй вызов 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, то элементы, рисуемые внутри контейнера, будут отсечены по пересечению двух отсеченных областей.

Параметры качества во вложенных контейнерах

Параметры качества (SmoothingMode, TextRenderingHint и т. д.) во вложенных контейнерах не являются накопительными. Вместо этого параметры качества контейнера временно заменяют параметры качества объекта Graphics. При создании нового контейнера его параметры качества устанавливаются в значения по умолчанию. Например, предположим, имеется объект Graphics с режимом сглаживания AntiAlias. При создании контейнера он имеет режим сглаживания по умолчанию. Вы можете свободно задать режим сглаживания для контейнера, так что все элементы в нем будут рисоваться в соответствии с этим режимом. Элементы, рисуемые после вызова EndContainer, будут рисоваться в соответствии с режимом сглаживания (AntiAlias), который действовал перед вызовом BeginContainer.

Несколько уровней вложенных контейнеров

Вы не ограничены одним контейнером в объекте 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.

См. также