Contenedores de gráficos

El estado de los gráficos (región de recorte, transformaciones y valores de calidad) se almacena en un objeto Graphics. GDI+ permite reemplazar o aumentar temporalmente parte del estado en un objeto Graphics por medio de un contenedor. Para iniciar un contenedor, hay que llamar al método BeginContainer de un objeto Graphics y, para finalizar un contenedor, hay que llamar al método EndContainer. Cualquier cambio de estado que se realice en el objeto Graphics, entre BeginContainer y EndContainer, pertenece al contenedor y no sobrescribe el estado existente del objeto Graphics.

En el siguiente ejemplo se crea un contenedor en un objeto Graphics. La transformación universal del objeto Graphics es una traslación de 200 unidades hacia la derecha, y la transformación de coordenadas universales del contenedor es una traslación de 100 unidades hacia abajo.

myGraphics.TranslateTransform(200, 0)

myGraphicsContainer = myGraphics.BeginContainer()
   myGraphics.TranslateTransform(0, 100)
   myGraphics.DrawRectangle(myPen, 0, 0, 50, 50)
myGraphics.EndContainer(myGraphicsContainer)
myGraphics.DrawRectangle(myPen, 0, 0, 50, 50)
[C#]
myGraphics.TranslateTransform(200, 0);

myGraphicsContainer = myGraphics.BeginContainer();
   myGraphics.TranslateTransform(0, 100);
   myGraphics.DrawRectangle(myPen, 0, 0, 50, 50);
myGraphics.EndContainer(myGraphicsContainer);
myGraphics.DrawRectangle(myPen, 0, 0, 50, 50);

Observe que en el ejemplo anterior, la instrucción myGraphics.DrawRectangle(0, 0, 50, 50) que tiene lugar entre las llamadas a BeginContainer y EndContainer genera un rectángulo diferente al generado por la misma instrucción después de la llamada a EndContainer. Sólo se aplica la traslación horizontal a la llamada DrawRectangle realizada fuera del contenedor. Ambas transformaciones (traslación horizontal de 200 unidades y traslación vertical de 100 unidades) se aplican a la llamada DrawRectangle realizada dentro del contenedor. En la siguiente ilustración se muestran los dos rectángulos.

Los contenedores pueden anidarse dentro de contenedores. En el siguiente ejemplo se crea un contenedor en un objeto Graphics y otro contenedor dentro del primer contenedor. La transformación universal del objeto Graphics es una conversión de 100 unidades en la dirección del eje x y de 80 unidades en la dirección del eje y. La transformación de coordenadas universales del primer contenedor es una rotación de 30 grados. La transformación de coordenadas universales del segundo contenedor es un ajuste de escala en un factor de 2 en la dirección del eje x. Dentro del segundo contenedor, se realiza una llamada al método DrawEllipse.

myGraphics.TranslateTransform(100, 80, MatrixOrder.Append)

container1 = myGraphics.BeginContainer()
   myGraphics.RotateTransform(30, MatrixOrder.Append)

   container2 = myGraphics.BeginContainer()
      myGraphics.ScaleTransform(2, 1)
      myGraphics.DrawEllipse(myPen, - 30, - 20, 60, 40)
   myGraphics.EndContainer(container2)

myGraphics.EndContainer(container1)
[C#]
myGraphics.TranslateTransform(100, 80, MatrixOrder.Append);

container1 = myGraphics.BeginContainer();
   myGraphics.RotateTransform(30, MatrixOrder.Append);

   container2 = myGraphics.BeginContainer();
      myGraphics.ScaleTransform(2, 1);
      myGraphics.DrawEllipse(myPen, -30, -20, 60, 40);
   myGraphics.EndContainer(container2);

myGraphics.EndContainer(container1);

En la siguiente ilustración se muestra la elipse.

Observe que las tres transformaciones se aplican a la llamada DrawEllipse realizada en el segundo contenedor. Además, tenga en cuenta el orden de las transformaciones: primero el ajuste de escala, después la rotación y después la traslación. La transformación más interna es la primera que se aplica, y la transformación más externa se aplica la última.

Todas las propiedades de un objeto Graphics pueden establecerse dentro de un contenedor (entre las llamadas a BeginContainer y EndContainer). Puede establecerse, por ejemplo, una región de recorte dentro de un contenedor. Cualquier dibujo realizado dentro del contenedor se limitará a la región de recorte de dicho contenedor y también se limitará a las regiones de recorte de cualquier contenedor externo y a la región de recorte del propio objeto Graphics.

Las propiedades que se han explicado hasta ahora, la transformación de coordenadas universales y la región de recorte, se combinan mediante contenedores anidados. Otras propiedades se reemplazan temporalmente mediante un contenedor anidado. Por ejemplo, si la propiedad SmoothingMode se establece en SmoothingMode.AntiAlias en un contenedor, todos los métodos de dibujo a los que se llame dentro de ese contenedor utilizarán el modo de suavizado AntiAlias, pero los métodos de dibujo a los que se llame tras EndContainer utilizarán el modo de suavizado que se aplicaba antes de la llamada a BeginContainer.

Para ver otro ejemplo de combinación de transformaciones de coordenadas universales de un objeto Graphics y un contenedor, vamos a suponer que se desea dibujar un ojo y colocarlo en varias ubicaciones en una secuencia de caras. En el siguiente ejemplo se dibuja un ojo centrado en el origen del sistema de coordenadas:

Private Sub DrawEye(myGraphics As Graphics)
   Dim eyeContainer As GraphicsContainer
      
   eyeContainer = myGraphics.BeginContainer()
      
      Dim myBlackPen As New Pen(Color.Black)
      Dim myGreenBrush As New SolidBrush(Color.Green)
      Dim myBlackBrush As New SolidBrush(Color.Black)
      
      Dim myTopPath As New GraphicsPath()
      myTopPath.AddEllipse(- 30, - 50, 60, 60)
      
      Dim myBottomPath As New GraphicsPath()
      myBottomPath.AddEllipse(- 30, - 10, 60, 60)
      
      Dim myTopRegion As New [Region](myTopPath)
      Dim myBottomRegion As New [Region](myBottomPath)
      
      ' Draw the outline of the eye.
      ' The outline of the eye consists of two ellipses.
      ' The top ellipse is clipped by the bottom ellipse, and
      ' the bottom ellipse is clipped by the top ellipse.
      myGraphics.Clip = myTopRegion
      myGraphics.DrawPath(myBlackPen, myBottomPath)
      myGraphics.Clip = myBottomRegion
      myGraphics.DrawPath(myBlackPen, myTopPath)
      
      ' Fill the iris.
      ' The iris is clipped by the bottom ellipse.
      myGraphics.FillEllipse(myGreenBrush, - 10, - 15, 20, 22)
      
      ' Fill the pupil.
      myGraphics.FillEllipse(myBlackBrush, - 3, - 7, 6, 9)
      
   myGraphics.EndContainer(eyeContainer)
End Sub 'DrawEye
[C#]
private void DrawEye(Graphics myGraphics)
{
   GraphicsContainer eyeContainer;

   eyeContainer = myGraphics.BeginContainer();

      Pen myBlackPen = new Pen(Color.Black);
      SolidBrush myGreenBrush = new SolidBrush(Color.Green);
      SolidBrush myBlackBrush = new SolidBrush(Color.Black);

      GraphicsPath myTopPath = new GraphicsPath();
      myTopPath.AddEllipse(-30, -50, 60, 60);

      GraphicsPath myBottomPath = new GraphicsPath();
      myBottomPath.AddEllipse(-30, -10, 60, 60);

      Region myTopRegion = new Region(myTopPath);
      Region myBottomRegion = new Region(myBottomPath);

      // Draw the outline of the eye.
      // The outline of the eye consists of two ellipses.
      // The top ellipse is clipped by the bottom ellipse, and
      // the bottom ellipse is clipped by the top ellipse.
      myGraphics.Clip = myTopRegion;
      myGraphics.DrawPath(myBlackPen, myBottomPath);
      myGraphics.Clip = myBottomRegion;
      myGraphics.DrawPath(myBlackPen, myTopPath);

      // Fill the iris.
      // The iris is clipped by the bottom ellipse.
      myGraphics.FillEllipse(myGreenBrush, -10, -15, 20, 22);

      // Fill the pupil.
      myGraphics.FillEllipse(myBlackBrush, -3, -7, 6, 9);

   myGraphics.EndContainer(eyeContainer);
}

En la siguiente ilustración se muestra el ojo y los ejes de coordenadas.

La función DrawEye del ejemplo anterior recibe un objeto Graphics e inmediatamente crea un contenedor en ese objeto Graphics. Este contenedor aísla cualquier código que llame a la función DrawEye de la configuración de propiedades realizada durante la ejecución de la función DrawEye. Por ejemplo, el código en la función DrawEye establece la región de recorte del objeto Graphics, pero cuando DrawEye devuelve el control a la rutina de llamada, la región de recorte volverá a ser como era antes de la llamada a DrawEye.

En el siguiente ejemplo se dibujan tres elipses (caras), cada una de ellas con un ojo dentro:

' Draw an ellipse centered at (100, 100).
myGraphics.TranslateTransform(100, 100)
myGraphics.DrawEllipse(myBlackPen, - 40, - 60, 80, 120)

' Draw the eye at the center of the ellipse.
DrawEye(myGraphics)

' Draw an ellipse centered at 200, 100.
myGraphics.TranslateTransform(100, 0, MatrixOrder.Append)
myGraphics.DrawEllipse(myBlackPen, - 40, - 60, 80, 120)
      
' Rotate the eye 40 degrees, and draw it 30 units above
' the center of the ellipse.
myGraphicsContainer = myGraphics.BeginContainer()
   myGraphics.RotateTransform(- 40)
   myGraphics.TranslateTransform(0, - 30, MatrixOrder.Append)
   DrawEye(myGraphics)
myGraphics.EndContainer(myGraphicsContainer)
      
' Draw an ellipse centered at (300, 100).
myGraphics.TranslateTransform(100, 0, MatrixOrder.Append)
myGraphics.DrawEllipse(myBlackPen, - 40, - 60, 80, 120)
      
' Stretch and rotate the eye, and draw it at the 
' center of the ellipse.
myGraphicsContainer = myGraphics.BeginContainer()
   myGraphics.ScaleTransform(2, 1.5F)
   myGraphics.RotateTransform(45, MatrixOrder.Append)
   DrawEye(myGraphics)
myGraphics.EndContainer(myGraphicsContainer)
[C#]
// Draw an ellipse centered at (100, 100).
myGraphics.TranslateTransform(100, 100);
myGraphics.DrawEllipse(myBlackPen, -40, -60, 80, 120);

// Draw the eye at the center of the ellipse.
DrawEye(myGraphics);

// Draw an ellipse centered at 200, 100.
myGraphics.TranslateTransform(100, 0, MatrixOrder.Append);
myGraphics.DrawEllipse(myBlackPen, -40, -60, 80, 120);

// Rotate the eye 40 degrees, and draw it 30 units above
// the center of the ellipse.
myGraphicsContainer = myGraphics.BeginContainer();
   myGraphics.RotateTransform(-40);
   myGraphics.TranslateTransform(0, -30, MatrixOrder.Append);
   DrawEye(myGraphics);
myGraphics.EndContainer(myGraphicsContainer);

// Draw an ellipse centered at (300, 100).
myGraphics.TranslateTransform(100, 0, MatrixOrder.Append);
myGraphics.DrawEllipse(myBlackPen, -40, -60, 80, 120);

// Stretch and rotate the eye, and draw it at the 
// center of the ellipse.
myGraphicsContainer = myGraphics.BeginContainer();
   myGraphics.ScaleTransform(2, 1.5f);
   myGraphics.RotateTransform(45, MatrixOrder.Append);
   DrawEye(myGraphics);
myGraphics.EndContainer(myGraphicsContainer);

En la siguiente ilustración se muestran las tres elipses.

En el ejemplo anterior, todas las elipses se dibujan con la llamada a DrawEllipse(myBlackPen, -40, -60, 80, 120), que dibuja una elipse centrada en el origen del sistema de coordenadas. Las elipses se alejan de la esquina superior izquierda del área de cliente mediante el establecimiento de una transformación de coordenadas universales del objeto Graphics. La instrucción myGraphics.TranslateTransform(100, 100) centra la primera elipse en (100, 100). La instrucción myGraphics.TranslateTransform(100, 0) hace que el centro de la segunda elipse esté 100 unidades a la derecha del centro de la primera elipse. Del mismo modo, el centro de la tercera elipse está 100 unidades a la derecha del centro de la segunda elipse.

Los contenedores del ejemplo anterior se utilizan para transformar el ojo con respecto al centro de una elipse determinada. El primer ojo se dibuja en el centro de la elipse que no ha sufrido transformación alguna, por lo tanto, la llamada a DrawEye no está dentro de un contenedor. El segundo ojo se rota 40 grados y se dibuja 30 unidades por encima del centro de la elipse y, así, se llama a la función DrawEye y a los métodos que establecen la transformación dentro de un contenedor. El tercer ojo se expande, rota y se dibuja en el centro de la elipse. Al igual que ocurre con el segundo ojo, se llama a la función DrawEye y a los métodos que establecen la transformación dentro de un contenedor.