Benutzerdefinierte Layouts
Die .NET Multi-Platform App UI (.NET MAUI) definiert mehrere Layoutklassen, die ihre untergeordneten Elemente auf andere Weise anordnen. Ein Layout kann als Liste von Ansichten mit Regeln und Eigenschaften betrachtet werden, die definieren, wie diese Ansichten innerhalb des Layouts angeordnet werden. Beispiele für Layouts sind Grid, AbsoluteLayout und VerticalStackLayout.
.NET MAUI-Layoutklassen werden von der abstrakten Layout-Klasse abgeleitet. Diese Klasse delegiert plattformübergreifendes Layout und Messungen an eine Layout-Manager-Klasse. Die Layout-Klasse enthält auch eine überschreibbare CreateLayoutManager()-Methode, mit deren Hilfe abgeleitete Layouts den Layout-Manager angeben können.
Jede Layout-Manager-Klasse implementiert die ILayoutManager-Schnittstelle, die angibt, dass Measure- und ArrangeChildren- Implementierungen bereitgestellt werden müssen:
- Die Measure-Implementierung ruft IView.Measure für jede Ansicht im Layout auf und gibt die Gesamtgröße des Layouts mit den entsprechenden Einschränkungen zurück.
- Die ArrangeChildren-Implementierung bestimmt, wo jede Ansicht innerhalb der Grenzen des Layouts platziert werden soll, und ruft für jede Ansicht Arrange mit den entsprechenden Grenzen auf. Der Rückgabewert ist die tatsächliche Größe des Layouts.
Die Layouts von .NET MAUI verfügen über vordefinierte Layout-Manager, um ihr Layout zu verarbeiten. Manchmal ist es jedoch erforderlich, Seiteninhalte mithilfe eines Layouts zu organisieren, das nicht von .NET MAUI bereitgestellt wird. Sie können dies erreichen, indem Sie ein benutzerdefiniertes Layout erstellen. Dafür müssen Sie allerdings verstehen, wie der plattformübergreifende Layoutprozess von .NET MAUI funktioniert.
Layoutprozess
Der plattformübergreifende Layoutprozess von .NET MAUI basiert auf dem nativen Layoutprozess auf jeder Plattform. Im Allgemeinen wird der Layoutprozess vom nativen Layoutsystem initiiert. Der plattformübergreifende Prozess wird ausgeführt, wenn ein Layout- oder Inhaltssteuerelement als Ergebnis der Messung oder Anordnung durch das native Layoutsystem initiiert wird.
Hinweis
Jede Plattform behandelt das Layout etwas anders. Der plattformübergreifende Layoutprozess von .NET MAUI zielt jedoch darauf ab, so plattformunabhängig wie möglich zu sein.
Das folgende Diagramm zeigt den Prozess, wenn ein natives Layoutsystem die Layoutmessung initiiert:
Alle .NET MAUI-Layouts verfügen über ein einzelnes Hintergrundlayout auf jeder Plattform:
- Unter Android ist dieses Hintergrundlayout
LayoutViewGroup
. - Unter iOS und Mac Catalyst ist dieses Hintergrundlayout
LayoutView
. - Unter Windows ist dieses Hintergrundlayout
LayoutPanel
.
Wenn das native Layoutsystem für eine Plattform die Messung eines dieser Hintergrundlayouts anfordert, ruft das Hintergrundlayout die Layout.CrossPlatformMeasure-Methode auf. An diesem Punkt wird das Steuerelement vom nativen Layoutsystem an das .NET MAUI-Layoutsystem übergeben. Layout.CrossPlatformMeasure ruft die Measure-Methode der Layout-Manager auf. Diese Methode ist für die Messung untergeordneter Ansichten verantwortlich und ruft für jede Ansicht im Layout IView.Measure auf. Die Ansicht misst das native Steuerelement und aktualisiert basierend auf dieser Messung seine DesiredSize-Eigenschaft. Dieser Wert wird als Ergebnis der CrossPlatformMeasure
-Methode an das Hintergrundlayout zurückgegeben. Das Hintergrundlayout führt jede notwendige interne Verarbeitung aus, und gibt die gemessene Größe an die Plattform zurück.
Das folgende Diagramm zeigt den Prozess, wenn ein natives Layoutsystem die Layoutanordnung initiiert:
Wenn das native Layoutsystem für eine Plattform die Anordnung oder das Layout eines dieser Hintergrundlayouts anfordert, ruft das Hintergrundlayout die Layout.CrossPlatformArrange-Methode auf. An diesem Punkt wird das Steuerelement vom nativen Layoutsystem an das .NET MAUI-Layoutsystem übergeben. Layout.CrossPlatformArrange ruft die ArrangeChildren-Methode der Layout-Manager auf. Diese Methode ist dafür verantwortlich, zu bestimmen, wo jede Ansicht innerhalb der Grenzen des Layouts platziert werden soll, und ruft für jede Ansicht Arrange auf, um ihre Position festzulegen. Dieser Wert wird als Ergebnis der CrossPlatformArrange
-Methode an das Hintergrundlayout zurückgegeben. Das Hintergrundlayout führt jede notwendige interne Verarbeitung aus, und gibt die tatsächliche Größe an die Plattform zurück.
Hinweis
ILayoutManager.Measure wird möglicherweise mehrmals vor ArrangeChildren aufgerufen, da eine Plattform möglicherweise einige spekulative Messungen durchführen muss, bevor Ansichten angeordnet werden.
Ansätze für benutzerdefinierte Layouts
Es gibt zwei Hauptansätze zum Erstellen eines benutzerdefinierten Layouts:
- Erstellen Sie einen benutzerdefinierten Layouttyp, der in der Regel eine Unterklasse eines vorhandenen Layouttyps oder von Layout ist, und setzen Sie in Ihrem benutzerdefinierten Layouttyp CreateLayoutManager() außer Kraft. Stellen Sie dann eine ILayoutManager-Implementierung bereit, die Ihre benutzerdefinierte Layoutlogik enthält. Weitere Informationen finden Sie unter Erstellen eines benutzerdefinierten Layouttyps.
- Ändern Sie das Verhalten eines vorhandenen Layouttyps, indem Sie einen Typ erstellen, der ILayoutManagerFactory implementiert. Verwenden Sie dann diese Layout-Manager-Factory, um den standardmäßigen Layout-Manager von .NET MAUI für das vorhandene Layout durch Ihre eigene ILayoutManager-Implementierung zu ersetzen, die Ihre benutzerdefinierte Layoutlogik enthält. Weitere Informationen finden Sie unter Ändern des Verhaltens eines vorhandenen Layouts.
Erstellen eines benutzerdefinierten Layouttyps
Der Prozess zum Erstellen eines benutzerdefinierten Layouttyps besteht aus folgenden Schritten:
Erstellen Sie eine Klasse, in der ein vorhandener Layouttyp oder die Layout-Klasse als Unterklasse eingerichtet wird, und setzen Sie CreateLayoutManager() in Ihrem benutzerdefinierten Layouttyp außer Kraft. Weitere Informationen finden Sie unter Unterklassifizieren eines Layouts.
Erstellen Sie eine Layout-Manager-Klasse, die von einem vorhandenen Layout-Manager abgeleitet wird oder die ILayoutManager-Schnittstelle direkt implementiert. In Ihrer Layout-Manager-Klasse sollten Sie:
- die Measure Methode außer Kraft setzen oder implementieren, um die Gesamtgröße des Layouts mit den entsprechenden Einschränkungen zu berechnen.
- die ArrangeChildren-Methode außer Kraft setzen oder implementieren, um die Größe aller untergeordneten Elemente innerhalb des Layouts anzupassen und zu positionieren.
Weitere Informationen finden Sie unter Erstellen eines Layout-Managers.
Nutzen Sie Ihren benutzerdefinierten Layouttyp, indem Sie ihn zu einer Page hinzufügen und dem Layout untergeordnete Elemente hinzufügen. Weitere Informationen finden Sie unter Erstellen des Layouttyps.
Ein ausrichtungssensitives HorizontalWrapLayout
wird verwendet, um diesen Prozess zu veranschaulichen. Ein HorizontalWrapLayout
ähnelt einem HorizontalStackLayout insofern, als es die untergeordneten Elemente horizontal auf der Seite anordnet. Es umschließt jedoch die Anzeige untergeordneter Elemente in eine neue Zeile, wenn es auf den rechten Rand des Containers trifft.
Hinweis
Im Beispiel werden zusätzliche benutzerdefinierte Layouts definiert, die Sie nutzen können, um zu verstehen, wie Sie ein benutzerdefiniertes Layout erstellen.
Unterklasse eines Layouts
Zum Erstellen eines benutzerdefinierten Layouttyps müssen Sie zunächst einen vorhandenen Layouttyp oder Layout-Klasse als Unterklasse erstellen. Setzen Sie dann CreateLayoutManager() in Ihrem Layouttyp außer Kraft, und geben Sie eine neue Instanz des Layout-Managers für Ihren Layouttyp zurück:
using Microsoft.Maui.Layouts;
public class HorizontalWrapLayout : HorizontalStackLayout
{
protected override ILayoutManager CreateLayoutManager()
{
return new HorizontalWrapLayoutManager(this);
}
}
Das HorizontalWrapLayout
wird vom HorizontalStackLayout abgeleitet, um seine Layoutfunktionalität nutzen zu können. .NET MAUI-Layouts delegieren plattformübergreifendes Layout und Messungen an eine Layout-Manager-Klasse. Daher gibt die Außerkraftsetzung von CreateLayoutManager() eine neue Instanz der HorizontalWrapLayoutManager
-Klasse zurück, bei der es sich um den Layout-Manager handelt, der im nächsten Abschnitt vorgestellt wird.
Erstellen eines Layout-Managers
Eine Layout-Manager-Klasse wird verwendet, um plattformübergreifendes Layout und Messungen für Ihren benutzerdefinierten Layouttyp auszuführen. Sie sollte von einem vorhandenen Layout-Manager abgeleitet werden oder direkt die ILayoutManager-Schnittstelle implementieren. Der HorizontalWrapLayoutManager
wird vom HorizontalStackLayoutManager abgeleitet, sodass er die zugrunde liegenden Funktionen nutzen und auf die Elemente in der Vererbungshierarchie zugreifen kann:
using Microsoft.Maui.Layouts;
using HorizontalStackLayoutManager = Microsoft.Maui.Layouts.HorizontalStackLayoutManager;
public class HorizontalWrapLayoutManager : HorizontalStackLayoutManager
{
HorizontalWrapLayout _layout;
public HorizontalWrapLayoutManager(HorizontalWrapLayout horizontalWrapLayout) : base(horizontalWrapLayout)
{
_layout = horizontalWrapLayout;
}
public override Size Measure(double widthConstraint, double heightConstraint)
{
}
public override Size ArrangeChildren(Rect bounds)
{
}
}
Der HorizontalWrapLayoutManager
-Konstruktor speichert eine Instanz des HorizontalWrapLayout
-Typs in einem Feld, sodass im gesamten Layout-Manager darauf zugegriffen werden kann. Der Layout-Manager setzt außerdem die Measure- und ArrangeChildren-Methode aus der HorizontalStackLayoutManager-Klasse außer Kraft. In diesen Methoden definieren Sie die Logik zum Implementieren des benutzerdefinierten Layouts.
Messen der Layoutgröße
Der Zweck der ILayoutManager.Measure-Implementierung besteht darin, die Gesamtgröße des Layouts zu berechnen. Dies sollte durch Aufrufen von IView.Measure für jedes untergeordnete Element im Layout geschehen. Anschließend sollten diese Daten verwendet werden, um die Gesamtgröße des Layouts entsprechend ihrer Einschränkungen zu berechnen und zurückzugeben.
Im folgenden Beispiel wird die Measure-Implementierung für die HorizontalWrapLayoutManager
-Klasse gezeigt:
public override Size Measure(double widthConstraint, double heightConstraint)
{
var padding = _layout.Padding;
widthConstraint -= padding.HorizontalThickness;
double currentRowWidth = 0;
double currentRowHeight = 0;
double totalWidth = 0;
double totalHeight = 0;
for (int n = 0; n < _layout.Count; n++)
{
var child = _layout[n];
if (child.Visibility == Visibility.Collapsed)
{
continue;
}
var measure = child.Measure(double.PositiveInfinity, heightConstraint);
// Will adding this IView put us past the edge?
if (currentRowWidth + measure.Width > widthConstraint)
{
// Keep track of the width so far
totalWidth = Math.Max(totalWidth, currentRowWidth);
totalHeight += currentRowHeight;
// Account for spacing
totalHeight += _layout.Spacing;
// Start over at 0
currentRowWidth = 0;
currentRowHeight = measure.Height;
}
currentRowWidth += measure.Width;
currentRowHeight = Math.Max(currentRowHeight, measure.Height);
if (n < _layout.Count - 1)
{
currentRowWidth += _layout.Spacing;
}
}
// Account for the last row
totalWidth = Math.Max(totalWidth, currentRowWidth);
totalHeight += currentRowHeight;
// Account for padding
totalWidth += padding.HorizontalThickness;
totalHeight += padding.VerticalThickness;
// Ensure that the total size of the layout fits within its constraints
var finalWidth = ResolveConstraints(widthConstraint, Stack.Width, totalWidth, Stack.MinimumWidth, Stack.MaximumWidth);
var finalHeight = ResolveConstraints(heightConstraint, Stack.Height, totalHeight, Stack.MinimumHeight, Stack.MaximumHeight);
return new Size(finalWidth, finalHeight);
}
Die Measure(Double, Double)-Methode listet alle sichtbaren untergeordneten Elemente im Layout auf, wobei die IView.Measure-Methode für jedes untergeordnete Element aufgerufen wird. Anschließend gibt sie die Gesamtgröße des Layouts zurück, wobei die Einschränkungen und die Werte der Padding- und Spacing-Eigenschaft berücksichtigt werden. Die ResolveConstraints-Methode wird aufgerufen, um sicherzustellen, dass die Gesamtgröße des Layouts gemäß ihrer Einschränkungen passt.
Wichtig
Überspringen Sie beim Aufzählen von untergeordneten Elementen in der ILayoutManager.Measure-Implementierung alle untergeordneten Elemente, deren Visibility-Eigenschaft auf Collapsed festgelegt ist. Dadurch wird sichergestellt, dass das benutzerdefinierte Layout keinen Platz für unsichtbare untergeordnete Elemente übrig lässt.
Anordnen von untergeordneten Elementen im Layout
Der Zweck der ArrangeChildren-Implementierung besteht darin, alle untergeordneten Elemente innerhalb des Layouts größenmäßig anzupassen und zu positionieren. Um zu bestimmen, wo jedes untergeordnete Element innerhalb der Grenzen des Layouts platziert werden soll, sollte sie Arrange für jedes untergeordnete Element mit den entsprechenden Grenzen aufrufen. Dadurch sollte ein Wert zurückgegeben werden, der der tatsächlichen Größe des Layouts entspricht.
Warnung
Wenn die ArrangeChildren-Methode nicht für jedes untergeordnete Element im Layout aufgerufen wird, erhält es niemals eine passende Größe oder Position und wird daher nicht auf der Seite sichtbar.
Im folgenden Beispiel wird die ArrangeChildren-Implementierung für die HorizontalWrapLayoutManager
-Klasse gezeigt:
public override Size ArrangeChildren(Rect bounds)
{
var padding = Stack.Padding;
double top = padding.Top + bounds.Top;
double left = padding.Left + bounds.Left;
double currentRowTop = top;
double currentX = left;
double currentRowHeight = 0;
double maxStackWidth = currentX;
for (int n = 0; n < _layout.Count; n++)
{
var child = _layout[n];
if (child.Visibility == Visibility.Collapsed)
{
continue;
}
if (currentX + child.DesiredSize.Width > bounds.Right)
{
// Keep track of our maximum width so far
maxStackWidth = Math.Max(maxStackWidth, currentX);
// Move down to the next row
currentX = left;
currentRowTop += currentRowHeight + _layout.Spacing;
currentRowHeight = 0;
}
var destination = new Rect(currentX, currentRowTop, child.DesiredSize.Width, child.DesiredSize.Height);
child.Arrange(destination);
currentX += destination.Width + _layout.Spacing;
currentRowHeight = Math.Max(currentRowHeight, destination.Height);
}
var actual = new Size(maxStackWidth, currentRowTop + currentRowHeight);
// Adjust the size if the layout is set to fill its container
return actual.AdjustForFill(bounds, Stack);
}
Die ArrangeChildren
-Methode listet alle sichtbaren untergeordneten Elemente im Layout auf, passt ihre Größe an und platziert sie innerhalb des Layouts. Dies geschieht durch Aufrufen von Arrange für jedes untergeordnete Element mit den entsprechenden Grenzen, die das Padding und Spacing des zugrunde liegenden Layouts berücksichtigen. Anschließend wird die tatsächliche Größe des Layouts zurückgegeben. Die AdjustForFill-Methode wird aufgerufen, um sicherzustellen, dass die Größe berücksichtigt, ob das Layout seine HorizontalLayoutAlignment- und VerticalLayoutAlignment-Eigenschaft auf LayoutOptions.Fill hat.
Wichtig
Überspringen Sie beim Aufzählen von untergeordneten Elementen in der ArrangeChildren-Implementierung alle untergeordneten Elemente, deren Visibility-Eigenschaft auf Collapsed festgelegt ist. Dadurch wird sichergestellt, dass das benutzerdefinierte Layout keinen Platz für unsichtbare untergeordnete Elemente übrig lässt.
Verwenden des Layouttyps
Die HorizontalWrapLayout
-Klasse kann genutzt werden, indem sie in einen von Page abgeleiteten Typ eingefügt wird:
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:layouts="clr-namespace:CustomLayoutDemos.Layouts"
x:Class="CustomLayoutDemos.Views.HorizontalWrapLayoutPage"
Title="Horizontal wrap layout">
<ScrollView Margin="20">
<layouts:HorizontalWrapLayout Spacing="20">
<Image Source="img_0074.jpg"
WidthRequest="150" />
<Image Source="img_0078.jpg"
WidthRequest="150" />
<Image Source="img_0308.jpg"
WidthRequest="150" />
<Image Source="img_0437.jpg"
WidthRequest="150" />
<Image Source="img_0475.jpg"
WidthRequest="150" />
<Image Source="img_0613.jpg"
WidthRequest="150" />
<!-- More images go here -->
</layouts:HorizontalWrapLayout>
</ScrollView>
</ContentPage>
Steuerelemente können nach Bedarf zum HorizontalWrapLayout
hinzugefügt werden. In diesem Beispiel werden die Image-Steuerelemente angezeigt, wenn die Seite das HorizontalWrapLayout
enthält:
Die Anzahl der Spalten in jeder Zeile hängt von der Bildgröße, der Breite der Seite und der Anzahl der Pixel pro geräteunabhängiger Einheit ab:
Hinweis
Scrollen wird durch Umbrechen von HorizontalWrapLayout
in eine ScrollView unterstützt.
Ändern des Verhaltens eines vorhandenen Layouts
In einigen Szenarien möchten Sie möglicherweise das Verhalten eines vorhandenen Layouttyps ändern, ohne einen benutzerdefinierten Layouttyp erstellen zu müssen. Für solche Szenarien können Sie einen Typ erstellen, der ILayoutManagerFactory implementiert, und diese Schnittstelle verwendet, um den standardmäßigen Layout-Manager von .NET MAUI für das vorhandene Layout durch Ihre eigene ILayoutManager-Implementierung zu ersetzen. Auf diese Weise können Sie einen neuen Layout-Manager für ein vorhandenes Layout definieren, z. B. einen benutzerdefinierten Layout-Manager für Grid. Dies kann in Szenarien hilfreich sein, in denen Sie ein neues Verhalten zu einem Layout hinzufügen möchten, ohne den Typ eines vorhandenen und viel genutzten Layouts in Ihrer App aktualisieren möchten.
Der Prozess zum Ändern des Verhaltens eines vorhandenen Layouts mit einer Layout-Manager-Factory besteht aus folgenden Schritten:
- Erstellen Sie einen Layout-Manager, der von einem der Layout-Manager-Typen von .NET MAUI abgeleitet wird. Weitere Informationen finden Sie unter Erstellen eines benutzerdefinierten Layout-Managers.
- Erstellen Sie einen Typ, der ILayoutManagerFactory implementiert. Weitere Informationen finden Sie unter Erstellen einer Layout-Manager-Factory.
- Registrieren Sie Ihre Layout-Manager-Factory beim Dienstanbieter der App. Weitere Informationen finden Sie unter Registrieren der Layout-Manager-Factory.
Erstellen eines benutzerdefinierten Layout-Managers
Ein Layout-Manager wird verwendet, um plattformübergreifendes Layout und Messungen für ein Layout durchzuführen. Um das Verhalten eines vorhandenen Layouts zu ändern, sollten Sie einen benutzerdefinierten Layout-Manager erstellen, der vom Layout-Manager für das Layout abgeleitet wird:
using Microsoft.Maui.Layouts;
public class CustomGridLayoutManager : GridLayoutManager
{
public CustomGridLayoutManager(IGridLayout layout) : base(layout)
{
}
public override Size Measure(double widthConstraint, double heightConstraint)
{
EnsureRows();
return base.Measure(widthConstraint, heightConstraint);
}
void EnsureRows()
{
if (Grid is not Grid grid)
{
return;
}
// Find the maximum row value from the child views
int maxRow = 0;
foreach (var child in grid)
{
maxRow = Math.Max(grid.GetRow(child), maxRow);
}
// Add more rows if we need them
for (int n = grid.RowDefinitions.Count; n <= maxRow; n++)
{
grid.RowDefinitions.Add(new RowDefinition(GridLength.Star));
}
}
}
In diesem Beispiel wird CustomGridLayoutManager
von der GridLayoutManager-Klasse von .NET MAUI abgeleitet und die Measure-Methode außer Kraft gesetzt. Dieser benutzerdefinierte Layout-Manager stellt sicher, dass zur Laufzeit die RowDefinitions genügend Zeilen für das Grid enthalten, um jede angefügte Grid.Row
-Eigenschaft in einer untergeordneten Ansicht zu berücksichtigen. Ohne diese Änderung müsste die RowDefinitions für das Grid zur Entwurfszeit festgelegt werden.
Wichtig
Wenn Sie das Verhalten eines vorhandenen Layout-Managers ändern, denken Sie daran, sicherzustellen, dass Sie die base.Measure
-Methode aus Ihrer Measure-Implementierung aufrufen.
Erstellen einer Layout-Manager-Factory
Der benutzerdefinierte Layout-Manager sollte in einer Layout-Manager-Factory erstellt werden. Dies wird durch Erstellen eines Typs erreicht, der die ILayoutManagerFactory-Schnittstelle implementiert:
using Microsoft.Maui.Layouts;
public class CustomLayoutManagerFactory : ILayoutManagerFactory
{
public ILayoutManager CreateLayoutManager(Layout layout)
{
if (layout is Grid)
{
return new CustomGridLayoutManager(layout as IGridLayout);
}
return null;
}
}
In diesem Beispiel wird eine CustomGridLayoutManager
-Instanz zurückgegeben, wenn das Layout ein Grid ist.
Registrieren der Layout-Manager-Factory
Die Layout-Manager-Factory sollte beim Dienstanbieter Ihrer App in Ihrer MauiProgram
-Klasse registriert werden:
public static class MauiProgram
{
public static MauiApp CreateMauiApp()
{
var builder = MauiApp.CreateBuilder();
builder
.UseMauiApp<App>()
.ConfigureFonts(fonts =>
{
fonts.AddFont("OpenSans-Regular.ttf", "OpenSansRegular");
fonts.AddFont("OpenSans-Semibold.ttf", "OpenSansSemibold");
});
// Setup a custom layout manager so the default manager for the Grid can be replaced.
builder.Services.Add(new ServiceDescriptor(typeof(ILayoutManagerFactory), new CustomLayoutManagerFactory()));
return builder.Build();
}
}
Wenn die App dann ein Grid rendert, wird der benutzerdefinierte Layout-Manager verwendet, um sicherzustellen, dass zur Laufzeit die RowDefinitions genügend Zeilen für das Gridenthalten, um jede angefügte Grid.Row
-Eigenschaft in untergeordneten Ansichten zu berücksichtigen.
Das folgende Beispiel zeigt ein Grid, das die angefügte Grid.Row
-Eigenschaft in untergeordneten Ansichten festlegt, jedoch nicht die RowDefinitions-Eigenschaft:
<Grid>
<Label Text="This Grid demonstrates replacing the LayoutManager for an existing layout type." />
<Label Grid.Row="1"
Text="In this case, it's a LayoutManager for Grid which automatically adds enough rows to accommodate the rows specified in the child views' attached properties." />
<Label Grid.Row="2"
Text="Notice that the Grid doesn't explicitly specify a RowDefinitions collection." />
<Label Grid.Row="3"
Text="In MauiProgram.cs, an instance of an ILayoutManagerFactory has been added that replaces the default GridLayoutManager. The custom manager will automatically add the necessary RowDefinitions at runtime." />
<Label Grid.Row="5"
Text="We can even skip some rows, and it will add the intervening ones for us (notice the gap between the previous label and this one)." />
</Grid>
Die Layout-Manager-Factory verwendet den benutzerdefinierten Layout-Manager, um sicherzustellen, dass das Grid in diesem Beispiel richtig angezeigt wird, obwohl die RowDefinitions-Eigenschaft nicht festgelegt wird: