Compartir a través de


Dibujar formas

Aprenda a dibujar formas, como elipses, rectángulos, polígonos y trazados. La clase Path es la manera de visualizar un lenguaje de dibujo bastante complejo basado en vectores en una interfaz de usuario XAML; por ejemplo, puede dibujar curvas Bezier.

Dos conjuntos de clases definen una región de espacio en la interfaz de usuario XAML: clases de formas y clases geometry . La principal diferencia entre estas clases es que una forma tiene un pincel asociado y se puede representar en la pantalla, y una geometría simplemente define una región de espacio y no se representa a menos que ayude a contribuir a la información a otra propiedad de la interfaz de usuario. Puede considerar una forma como uiElement con su límite definido por una geometría. En este tema se tratan principalmente las clases Shape .

Las clases Shape son Line, Ellipse, Rectangle, Polygon, Polyline y Path. La ruta de acceso es interesante porque puede definir una geometría arbitraria y la clase Geometry está implicada aquí porque es una manera de definir las partes de una ruta de acceso.

Relleno y contorno para formas

Para que una forma se renderice en el lienzo de la aplicación, debe asociarse un pincel a ella. Establezca la propiedad Fill de la forma en el pincel que desee. Para obtener más información sobre los pinceles, consulta Uso de pinceles.

Una forma también puede tener un trazo, que es una línea dibujada alrededor del perímetro de la forma. Un trazo también requiere un pincel que define su apariencia y debe tener un valor no nulo para StrokeThickness. StrokeThickness es una propiedad que define el grosor del perímetro alrededor del borde de la forma. Si no especifica un valor Brush de Stroke, o si establece StrokeThickness en 0, el borde alrededor de la forma no se dibuja.

Elipse

Una elipse es una forma con un perímetro curvado. Para crear una elipse básica, especifique un width, height y un pincel para el relleno.

En el ejemplo siguiente se crea una elipse con un ancho de 200 y un alto de 200, y se usa un SolidColorBrush de color SteelBlue como relleno.

<Ellipse Fill="SteelBlue" Height="200" Width="200" />
var ellipse1 = new Ellipse();
ellipse1.Fill = new SolidColorBrush(Colors.SteelBlue);
ellipse1.Width = 200;
ellipse1.Height = 200;

// When you create a XAML element in code, you have to add
// it to the XAML visual tree. This example assumes you have
// a panel named 'layoutRoot' in your XAML file, like this:
// <Grid x:Name="layoutRoot>
layoutRoot.Children.Add(ellipse1);

Aquí está la elipse renderizada.

Un elipse representado.

En este caso, la elipse es lo que la mayoría de las personas considerarían un círculo, pero así es como declaras una forma de círculo en XAML: usa una elipse con el mismo ancho y alto.

Cuando se coloca una elipse en un diseño de interfaz de usuario, se supone que su tamaño es el mismo que un rectángulo con ese Ancho y Alto; el área fuera del perímetro no tiene representación, pero todavía forma parte de su tamaño de ranura de diseño.

Un conjunto de 6 elementos Ellipse forman parte de la plantilla de control para el control ProgressRing y 2 elementos elipse concéntricos forman parte de un RadioButton.

Rectángulo

Un Rectángulo es una forma de cuatro lados con sus lados opuestos siendo iguales. Para crear un rectángulo básico, especifique un ancho, un alto y un relleno.

Puede redondear las esquinas de un rectángulo. Para crear esquinas redondeadas, especifique un valor para las propiedades RadiusX y RadiusY . Estas propiedades especifican el eje X y el eje Y de una elipse que define la curva de las esquinas. El valor máximo permitido de RadiusX es el ancho dividido por dos y el valor máximo permitido de RadiusY es el alto dividido por dos.

En el ejemplo siguiente se crea un rectángulo con un ancho de 200 y un alto de 100. Usa un valor Azul de SolidColorBrush para su Relleno y un valor Negro de SolidColorBrush para su trazo. Establecemos strokeThickness en 3. Establecemos la propiedad RadiusX en 50 y la propiedad RadiusY en 10, lo que proporciona las esquinas redondeadas del rectángulo .

<Rectangle Fill="Blue"
           Width="200"
           Height="100"
           Stroke="Black"
           StrokeThickness="3"
           RadiusX="50"
           RadiusY="10" />
var rectangle1 = new Rectangle();
rectangle1.Fill = new SolidColorBrush(Colors.Blue);
rectangle1.Width = 200;
rectangle1.Height = 100;
rectangle1.Stroke = new SolidColorBrush(Colors.Black);
rectangle1.StrokeThickness = 3;
rectangle1.RadiusX = 50;
rectangle1.RadiusY = 10;

// When you create a XAML element in code, you have to add
// it to the XAML visual tree. This example assumes you have
// a panel named 'layoutRoot' in your XAML file, like this:
// <Grid x:Name="layoutRoot>
layoutRoot.Children.Add(rectangle1);

Este es el rectángulo representado.

Rectángulo representado.

Hay algunos escenarios para las definiciones de interfaz de usuario en las que, en lugar de usar un rectángulo, un borde podría ser más adecuado. Si su intención es crear una forma de rectángulo alrededor de otros contenidos, puede ser mejor usar Border porque puede tener contenido secundario y ajustará automáticamente el tamaño alrededor de esos contenidos, en lugar de usar las dimensiones fijas para el alto y el ancho, como Rectangle hace. Un borde también tiene la opción de tener esquinas redondeadas si establece la propiedad CornerRadius.

Por otro lado, un Rectángulo es probablemente una mejor opción para la composición del control. Una forma Rectangle se ve en muchas plantillas de control porque se usa como una parte "FocusVisual" para controles enfocados. Cada vez que el control está en un estado visual "Centrado", este rectángulo se hace visible, en otros estados está oculto.

Polygon

Un polígono es una forma con un límite definido por un número arbitrario de puntos. El límite se crea conectando una línea de un punto a otro, con el último punto conectado al primer punto. La propiedad Points define la colección de puntos que componen el límite. En XAML, definirás los puntos con una lista separada por comas. En el código subyacente se usa pointCollection para definir los puntos y se agrega cada punto individual como un valor Point a la colección.

No es necesario declarar explícitamente los puntos de forma que el punto de inicio y el punto final se especifiquen como el mismo valor de punto . La lógica de representación de un polígono supone que está definiendo una forma cerrada y conectará el punto final al punto de inicio implícitamente.

En el ejemplo siguiente se crea un polígono con 4 puntos establecidos en (10,200), (60,140), (130,140)y (180,200). Usa un valor LightBlue de SolidColorBrush para su relleno y no tiene ningún valor para Stroke por lo que no tiene contorno perimetral.

<Polygon Fill="LightBlue"
         Points="10,200,60,140,130,140,180,200" />
var polygon1 = new Polygon();
polygon1.Fill = new SolidColorBrush(Colors.LightBlue);

var points = new PointCollection();
points.Add(new Windows.Foundation.Point(10, 200));
points.Add(new Windows.Foundation.Point(60, 140));
points.Add(new Windows.Foundation.Point(130, 140));
points.Add(new Windows.Foundation.Point(180, 200));
polygon1.Points = points;

// When you create a XAML element in code, you have to add
// it to the XAML visual tree. This example assumes you have
// a panel named 'layoutRoot' in your XAML file, like this:
// <Grid x:Name="layoutRoot>
layoutRoot.Children.Add(polygon1);

Este es el polígono representado.

Polígono representado.

Sugerencia

Un valor point se usa a menudo como un tipo en XAML para escenarios distintos de declarar los vértices de las formas. Por ejemplo, un punto forma parte de los datos del evento para los eventos táctiles, por lo que puede saber exactamente dónde se produjo la acción táctil en un espacio de coordenadas. Para obtener más información sobre Point y cómo usarlo en XAML o código, consulta el tema de referencia de API para Point.

Line

Una línea es simplemente una línea dibujada entre dos puntos en el espacio de coordenadas. Una línea omite cualquier valor proporcionado para Fill, porque no tiene espacio interior. Para una línea, asegúrese de especificar valores para las propiedades Stroke y StrokeThickness, ya que, de lo contrario, la línea no se representará.

No se usan valores de punto para especificar una forma línea ; en su lugar, se usan valores double discretos para X1, Y1, X2 e Y2. Esto permite un marcado mínimo para líneas horizontales o verticales. Por ejemplo, <Line Stroke="Red" X2="400"/> define una línea horizontal de 400 píxeles de longitud. Las otras propiedades X,Y son 0 de forma predeterminada, por lo que en términos de puntos este XAML dibujaría una línea de (0,0) a (400,0). A continuación, podría usar translateTransform para mover toda la línea, si quiere que comience en un punto distinto de (0,0).

<Line Stroke="Red" X2="400"/>
var line1 = new Line();
line1.Stroke = new SolidColorBrush(Colors.Red);
line1.X2 = 400;

// When you create a XAML element in code, you have to add
// it to the XAML visual tree. This example assumes you have
// a panel named 'layoutRoot' in your XAML file, like this:
// <Grid x:Name="layoutRoot>
layoutRoot.Children.Add(line1);

Polilínea

Una polilínea es similar a un polígono en que el límite de la forma se define mediante un conjunto de puntos, salvo que el último punto de una polilínea no está conectado al primer punto.

Nota:

Podría tener explícitamente un punto de inicio y un punto de conexión idénticos en los puntos establecidos para la polilínea, pero en ese caso probablemente podría haber usado un polígono en su lugar.

Si especifica un relleno de una polilínea, el relleno pinta el espacio interior de la forma, incluso si el punto inicial y el punto final de los puntos establecidos para la polilínea no se intersecan. Si no especificas un relleno, entonces la polilínea es similar a la que se representaría si especificaras varios elementos Línea individuales, cuyos puntos iniciales y finales de líneas consecutivas se intersectan.

Al igual que con un Polygon, la propiedad Points define la colección de puntos que componen el límite. En XAML, definirás los puntos con una lista separada por comas. En el código subyacente, se usa pointCollection para definir los puntos y se agrega cada punto individual como una estructura Point a la colección.

En este ejemplo se crea una polilínea con cuatro puntos establecidos en (10,200), (60,140), (130,140)y (180,200). Se define un trazo , pero no un relleno.

<Polyline Stroke="Black"
          StrokeThickness="4"
          Points="10,200,60,140,130,140,180,200" />
var polyline1 = new Polyline();
polyline1.Stroke = new SolidColorBrush(Colors.Black);
polyline1.StrokeThickness = 4;

var points = new PointCollection();
points.Add(new Windows.Foundation.Point(10, 200));
points.Add(new Windows.Foundation.Point(60, 140));
points.Add(new Windows.Foundation.Point(130, 140));
points.Add(new Windows.Foundation.Point(180, 200));
polyline1.Points = points;

// When you create a XAML element in code, you have to add
// it to the XAML visual tree. This example assumes you have
// a panel named 'layoutRoot' in your XAML file, like this:
// <Grid x:Name="layoutRoot>
layoutRoot.Children.Add(polyline1);

Esta es la polilínea representada. Observe que los puntos primero y último no están conectados por el esquema de trazo , ya que están en un polígono.

Polilínea representada.

Ruta

Un trazo es la forma más versátil porque se puede usar para definir una geometría arbitraria. Pero con esta versatilidad viene complejidad. Veamos ahora cómo crear una ruta de acceso básica en XAML.

Defina la geometría de una ruta de acceso con la propiedad Data . Hay dos técnicas para establecer datos:

  • Puedes establecer un valor de cadena para Datos en XAML. En este formato, el valor Path.Data consume un formato de serialización para gráficos. Normalmente no se edita el texto de este valor en forma de cadena después de establecerlo por primera vez. En su lugar, usa herramientas de diseño que le permiten trabajar en un diseño o dibujar metáforas en una superficie. A continuación, guardas o exporta la salida, y esto te proporciona un archivo XAML o un fragmento de cadena XAML con información de Path.Data .
  • Puede establecer la propiedad Data en un único objeto Geometry . Esto se puede hacer en código o en XAML. Esa geometría única suele ser geometryGroup, que actúa como un contenedor que puede componer varias definiciones de geometría en un solo objeto con fines del modelo de objetos. La razón más común para hacerlo es porque quiere usar una o varias de las curvas y formas complejas que se pueden definir como valores de segmentos para una pathFigure, por ejemplo BezierSegment.

En este ejemplo se muestra un trazo que podría haber resultado de usar Blend para Visual Studio para crear solo unas pocas formas vectoriales y luego guardar el resultado como XAML. La ruta total consta de un segmento de curva Bezier y un segmento de línea. El ejemplo está pensado principalmente para proporcionarle algunos ejemplos de los elementos que existen en el formato de serialización Path.Data y cuáles representan los números.

Estos datos comienzan con el comando Move, indicado por "M", que establece un punto de inicio absoluto para la trayectoria.

El primer segmento es una curva Bezier cúbica que comienza en y termina en (100,200)(400,175), que se dibuja mediante los dos puntos (100,25) de control y (400,350). Este segmento se indica mediante el comando "C" en la cadena de atributo Data .

El segundo segmento comienza con un comando de línea horizontal absoluto "H", que especifica una línea dibujada desde el punto de conexión (400,175) de subruta anterior a un nuevo punto de conexión (280,175). Dado que es un comando de línea horizontal, el valor especificado es una coordenada x.

<Path Stroke="DarkGoldenRod" 
      StrokeThickness="3"
      Data="M 100,200 C 100,25 400,350 400,175 H 280" />

Esta es la ruta representada.

Captura de pantalla de una trayectoria simple renderizada.

En el ejemplo siguiente se muestra un uso de la otra técnica que se describe: geometryGroup con pathGeometry. En este ejemplo se ejercen algunos de los tipos de geometría que se pueden usar como parte de PathGeometry: PathFigure y los distintos elementos que pueden ser un segmento en PathFigure.Segments.

<Path Stroke="Black" StrokeThickness="1" Fill="#CCCCFF">
    <Path.Data>
        <GeometryGroup>
            <RectangleGeometry Rect="50,5 100,10" />
            <RectangleGeometry Rect="5,5 95,180" />
            <EllipseGeometry Center="100, 100" RadiusX="20" RadiusY="30"/>
            <RectangleGeometry Rect="50,175 100,10" />
            <PathGeometry>
                <PathGeometry.Figures>
                    <PathFigureCollection>
                        <PathFigure IsClosed="true" StartPoint="50,50">
                            <PathFigure.Segments>
                                <PathSegmentCollection>
                                    <BezierSegment Point1="75,300" Point2="125,100" Point3="150,50"/>
                                    <BezierSegment Point1="125,300" Point2="75,100"  Point3="50,50"/>
                                </PathSegmentCollection>
                            </PathFigure.Segments>
                        </PathFigure>
                    </PathFigureCollection>
                </PathGeometry.Figures>
            </PathGeometry>
        </GeometryGroup>
    </Path.Data>
</Path>
var path1 = new Microsoft.UI.Xaml.Shapes.Path();
path1.Fill = new SolidColorBrush(Windows.UI.Color.FromArgb(255, 204, 204, 255));
path1.Stroke = new SolidColorBrush(Colors.Black);
path1.StrokeThickness = 1;

var geometryGroup1 = new GeometryGroup();
var rectangleGeometry1 = new RectangleGeometry();
rectangleGeometry1.Rect = new Rect(50, 5, 100, 10);
var rectangleGeometry2 = new RectangleGeometry();
rectangleGeometry2.Rect = new Rect(5, 5, 95, 180);
geometryGroup1.Children.Add(rectangleGeometry1);
geometryGroup1.Children.Add(rectangleGeometry2);

var ellipseGeometry1 = new EllipseGeometry();
ellipseGeometry1.Center = new Point(100, 100);
ellipseGeometry1.RadiusX = 20;
ellipseGeometry1.RadiusY = 30;
geometryGroup1.Children.Add(ellipseGeometry1);

var pathGeometry1 = new PathGeometry();
var pathFigureCollection1 = new PathFigureCollection();
var pathFigure1 = new PathFigure();
pathFigure1.IsClosed = true;
pathFigure1.StartPoint = new Windows.Foundation.Point(50, 50);
pathFigureCollection1.Add(pathFigure1);
pathGeometry1.Figures = pathFigureCollection1;

var pathSegmentCollection1 = new PathSegmentCollection();
var pathSegment1 = new BezierSegment();
pathSegment1.Point1 = new Point(75, 300);
pathSegment1.Point2 = new Point(125, 100);
pathSegment1.Point3 = new Point(150, 50);
pathSegmentCollection1.Add(pathSegment1);

var pathSegment2 = new BezierSegment();
pathSegment2.Point1 = new Point(125, 300);
pathSegment2.Point2 = new Point(75, 100);
pathSegment2.Point3 = new Point(50, 50);
pathSegmentCollection1.Add(pathSegment2);
pathFigure1.Segments = pathSegmentCollection1;

geometryGroup1.Children.Add(pathGeometry1);
path1.Data = geometryGroup1;

// When you create a XAML element in code, you have to add
// it to the XAML visual tree. This example assumes you have
// a panel named 'layoutRoot' in your XAML file, like this:
// <Grid x:Name="layoutRoot">
layoutRoot.Children.Add(path1);

Esta es la ruta representada.

Captura de pantalla de una compleja ruta de acceso renderizada.

El uso de PathGeometry puede ser más legible que rellenar una cadena Path.Data . Por otro lado, Path.Data usa una sintaxis compatible con las definiciones de ruta de acceso de imagen de gráficos vectoriales escalables (SVG), por lo que puede ser útil para migrar gráficos desde SVG o como salida de una herramienta como Blend.

UWP y WinUI 2

Importante

La información y los ejemplos de este artículo están optimizados para aplicaciones que usan el SDK de Aplicaciones para Windows y WinUI 3, pero generalmente son aplicables a las aplicaciones para UWP que usan WinUI 2. Consulta la referencia de la API de UWP para obtener información y ejemplos específicos de la plataforma.

Esta sección contiene la información que necesitas para utilizar el control en una aplicación UWP o WinUI 2.

Existen API para estas formas en el espacio de nombres Windows.UI.Xaml.Shapes .