Udostępnij przez


Rysuj kształty

Dowiedz się, jak rysować kształty, takie jak elipsy, prostokąty, wielokąty i ścieżki. Klasa Path to sposób wizualizacji dość złożonego języka rysunkowego opartego na wektorach w interfejsie użytkownika XAML; na przykład możesz narysować krzywe Beziera.

Dwa zestawy klas definiują region przestrzeni w interfejsie użytkownika XAML: klasy kształtów i klasy Geometry . Główną różnicą między tymi klasami jest to, że Shape ma powiązany z nim pędzel i może być renderowany na ekranie, a Geometry po prostu definiuje region przestrzeni i nie jest renderowana, chyba że pomaga przekazywać informacje do innej właściwości interfejsu użytkownika. Kształt można traktować jako element interfejsu użytkownika z granicą zdefiniowaną przez geometrię. W tym temacie omówiono głównie klasy Shape.

Klasy Kształt to Linia, Elipsa, Prostokąt, Wielokąt, Wielolinia i Ścieżka. Ścieżka jest interesująca, ponieważ może zdefiniować dowolną geometrię, a klasa Geometry jest tutaj zaangażowana, ponieważ jest to jeden ze sposobów definiowania części ścieżki.

Wypełnienie i pociągnięcie kształtów

Aby kształt mógł być renderowany na kanwie aplikacji, musisz skojarzyć z nim pędzel. Ustaw właściwość FillKształtu na Pędzel, którego chcesz użyć. Aby uzyskać więcej informacji na temat pędzli, zobacz Używanie pędzli.

Kształt może również mieć kontur, czyli linię rysowaną wokół obwodu kształtu. Obrys również wymaga pędzla, który definiuje jego wygląd, i powinna mieć niezerową wartość dla grubości obrysu. StrokeThickness to właściwość, która definiuje grubość obwodu wokół krawędzi kształtu. Jeśli nie określisz wartości Brush dla Obrysu, lub ustawisz StrokeThickness na 0, obramowanie wokół kształtu nie zostanie narysowane.

Elipsa

Elipsa to kształt z zakrzywionym obwodem. Aby utworzyć podstawową elipsę, określ szerokość, wysokość i szczotkę dla wypełnienia.

W następnym przykładzie zostanie utworzona elipsa o szerokości 200 i wysokości 200 przy użyciu wypełnienia w kolorze SteelBlueSolidColorBrush.

<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);

Oto renderowana elipsa.

Renderowana elipsa.

W tym przypadku Elipsa jest tym, co większość osób uważa za okrąg, ale tak deklarujesz kształt koła w języku XAML: użyj Elipsy o równej szerokości i wysokości.

Gdy elipsa jest umieszczona w układzie interfejsu użytkownika, przyjmuje się, że jej rozmiar jest taki sam jak prostokąt o tej szerokości i wysokości; obszar poza obwodem nie jest renderowany, ale nadal stanowi część przestrzeni układu.

Zestaw 6 elementów elipsy jest częścią szablonu kontrolki ProgressRing, a 2 koncentryczne elipsy są częścią przyciski radiowego.

Prostokąt

Prostokąt to czterostronny kształt, którego przeciwległe boki są równe. Aby utworzyć podstawowy prostokąt, określ szerokość, wysokość i wypełnienie.

Rogi prostokąta można zaokrąglić. Aby utworzyć zaokrąglone rogi, określ wartość właściwości RadiusX i RadiusY . Te właściwości określają oś x i oś y elipsy, która definiuje krzywiznę narożników. Maksymalna dozwolona wartość radiusX to szerokość podzielona przez dwa, a maksymalna dozwolona wartość radiusY to wysokość podzielona przez dwa.

W następnym przykładzie zostanie utworzony prostokąt o szerokości 200 i wysokości 100. Używa wartości BlueSolidColorBrush dla jego Fill i wartości BlackSolidColorBrush dla jego Stroke. Ustawiliśmy StrokeThickness na 3. Ustawiliśmy właściwość RadiusX na wartość 50, a właściwość RadiusY na 10, co daje zaokrąglone rogi prostokąta .

<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);

Oto renderowany prostokąt.

Renderowany prostokąt.

Istnieją pewne scenariusze definicji interfejsu użytkownika, w których zamiast używać prostokąta, obramowanie może być bardziej odpowiednie. Jeśli twoim zamiarem jest utworzenie kształtu prostokąta wokół innej zawartości, lepszym rozwiązaniem może być użycie Border, ponieważ może ono zawierać zawartość podrzędną i automatycznie dostosowuje się do rozmiaru tej zawartości, zamiast używać stałych wymiarów dla wysokości i szerokości, jak robi to Rectangle. Obramowanie ma również możliwość zaokrąglenia narożników, jeśli ustawisz właściwość CornerRadius.

Z drugiej strony prostokąt jest prawdopodobnie lepszym wyborem do kompozycji kontrolnej. Kształt prostokąta jest widoczny w wielu szablonach kontrolek, ponieważ jest używany jako część "FocusVisual" dla kontrolek z możliwością koncentracji uwagi. Za każdym razem, gdy kontrolka znajduje się w stanie wizualnym "Skoncentrowany", ten prostokąt jest widoczny, natomiast w innych stanach jest ukryty.

Polygon

Wielokąt to kształt z granicą zdefiniowaną przez dowolną liczbę punktów. Granica jest tworzona przez połączenie linii z jednego punktu do następnego z ostatnim punktem połączonym z pierwszym punktem. Właściwość Points definiuje kolekcję punktów tworzących granicę. W języku XAML zdefiniujesz punkty z listą oddzieloną przecinkami. W kodzie używasz PointCollection do zdefiniowania punktów i dodajesz każdy pojedynczy punkt jako wartość Point do kolekcji.

Nie trzeba jawnie deklarować punktów, tak aby punkt początkowy i punkt końcowy zostały określone jako ta sama wartość punktu . Logika renderowania dla wielokąta zakłada, że definiujesz zamknięty kształt i połączy punkt końcowy z punktem początkowym niejawnie.

W następnym przykładzie zostanie utworzony wielokąt z 4 punktami ustawionymi na (10,200), (60,140), (130,140) i (180,200). Używa wartości LightBlueSolidColorBrush dla wypełnienia i nie ma wartości Stroke , więc nie ma konturu obwodowego.

<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);

Oto renderowany wielokąt.

Renderowany wielokąt.

Wskazówka

Wartość punktu jest często używana jako typ w języku XAML dla scenariuszy innych niż deklarowanie wierzchołków kształtów. Na przykład punkt jest częścią danych zdarzenia dla zdarzeń dotykowych, więc możesz wiedzieć dokładnie, gdzie w przestrzeni współrzędnej wystąpiła akcja dotykowa. Aby uzyskać więcej informacji o punkcie i sposobie używania go w języku XAML lub kodzie, zobacz temat Dokumentacja interfejsu API dla punktu.

Linia

Linia jest po prostu linią rysowaną między dwoma punktami w przestrzeni współrzędnej. Linia ignoruje dowolną wartość podaną dla pola Fill, ponieważ nie ma przestrzeni wewnętrznej. W przypadku linii upewnij się, że określono wartości właściwości Stroke i StrokeThickness, ponieważ w przeciwnym razie linia nie będzie renderowana.

Wartości Point nie są używane do określania kształtu Line, zamiast tego używa się dyskretnych wartości Double dla X1, Y1, X2 i Y2. To umożliwia minimalne oznaczenia dla linii poziomych lub pionowych. Na przykład <Line Stroke="Red" X2="400"/> definiuje linię poziomą o długości 400 pikseli. Pozostałe właściwości X,Y są domyślnie 0, więc pod względem punktów ten XAML narysuje linię od (0,0) do (400,0). Następnie możesz użyć funkcji TranslateTransform , aby przenieść całą linię, jeśli chcesz, aby rozpoczynała się w punkcie innym niż (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);

Polilinia

Wielokąt jest podobny do wielokąta w tym, że granica kształtu jest definiowana przez zestaw punktów, z wyjątkiem ostatniego punktu w polilinii nie jest połączona z pierwszym punktem.

Uwaga / Notatka

Można jawnie mieć identyczny punkt początkowy i punkt końcowy w zbiorze Punktów dla polylinii, ale w takim przypadku prawdopodobnie można było użyć wielokąta zamiast.

Jeśli określisz wypełnieniepoliliny, wypełnienie wypełnia przestrzeń wewnętrzną kształtu, nawet jeśli punkty początkowy i końcowy w zbiorze punktów ustawionych dla poliliny się nie przecinają. Jeśli nie określisz wypełnienia, wtedy Polyline jest podobna do tego, co zostałoby wyrenderowane, gdybyś określił kilka pojedynczych elementów Line, w których punkty początkowe i końcowe kolejnych linii przecinają się.

Podobnie jak w przypadku wielokąta właściwość Points definiuje kolekcję punktów tworzących granicę. W języku XAML zdefiniujesz punkty za pomocą listy oddzielonej przecinkami. W kodzie używasz kolekcji PointCollection do definiowania punktów i dodawania poszczególnych punktów jako struktury punktów do kolekcji.

W tym przykładzie utworzono wielolinię z czterema punktami ustawionymi na (10,200), (60,140), (130,140) i (180,200). Obrys jest zdefiniowany, ale nie wypełnienie.

<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);

Oto renderowana polilinia. Zwróć uwagę, że pierwsze i ostatnie punkty nie są połączone za pomocą konturu pociągnięcia , ponieważ znajdują się w wielokącie.

Renderowana linia wieloliniowa.

Ścieżka

Ścieżka jest najbardziej wszechstronnym kształtem, ponieważ służy do definiowania dowolnej geometrii. Ale z tą wszechstronnością przychodzi złożoność. Teraz przyjrzyjmy się, jak utworzyć podstawową ścieżkę w języku XAML.

Zdefiniujesz geometrię ścieżki za pomocą właściwości Data . Istnieją dwie techniki ustawiania danych:

  • Wartość ciągu można ustawić dla Data w XAML. W tym formularzu wartość Path.Data korzysta z formatu serializacji dla grafiki. Zazwyczaj ta wartość nie jest edytowana tekstowo w postaci ciągu po jej pierwszym ustanowieniu. Zamiast tego używasz narzędzi projektowych, które umożliwiają pracę w metaforze projektu lub rysowania na powierzchni. Następnie zapiszesz lub wyeksportujesz dane wyjściowe, co daje plik XAML lub fragment ciągu XAML z informacjami Path.Data .
  • Właściwość Data można ustawić na pojedynczy obiekt Geometry . Można to zrobić w kodzie lub w języku XAML. Ta pojedyncza geometria jest zazwyczaj grupą GeometryGroup, która działa jako kontener, który może składać wiele definicji geometrii w jeden obiekt na potrzeby modelu obiektów. Najczęstszą przyczyną tego działania jest to, że chcesz użyć co najmniej jednej krzywej i złożonych kształtów, które można zdefiniować jako wartości Segmenty dla elementu PathFigure, na przykład BezierSegment.

W tym przykładzie pokazano ścieżkę , która mogła wynikać z używania programu Blend dla programu Visual Studio do utworzenia zaledwie kilku kształtów wektorów, a następnie zapisania wyniku jako kodu XAML. Całkowita ścieżka składa się z segmentu krzywej Beziera i segmentu linii. Przykład ma na celu głównie pokazanie, jakie elementy istnieją w formacie serializacji Path.Data i co oznaczają liczby.

Te dane zaczynają się od polecenia Przesuń wskazanego przez "M", które ustanawia bezwzględny punkt początkowy dla ścieżki.

Pierwszy segment to krzywa Beziera sześcienna, która zaczyna się od (100,200) i kończy się na (400,175), która jest rysowana przy użyciu dwóch punktów kontrolnych (100,25) i (400,350). Ten segment jest wskazywany przez polecenie "C" w ciągu atrybutu Dane .

Drugi segment zaczyna się od polecenia bezwzględnej linii poziomej "H", które określa linię narysowaną począwszy od poprzedniego punktu końcowego podścieżki (400,175) do nowego punktu końcowego (280,175). Ponieważ jest to polecenie wiersza poziomego, określona wartość jest współrzędną x.

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

Oto renderowana ścieżka.

Zrzut ekranu przedstawiający prostą wyrenderowaną ścieżkę.

W następnym przykładzie pokazano użycie innej techniki, którą omówiliśmy: GeometryGroup z elementem PathGeometry. W tym przykładzie wykorzystuje niektóre typy geometrii składowych, które mogą być używane w ramach PathGeometry: PathFigure i różnych elementów, które mogą być segmentami w 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);

Oto renderowana ścieżka.

Zrzut ekranu przedstawiający złożoną wyrenderowaną ścieżkę.

Użycie metody PathGeometry może być bardziej czytelne niż wypełnianie ciągu Path.Data . Z drugiej strony Path.Data używa składni zgodnej z definicjami ścieżek grafiki wektorowej skalowalnej (SVG), więc może być przydatna do portowania grafiki z formatu SVG lub jako wyjście z narzędzia takiego jak Blend.

UwP i WinUI 2

Ważne

Informacje i przykłady w tym artykule są zoptymalizowane dla aplikacji korzystających z Windows App SDK oraz WinUI 3, ale generalnie mają zastosowanie także w aplikacjach UWP używających WinUI 2. Zobacz dokumentację interfejsu API platformy UWP, aby uzyskać informacje i przykłady dotyczące platformy.

Ta sekcja zawiera informacje potrzebne do używania kontrolki w aplikacji platformy UWP lub WinUI 2.

Interfejsy API dla tych kształtów istnieją w przestrzeni nazw Windows.UI.Xaml.Shapes .