Erstellen eines benutzerdefinierten Layouts in Xamarin.Forms

Xamarin.Forms definiert fünf Layoutklassen – StackLayout, AbsoluteLayout, RelativeLayout, Grid und FlexLayout, und jede ordnet die untergeordneten Elemente auf andere Weise an. Manchmal ist es jedoch erforderlich, Seiteninhalte mithilfe eines Layouts zu organisieren, das nicht von Xamarin.Forms. In diesem Artikel wird erläutert, wie Sie eine benutzerdefinierte Layoutklasse schreiben und eine ausrichtungsabhängige WrapLayout-Klasse veranschaulicht, die die untergeordneten Elemente horizontal auf der Seite anordnet, und die Anzeige der nachfolgenden untergeordneten Elemente in zusätzliche Zeilen umschließt.

In Xamarin.Forms, alle Layoutklassen werden von der Layout<T> Klasse abgeleitet und beschränken den generischen Typ auf View und die abgeleiteten Typen. Die Klasse wird wiederum Layout<T> von der Layout Klasse abgeleitet, die den Mechanismus für die Positionierung und Größenanpassung untergeordneter Elemente bereitstellt.

Jedes visuelle Element ist dafür verantwortlich, seine eigene bevorzugte Größe zu bestimmen, die als angeforderte Größe bezeichnet wird. Page, Layoutund Layout<View> abgeleitete Typen sind für die Bestimmung des Standorts und der Größe ihres Untergeordneten oder untergeordneten Elemente relativ zu sich selbst verantwortlich. Daher umfasst das Layout eine Beziehung zwischen übergeordneten und untergeordneten Elementen, wobei das übergeordnete Element bestimmt, wie groß die untergeordneten Elemente sein sollen, aber versucht, die angeforderte Größe des untergeordneten Elements zu berücksichtigen.

Ein gründliches Verständnis der Xamarin.Forms Layout- und Ungültigkeitszyklen ist erforderlich, um ein benutzerdefiniertes Layout zu erstellen. Diese Zyklen werden jetzt erörtert.

Layout

Das Layout beginnt am oberen Rand der visuellen Struktur mit einer Seite und durchläuft alle Zweige der visuellen Struktur, um jedes visuelle Element auf einer Seite einzuschließen. Elemente, die über andere Elemente verfügen, sind für die Größenanpassung und Positionierung ihrer Untergeordneten relativ zu sich selbst verantwortlich.

Die VisualElement Klasse definiert eine Measure Methode, die ein Element für Layoutvorgänge misst, und eine Layout Methode, die den rechteckigen Bereich angibt, in dem das Element gerendert wird. Wenn eine Anwendung gestartet wird und die erste Seite angezeigt wird, beginnt ein Layoutzyklus , der zuerst aus Measure Aufrufen besteht, und dann Layout aufruft, für das Page Objekt:

  1. Während des Layoutzyklus ist jedes übergeordnete Element dafür verantwortlich, die Methode für die Measure untergeordneten Elemente aufzurufen.
  2. Nachdem die untergeordneten Elemente gemessen wurden, ist jedes übergeordnete Element dafür verantwortlich, die Methode für die Layout untergeordneten Elemente aufzurufen.

Dieser Zyklus stellt sicher, dass jedes visuelle Element auf der Seite Aufrufe der Measure Und Layout Methoden empfängt. Der Prozess wird im folgenden Diagramm dargestellt:

Xamarin.Forms Layoutzyklus

Hinweis

Beachten Sie, dass Layoutzyklen auch in einer Teilmenge der visuellen Struktur auftreten können, wenn sich etwas ändert, um das Layout zu beeinflussen. Dazu gehören Elemente, die einer Auflistung hinzugefügt oder daraus entfernt werden, z. B. in einer StackLayoutAuflistung, eine Änderung der IsVisible Eigenschaft eines Elements oder eine Änderung der Größe eines Elements.

Jede Xamarin.Forms Klasse mit einer Content oder einer Children Eigenschaft verfügt über eine überschreibbare LayoutChildren Methode. Benutzerdefinierte Layoutklassen, die von Layout<View> dieser Methode abgeleitet werden, müssen überschreiben und sicherstellen, dass die und Layout die Measure Methoden für alle untergeordneten Elemente des Elements aufgerufen werden, um das gewünschte benutzerdefinierte Layout bereitzustellen.

Darüber hinaus bestimmt jede Klasse, die von Layout der Methode abgeleitet wird oder Layout<View> die OnMeasure Methode überschreiben muss. Dabei bestimmt eine Layoutklasse die Größe, die sie sein muss, indem Aufrufe an die Measure Methoden der untergeordneten Elemente vorgenommen werden.

Hinweis

Elemente bestimmen ihre Größe basierend auf Einschränkungen, die angeben, wie viel Platz für ein Element innerhalb des übergeordneten Elements verfügbar ist. Einschränkungen, die an die Measure Methoden übergeben werden, können zwischen 0 und OnMeasure Double.PositiveInfinity0 liegen. Ein Element ist eingeschränkt oder vollständig eingeschränkt, wenn es einen Aufruf seiner Measure Methode mit nicht unendlichen Argumenten empfängt – das Element wird auf eine bestimmte Größe beschränkt. Ein Element ist nicht eingeschränkt oder teilweise eingeschränkt, wenn es einen Aufruf seiner Measure Methode mit mindestens einem Argument erhält, das gleich ist Double.PositiveInfinity – die unendliche Einschränkung kann als Hinweis auf die automatische Größenanpassung betrachtet werden.

Invalidierung

Die Ungültigheit ist der Prozess, mit dem eine Änderung in einem Element auf einer Seite einen neuen Layoutzyklus auslöst. Elemente werden als ungültig betrachtet, wenn sie nicht mehr über die richtige Größe oder Position verfügen. Wenn sich beispielsweise die FontSize Eigenschaft einer Button Änderung ändert, wird die Button Eigenschaft als ungültig bezeichnet, da sie nicht mehr über die richtige Größe verfügt. Das Ändern der Button Größe kann dann auswirkungen auf Änderungen am Layout über den Rest einer Seite haben.

Elemente werden durch Aufrufen der InvalidateMeasure Methode ungültig, in der Regel, wenn sich eine Eigenschaft des Elements ändert, die zu einer neuen Größe des Elements führen kann. Diese Methode löst das MeasureInvalidated Ereignis aus, das das übergeordnete Element behandelt, um einen neuen Layoutzyklus auszulösen.

Die Layout Klasse legt einen Handler für das MeasureInvalidated Ereignis für jedes untergeordnete Element fest, das seiner Content Eigenschaft oder Children Auflistung hinzugefügt wird, und trennt den Handler, wenn das untergeordnete Element entfernt wird. Daher wird jedes Element in der visuellen Struktur mit untergeordneten Elementen benachrichtigt, wenn eines seiner untergeordneten Elemente die Größe ändert. Das folgende Diagramm veranschaulicht, wie eine Änderung der Größe eines Elements in der visuellen Struktur Zu änderungen führen kann, die die Struktur aufwellen:

Ungültigierung in der visuellen Struktur

Die Layout Klasse versucht jedoch, die Auswirkungen einer Änderung der Größe eines untergeordneten Elements auf das Layout einer Seite einzuschränken. Wenn die Größe des Layouts eingeschränkt ist, wirkt sich eine Änderung der untergeordneten Größe nicht auf etwas höher als das übergeordnete Layout in der visuellen Struktur aus. In der Regel wirkt sich eine Änderung der Größe eines Layouts jedoch auf die Anordnung der untergeordneten Elemente des Layouts aus. Daher beginnt jede Änderung der Layoutgröße einen Layoutzyklus für das Layout, und das Layout empfängt Aufrufe an seine OnMeasure und LayoutChildren Methoden.

Die Layout Klasse definiert auch eine InvalidateLayout Methode, die der Methode einen ähnlichen Zweck hat InvalidateMeasure . Die InvalidateLayout Methode sollte immer aufgerufen werden, wenn eine Änderung vorgenommen wird, die sich darauf auswirkt, wie sich die Layoutposition und die Größe der untergeordneten Elemente auswirken. Beispielsweise ruft die Layout Klasse die InvalidateLayout Methode auf, wenn ein untergeordnetes Element zu einem Layout hinzugefügt oder daraus entfernt wird.

Dies InvalidateLayout kann außer Kraft gesetzt werden, um einen Cache zu implementieren, um sich wiederholende Aufrufe der Measure Methoden der untergeordneten Elemente des Layouts zu minimieren. Wenn die InvalidateLayout Methode überschrieben wird, wird eine Benachrichtigung darüber bereitgestellt, wann untergeordnete Elemente dem Layout hinzugefügt oder daraus entfernt werden. Ebenso kann die OnChildMeasureInvalidated Methode außer Kraft gesetzt werden, um eine Benachrichtigung bereitzustellen, wenn eine der untergeordneten Elemente des Layouts die Größe ändert. Bei beiden Methodenüberschreibungen sollte ein benutzerdefiniertes Layout reagieren, indem der Cache gelöscht wird. Weitere Informationen finden Sie unter Berechnen und Zwischenspeichern von Layoutdaten.

Erstellen eines benutzerdefinierten Layouts

Der Prozess zum Erstellen eines benutzerdefinierten Layouts lautet wie folgt:

  1. Erstellen Sie eine von der Layout<View>-Klasse abgeleitete Klasse. Weitere Informationen finden Sie unter Create a WrapLayout.

  2. [optional] Fügen Sie Eigenschaften hinzu, die durch bindungsfähige Eigenschaften gesichert werden, für alle Parameter, die für die Layoutklasse festgelegt werden sollen. Weitere Informationen finden Sie unter Add Properties Backed by Bindable Properties.

  3. Überschreiben Sie die Methode, um die OnMeasure Measure Methode für alle untergeordneten Elemente des Layouts aufzurufen, und geben Sie eine angeforderte Größe für das Layout zurück. Weitere Informationen finden Sie unter Überschreiben der OnMeasure-Methode.

  4. Überschreiben Sie die LayoutChildren Methode, um die Layout Methode für alle untergeordneten Elemente des Layouts aufzurufen. Wenn die Layout Methode für jedes untergeordnete Element in einem Layout nicht aufgerufen wird, erhält das untergeordnete Element niemals eine richtige Größe oder Position, und daher wird das untergeordnete Element nicht auf der Seite sichtbar. Weitere Informationen finden Sie unter Überschreiben der LayoutChildren-Methode.

    Hinweis

    Überspringen Sie beim Aufzählen von untergeordneten Elementen in den OnMeasure und LayoutChildren Außerkraftsetzungen alle untergeordneten Elemente, deren IsVisible Eigenschaft auf false". Dadurch wird sichergestellt, dass das benutzerdefinierte Layout keinen Platz für unsichtbare untergeordnete Elemente erhält.

  5. [optional] Überschreiben Sie die InvalidateLayout Methode, die benachrichtigt werden soll, wenn untergeordnete Elemente dem Layout hinzugefügt oder daraus entfernt werden. Weitere Informationen finden Sie unter Überschreiben der InvalidateLayout-Methode.

  6. [optional] Überschreiben Sie die OnChildMeasureInvalidated Methode, die benachrichtigt werden soll, wenn eines der untergeordneten Elemente des Layouts die Größe ändert. Weitere Informationen finden Sie unter Überschreiben der OnChildMeasureInvalidated-Methode.

Hinweis

Beachten Sie, dass die OnMeasure Außerkraftsetzung nicht aufgerufen wird, wenn die Größe des Layouts durch das übergeordnete Element und nicht durch die untergeordneten Elemente gesteuert wird. Die Außerkraftsetzung wird jedoch aufgerufen, wenn eine oder beide Einschränkungen unendlich sind oder wenn die Layoutklasse nicht standardmäßige HorizontalOptions oder VerticalOptions Eigenschaftswerte aufweist. Aus diesem Grund kann die LayoutChildren Außerkraftsetzung nicht auf untergeordnete Größen angewiesen werden, die während des OnMeasure Methodenaufrufs abgerufen wurden. LayoutChildren Stattdessen muss die Measure Methode für die untergeordneten Elemente des Layouts aufgerufen werden, bevor Sie die Layout Methode aufrufen. Alternativ kann die Größe der in der OnMeasure Außerkraftsetzung abgerufenen untergeordneten Elemente zwischengespeichert werden, um spätere Measure Aufrufe in der LayoutChildren Außerkraftsetzung zu vermeiden. Die Layoutklasse muss jedoch wissen, wann die Größen erneut abgerufen werden müssen. Weitere Informationen finden Sie unter Berechnen und Zwischenspeichern von Layoutdaten.

Die Layoutklasse kann dann genutzt werden, indem Sie sie zu einem PageHinzufügen und durch Hinzufügen von untergeordneten Elementen zum Layout verwenden. Weitere Informationen finden Sie unter "Verwenden des WrapLayout".

Erstellen eines WrapLayout

Die Beispielanwendung veranschaulicht eine Ausrichtungssensitive WrapLayout Klasse, die die untergeordneten Elemente horizontal auf der Seite anordnet und anschließend die Anzeige der nachfolgenden untergeordneten Elemente in zusätzliche Zeilen umschließt.

Die WrapLayout Klasse weist basierend auf der maximalen Größe der untergeordneten Elemente den gleichen Speicherplatz zu, der als Zellengröße bezeichnet wird. Untergeordnete Elemente, die kleiner als die Zellengröße sind, können basierend auf ihren HorizontalOptions Werten und VerticalOptions Eigenschaftswerten innerhalb der Zelle positioniert werden.

Die WrapLayout Klassendefinition wird im folgenden Codebeispiel gezeigt:

public class WrapLayout : Layout<View>
{
  Dictionary<Size, LayoutData> layoutDataCache = new Dictionary<Size, LayoutData>();
  ...
}

Berechnen und Zwischenspeichern von Layoutdaten

Die LayoutData Struktur speichert Daten zu einer Sammlung von untergeordneten Elementen in einer Reihe von Eigenschaften:

  • VisibleChildCount – die Anzahl der untergeordneten Elemente, die im Layout sichtbar sind.
  • CellSize – die maximale Größe aller untergeordneten Elemente, angepasst an die Größe des Layouts.
  • Rows – die Anzahl der Zeilen.
  • Columns – die Anzahl der Spalten.

Das layoutDataCache Feld wird verwendet, um mehrere LayoutData Werte zu speichern. Wenn die Anwendung gestartet wird, werden zwei LayoutData Objekte im layoutDataCache Wörterbuch für die aktuelle Ausrichtung zwischengespeichert – eines für die Einschränkungsargumente für die OnMeasure Außerkraftsetzung und eines für die Außerkraftsetzung und eines für die width height LayoutChildren Außerkraftsetzung. Beim Drehen des Geräts in querformatierte Ausrichtung wird die OnMeasure Außerkraftsetzung und die LayoutChildren Außerkraftsetzung erneut aufgerufen, was dazu führt, dass weitere zwei LayoutData Objekte im Wörterbuch zwischengespeichert werden. Wenn das Gerät jedoch in hochformatiert zurückgegeben wird, sind keine weiteren Berechnungen erforderlich, da die layoutDataCache bereits erforderlichen Daten enthalten sind.

Das folgende Codebeispiel zeigt die GetLayoutData Methode, mit der die Eigenschaften der LayoutData strukturierten Struktur basierend auf einer bestimmten Größe berechnet werden:

LayoutData GetLayoutData(double width, double height)
{
  Size size = new Size(width, height);

  // Check if cached information is available.
  if (layoutDataCache.ContainsKey(size))
  {
    return layoutDataCache[size];
  }

  int visibleChildCount = 0;
  Size maxChildSize = new Size();
  int rows = 0;
  int columns = 0;
  LayoutData layoutData = new LayoutData();

  // Enumerate through all the children.
  foreach (View child in Children)
  {
    // Skip invisible children.
    if (!child.IsVisible)
      continue;

    // Count the visible children.
    visibleChildCount++;

    // Get the child's requested size.
    SizeRequest childSizeRequest = child.Measure(Double.PositiveInfinity, Double.PositiveInfinity);

    // Accumulate the maximum child size.
    maxChildSize.Width = Math.Max(maxChildSize.Width, childSizeRequest.Request.Width);
    maxChildSize.Height = Math.Max(maxChildSize.Height, childSizeRequest.Request.Height);
  }

  if (visibleChildCount != 0)
  {
    // Calculate the number of rows and columns.
    if (Double.IsPositiveInfinity(width))
    {
      columns = visibleChildCount;
      rows = 1;
    }
    else
    {
      columns = (int)((width + ColumnSpacing) / (maxChildSize.Width + ColumnSpacing));
      columns = Math.Max(1, columns);
      rows = (visibleChildCount + columns - 1) / columns;
    }

    // Now maximize the cell size based on the layout size.
    Size cellSize = new Size();

    if (Double.IsPositiveInfinity(width))
      cellSize.Width = maxChildSize.Width;
    else
      cellSize.Width = (width - ColumnSpacing * (columns - 1)) / columns;

    if (Double.IsPositiveInfinity(height))
      cellSize.Height = maxChildSize.Height;
    else
      cellSize.Height = (height - RowSpacing * (rows - 1)) / rows;

    layoutData = new LayoutData(visibleChildCount, cellSize, rows, columns);
  }

  layoutDataCache.Add(size, layoutData);
  return layoutData;
}

Die GetLayoutData Methode führt die folgenden Vorgänge aus:

  • Er bestimmt, ob sich ein berechneter LayoutData Wert bereits im Cache befindet, und gibt ihn zurück, wenn er verfügbar ist.
  • Andernfalls werden alle untergeordneten Elemente aufgezählt, die Measure Methode für jedes untergeordnete Element mit einer unendlichen Breite und Höhe abgerufen und die maximale untergeordnete Größe bestimmt.
  • Vorausgesetzt, es gibt mindestens ein sichtbares untergeordnetes Element, berechnet die Anzahl der erforderlichen Zeilen und Spalten und berechnet dann eine Zellengröße für die untergeordneten Elemente basierend auf den Dimensionen der WrapLayout. Beachten Sie, dass die Zellengröße in der Regel etwas breiter ist als die maximale untergeordnete Größe, aber dass sie auch kleiner sein könnte, wenn die WrapLayout breiteste untergeordnete Oder für das höchste Kind nicht breit genug ist.
  • Er speichert den neuen LayoutData Wert im Cache.

Hinzufügen von Eigenschaften, die durch bindbare Eigenschaften gesichert werden

Die WrapLayout Klasse definiert ColumnSpacing und RowSpacing eigenschaften, deren Werte verwendet werden, um die Zeilen und Spalten im Layout zu trennen und die durch bindungsfähige Eigenschaften gesichert werden. Die bindungsfähigen Eigenschaften werden im folgenden Codebeispiel gezeigt:

public static readonly BindableProperty ColumnSpacingProperty = BindableProperty.Create(
  "ColumnSpacing",
  typeof(double),
  typeof(WrapLayout),
  5.0,
  propertyChanged: (bindable, oldvalue, newvalue) =>
  {
    ((WrapLayout)bindable).InvalidateLayout();
  });

public static readonly BindableProperty RowSpacingProperty = BindableProperty.Create(
  "RowSpacing",
  typeof(double),
  typeof(WrapLayout),
  5.0,
  propertyChanged: (bindable, oldvalue, newvalue) =>
  {
    ((WrapLayout)bindable).InvalidateLayout();
  });

Der Eigenschaft geänderte Handler jeder bindbaren Eigenschaft ruft die InvalidateLayout Methodenüberschreibung auf, um einen neuen Layoutdurchlauf für die WrapLayout. Weitere Informationen finden Sie unter Überschreiben der InvalidateLayout-Methode und Überschreiben der OnChildMeasureInvalidated-Methode.

Überschreiben der OnMeasure-Methode

Die OnMeasure Außerkraftsetzung wird im folgenden Codebeispiel gezeigt:

protected override SizeRequest OnMeasure(double widthConstraint, double heightConstraint)
{
  LayoutData layoutData = GetLayoutData(widthConstraint, heightConstraint);
  if (layoutData.VisibleChildCount == 0)
  {
    return new SizeRequest();
  }

  Size totalSize = new Size(layoutData.CellSize.Width * layoutData.Columns + ColumnSpacing * (layoutData.Columns - 1),
                layoutData.CellSize.Height * layoutData.Rows + RowSpacing * (layoutData.Rows - 1));
  return new SizeRequest(totalSize);
}

Die Außerkraftsetzung ruft die GetLayoutData Methode auf und erstellt ein SizeRequest Objekt aus den zurückgegebenen Daten, wobei auch die RowSpacing Werte und ColumnSpacing Eigenschaftswerte berücksichtigt werden. Weitere Informationen zur GetLayoutData Methode finden Sie unter Calculate and Cache Layout Data.

Wichtig

Die Measure Methoden OnMeasure sollten niemals eine unendliche Dimension anfordern, indem sie einen SizeRequest Wert mit einer Eigenschaft zurückgeben, auf die Double.PositiveInfinityfestgelegt ist. Mindestens eines der Einschränkungsargumente OnMeasure kann jedoch sein Double.PositiveInfinity.

Überschreiben der LayoutChildren-Methode

Die LayoutChildren Außerkraftsetzung wird im folgenden Codebeispiel gezeigt:

protected override void LayoutChildren(double x, double y, double width, double height)
{
  LayoutData layoutData = GetLayoutData(width, height);

  if (layoutData.VisibleChildCount == 0)
  {
    return;
  }

  double xChild = x;
  double yChild = y;
  int row = 0;
  int column = 0;

  foreach (View child in Children)
  {
    if (!child.IsVisible)
    {
      continue;
    }

    LayoutChildIntoBoundingRegion(child, new Rectangle(new Point(xChild, yChild), layoutData.CellSize));
    if (++column == layoutData.Columns)
    {
      column = 0;
      row++;
      xChild = x;
      yChild += RowSpacing + layoutData.CellSize.Height;
    }
    else
    {
      xChild += ColumnSpacing + layoutData.CellSize.Width;
    }
  }
}

Die Außerkraftsetzung beginnt mit einem Aufruf der GetLayoutData Methode und listet dann alle untergeordneten Elemente auf und positioniert sie innerhalb der Zelle der einzelnen untergeordneten Elemente. Dies wird durch Aufrufen der LayoutChildIntoBoundingRegion Methode erreicht, die verwendet wird, um ein untergeordnetes Element innerhalb eines Rechtecks basierend auf seinen HorizontalOptions Und VerticalOptions Eigenschaftswerten zu positionieren. Dies entspricht dem Aufrufen der Methode des untergeordneten Layout Elements.

Hinweis

Beachten Sie, dass das an die LayoutChildIntoBoundingRegion Methode übergebene Rechteck den gesamten Bereich enthält, in dem sich das untergeordnete Element befinden kann.

Weitere Informationen zur GetLayoutData Methode finden Sie unter Calculate and Cache Layout Data.

Überschreiben der InvalidateLayout-Methode

Die InvalidateLayout Außerkraftsetzung wird aufgerufen, wenn untergeordnete Elemente dem Layout hinzugefügt oder daraus entfernt werden, oder wenn eine der WrapLayout Eigenschaften den Wert ändert, wie im folgenden Codebeispiel gezeigt:

protected override void InvalidateLayout()
{
  base.InvalidateLayout();
  layoutInfoCache.Clear();
}

Durch die Außerkraftsetzung wird das Layout ungültig und alle zwischengespeicherten Layoutinformationen verworfen.

Hinweis

Wenn Sie verhindern möchten, dass die Layout Klasse die InvalidateLayout Methode aufruft, wenn ein untergeordnetes Element zu einem Layout hinzugefügt oder daraus entfernt wird, überschreiben Sie die ShouldInvalidateOnChildAdded Methode ShouldInvalidateOnChildRemoved und geben Sie diese zurück false. Die Layoutklasse kann dann einen benutzerdefinierten Prozess implementieren, wenn untergeordnete Elemente hinzugefügt oder entfernt werden.

Überschreiben der OnChildMeasureInvalidated-Methode

Die OnChildMeasureInvalidated Außerkraftsetzung wird aufgerufen, wenn eines der untergeordneten Elemente des Layouts die Größe ändert und im folgenden Codebeispiel dargestellt wird:

protected override void OnChildMeasureInvalidated()
{
  base.OnChildMeasureInvalidated();
  layoutInfoCache.Clear();
}

Die Außerkraftsetzung macht das untergeordnete Layout ungültig und verwirft alle zwischengespeicherten Layoutinformationen.

Verwenden des WrapLayout

Die WrapLayout Klasse kann genutzt werden, indem sie in einem Page abgeleiteten Typ platziert wird, wie im folgenden XAML-Codebeispiel veranschaulicht:

<ContentPage ... xmlns:local="clr-namespace:ImageWrapLayout">
    <ScrollView Margin="0,20,0,20">
        <local:WrapLayout x:Name="wrapLayout" />
    </ScrollView>
</ContentPage>

Der entsprechende C#-Code ist unten dargestellt:

public class ImageWrapLayoutPageCS : ContentPage
{
  WrapLayout wrapLayout;

  public ImageWrapLayoutPageCS()
  {
    wrapLayout = new WrapLayout();

    Content = new ScrollView
    {
      Margin = new Thickness(0, 20, 0, 20),
      Content = wrapLayout
    };
  }
  ...
}

Untergeordnete Elemente können dann nach WrapLayout Bedarf hinzugefügt werden. Das folgende Codebeispiel zeigt Image Elemente, die dem WrapLayoutFolgenden hinzugefügt werden:

protected override async void OnAppearing()
{
    base.OnAppearing();

    var images = await GetImageListAsync();
    if (images != null)
    {
        foreach (var photo in images.Photos)
        {
            var image = new Image
            {
                Source = ImageSource.FromUri(new Uri(photo))
            };
            wrapLayout.Children.Add(image);
        }
    }
}

async Task<ImageList> GetImageListAsync()
{
    try
    {
        string requestUri = "https://raw.githubusercontent.com/xamarin/docs-archive/master/Images/stock/small/stock.json";
        string result = await _client.GetStringAsync(requestUri);
        return JsonConvert.DeserializeObject<ImageList>(result);
    }
    catch (Exception ex)
    {
        Debug.WriteLine($"\tERROR: {ex.Message}");
    }

    return null;
}

Wenn die Seite mit dem WrapLayout Angezeigten angezeigt wird, greift die Beispielanwendung asynchron auf eine REMOTE-JSON-Datei zu, die eine Liste von Fotos enthält, erstellt ein Image Element für jedes Foto und fügt es der WrapLayoutDatei hinzu. Dies ergibt die in den folgenden Screenshots gezeigte Darstellung:

Screenshots des Beispiels für das Anwendungsporträt

Die folgenden Screenshots zeigen, WrapLayout nachdem sie in die Querformatausrichtung gedreht wurde:

Screenshot der iOS-AnwendungslandschaftScreenshot der Beispiel-Android-AnwendungslandschaftScreenshot der Beispiel-UWP-Anwendungslandschaft

Die Anzahl der Spalten in jeder Zeile hängt von der Fotogröße, der Bildschirmbreite und der Anzahl der Pixel pro geräteunabhängiger Einheit ab. Die Image Elemente laden die Fotos asynchron, und daher erhält die WrapLayout Klasse häufige Aufrufe an die LayoutChildren Methode, da jedes Image Element basierend auf dem geladenen Foto eine neue Größe erhält.