Erstellen eines benutzerdefinierten Layouts inXamarin.FormsCreate a Custom Layout in Xamarin.Forms

Beispiel herunterladen Herunterladen des BeispielsDownload Sample Download the sample

Xamarin.Formsdefiniert fünf layoutklassen – Stacklayout, AbsoluteLayout, relativelayout, Grid und flexlayout, und jede ordnet die untergeordneten Elemente auf andere Weise an. Manchmal ist es jedoch notwendig, den Seiten Inhalt mithilfe eines Layouts zu organisieren, das nicht von bereitgestellt wird Xamarin.Forms . In diesem Artikel wird erläutert, wie eine benutzerdefinierte Layoutklasse geschrieben wird, und es wird eine Orientierung-sensible wraplayout-Klasse veranschaulicht, die die untergeordneten Elemente horizontal auf der Seite anordnet und dann die Anzeige der nachfolgenden untergeordneten Elemente in zusätzliche ZeilenXamarin.Forms defines five layout classes – StackLayout, AbsoluteLayout, RelativeLayout, Grid, and FlexLayout, and each arranges its children in a different way. However, sometimes it's necessary to organize page content using a layout not provided by Xamarin.Forms. This article explains how to write a custom layout class, and demonstrates an orientation-sensitive WrapLayout class that arranges its children horizontally across the page, and then wraps the display of subsequent children to additional rows.

In Xamarin.Forms werden alle layoutklassen von der- Layout<T> Klasse abgeleitet und beschränken den generischen Typ auf View und die abgeleiteten Typen.In Xamarin.Forms, all layout classes derive from the Layout<T> class and constrain the generic type to View and its derived types. Die Layout<T> -Klasse wird wiederum von der- Layout Klasse abgeleitet, die den Mechanismus zur Positionierung und Größenanpassung von untergeordneten Elementen bereitstellt.In turn, the Layout<T> class derives from the Layout class, which provides the mechanism for positioning and sizing child elements.

Jedes visuelle Element ist für die Bestimmung seiner eigenen bevorzugten Größe zuständig, die als die angeforderte Größe bezeichnet wird.Every visual element is responsible for determining its own preferred size, which is known as the requested size. Page, Layout und Layout<View> abgeleitete Typen sind dafür verantwortlich, die Position und Größe ihrer untergeordneten Elemente relativ zu sich selbst zu bestimmen.Page, Layout, and Layout<View> derived types are responsible for determining the location and size of their child, or children, relative to themselves. Das Layout umfasst daher eine Beziehung zwischen über-und untergeordneten Elementen, bei der das übergeordnete Element bestimmt, welche Größe der untergeordneten Elemente sein sollte, aber versucht, die angeforderte Größe des untergeordneten Elements zu berücksichtigen.Therefore, layout involves a parent-child relationship, where the parent determines what the size of its children should be, but will attempt to accommodate the requested size of the child.

Xamarin.FormsZum Erstellen eines benutzerdefinierten Layouts ist ein umfassendes Verständnis der Layout-und invalidierungszyklen erforderlich.A thorough understanding of the Xamarin.Forms layout and invalidation cycles is required to create a custom layout. Diese Zyklen werden jetzt erläutert.These cycles will now be discussed.

LayoutLayout

Layout beginnt am oberen Rand der visuellen Struktur mit einer Seite und durchläuft alle Verzweigungen der visuellen Struktur, um alle visuellen Elemente auf einer Seite zu umfassen.Layout begins at the top of the visual tree with a page, and it proceeds through all branches of the visual tree to encompass every visual element on a page. Elemente, die übergeordnete Elemente anderer Elemente sind, sind für die Größenanpassung und Positionierung ihrer untergeordneten Elemente in Bezug auf sich verantwortlich.Elements that are parents to other elements are responsible for sizing and positioning their children relative to themselves.

Die- VisualElement Klasse definiert eine [ Measure ] (Xref:) Xamarin.Forms . Visualelement. Measure (System. Double, System. Double, Xamarin.Forms . Measures)) Methode, mit der ein Element für Layoutvorgänge und ein [ Layout ] (Xref:) gemessen wird Xamarin.Forms . Visualelement. Layout ( Xamarin.Forms . Rechteck))-Methode, die den rechteckigen Bereich angibt, in dem das Element gerendert wird.The VisualElement class defines a Measure method that measures an element for layout operations, and a Layout method that specifies the rectangular area the element will be rendered within. Wenn eine Anwendung gestartet wird und die erste Seite angezeigt wird, wird ein layoutcycle , der zuerst aus Measure -Aufrufen besteht und dann Layout aufruft, auf dem- Page Objekt gestartet:When an application starts and the first page is displayed, a layout cycle consisting first of Measure calls, and then Layout calls, starts on the Page object:

  1. Während des layoutcycle ist jedes übergeordnete Element für das Aufrufen der- Measure Methode auf seinen untergeordneten Elementen verantwortlich.During the layout cycle, every parent element is responsible for calling the Measure method on its children.
  2. Nachdem die untergeordneten Elemente gemessen wurden, ist jedes übergeordnete Element für das Aufrufen der-Methode für seine untergeordneten Elemente verantwortlich Layout .After the children have been measured, every parent element is responsible for calling the Layout method on its children.

Dadurch wird sichergestellt, dass alle visuellen Elemente auf der Seite Aufrufe an die Measure -Methode und die- Layout Methode empfangen.This cycle ensures that every visual element on the page receives calls to the Measure and Layout methods. Der Prozess wird in der folgenden Abbildung dargestellt:The process is shown in the following diagram:

Xamarin.FormsLayoutcycleXamarin.Forms Layout Cycle

Hinweis

Beachten Sie, dass layoutzyklen auch für eine Teilmenge der visuellen Struktur auftreten können, wenn sich etwas ändert, das sich auf das Layout auswirkt.Note that layout cycles can also occur on a subset of the visual tree if something changes to affect the layout. Dies schließt Elemente ein, die einer Auflistung hinzugefügt oder daraus entfernt werden, z. b. in einer StackLayout , eine Änderung der- IsVisible Eigenschaft eines-Elements oder eine Änderung in der Größe eines Elements.This includes items being added or removed from a collection such as in a StackLayout, a change in the IsVisible property of an element, or a change in the size of an element.

Jede Xamarin.Forms Klasse, die eine- Content Eigenschaft oder eine-Eigenschaft aufweist, Children verfügt über eine über schreibbare LayoutChildren Methode.Every Xamarin.Forms class that has a Content or a Children property has an overridable LayoutChildren method. Benutzerdefinierte layoutklassen, die von abgeleitet Layout<View> werden, müssen diese Methode überschreiben und sicherstellen, dass [ Measure ] (Xref: Xamarin.Forms . Visualelement. Measure (System. Double, System. Double, Xamarin.Forms . -Mess Flags)) und [ Layout ] (Xref: Xamarin.Forms . Visualelement. Layout ( Xamarin.Forms . Rechteck)) Methoden werden für alle untergeordneten Elemente aufgerufen, um das gewünschte benutzerdefinierte Layout bereitzustellen.Custom layout classes that derive from Layout<View> must override this method and ensure that the Measure and Layout methods are called on all the element's children, to provide the desired custom layout.

Außerdem muss jede Klasse, die von oder abgeleitet wird Layout Layout<View> , die- OnMeasure Methode überschreiben, wobei eine Layoutklasse die Größe bestimmt, die Sie benötigen, indem Sie Aufrufe an [ Measure ] (Xref:) sendet Xamarin.Forms . Visualelement. Measure (System. Double, System. Double, Xamarin.Forms . Accessreflags)) Methoden der untergeordneten Elemente.In addition, every class that derives from Layout or Layout<View> must override the OnMeasure method, which is where a layout class determines the size that it needs to be by making calls to the Measure methods of its children.

Hinweis

-Elemente bestimmen ihre Größe auf der Grundlage von Einschränkungen, die angeben, wie viel Speicherplatz für ein Element innerhalb des übergeordneten Elements des Elements verfügbar ist.Elements determine their size based on constraints, which indicate how much space is available for an element within the element's parent. Einschränkungen, die an [ Measure ] (Xref:) übermittelt werden Xamarin.Forms . Visualelement. Measure (System. Double, System. Double, Xamarin.Forms . "Messreflags") und- OnMeasure Methoden können zwischen 0 und liegen Double.PositiveInfinity .Constraints passed to the Measure and OnMeasure methods can range from 0 to Double.PositiveInfinity. Ein Element ist eingeschränktoder vollständig eingeschränkt, wenn es einen aufzurufenden [ Measure ] (Xref:) empfängt Xamarin.Forms . Visualelement. Measure (System. Double, System. Double, Xamarin.Forms . Messreflags)) Methode mit nicht unendlichen Argumenten: das Element ist auf eine bestimmte Größe beschränkt.An element is constrained, or fully constrained, when it receives a call to its Measure method with non-infinite arguments - the element is constrained to a particular size. Ein Element ist nicht eingeschränktoder teilweise eingeschränkt, wenn es einen aufzurufenden Befehl Measure mit mindestens einem Argument erhält, das gleich ist Double.PositiveInfinity – die unendliche Einschränkung kann sich als Angabe der autoSizing vorstellen.An element is unconstrained, or partially constrained, when it receives a call to its Measure method with at least one argument equal to Double.PositiveInfinity – the infinite constraint can be thought of as indicating autosizing.

InvalidierungInvalidation

Die Invalidierung ist der Prozess, durch den eine Änderung in einem Element auf einer Seite einen neuen layoutcycle auslöst.Invalidation is the process by which a change in an element on a page triggers a new layout cycle. Elemente werden als ungültig betrachtet, wenn Sie nicht mehr über die richtige Größe oder Position verfügen.Elements are considered invalid when they no longer have the correct size or position. Wenn beispielsweise die- FontSize Eigenschaft einer Button geändert wird, wird die als Button ungültig bezeichnet, da Sie nicht mehr die richtige Größe hat.For example, if the FontSize property of a Button changes, the Button is said to be invalid because it will no longer have the correct size. Wenn Sie die Größe des ändern, Button wirkt sich dies möglicherweise auf die Änderungen im Layout über den Rest einer Seite aus.Resizing the Button may then have a ripple effect of changes in layout through the rest of a page.

Elemente werden durch Aufrufen der- InvalidateMeasure Methode ungültig, wenn sich eine Eigenschaft des-Elements ändert, die möglicherweise zu einer neuen Größe des-Elements führt.Elements invalidate themselves by invoking the InvalidateMeasure method, generally when a property of the element changes that might result in a new size of the element. Diese Methode löst das- MeasureInvalidated Ereignis aus, über das die übergeordneten Handles des Elements einen neuen layoutcycle auslösen.This method fires the MeasureInvalidated event, which the element's parent handles to trigger a new layout cycle.

Die Layout -Klasse legt einen Handler für das-Ereignis für jedes untergeordnete Element fest, das der MeasureInvalidated Eigenschaft oder Auflistung hinzugefügt Content Children wurde, und trennt den Handler, wenn das untergeordnete Element entfernt wird.The Layout class sets a handler for the MeasureInvalidated event on every child added to its Content property or Children collection, and detaches the handler when the child is removed. Daher wird jedes Element in der visuellen Struktur, das über untergeordnete Elemente verfügt, benachrichtigt, wenn eines der untergeordneten Elemente die Größe ändert.Therefore, every element in the visual tree that has children is alerted whenever one of its children changes size. Im folgenden Diagramm wird veranschaulicht, wie eine Änderung in der Größe eines Elements in der visuellen Strukturänderungen verursachen kann, die die Struktur aufschlagen:The following diagram illustrates how a change in the size of an element in the visual tree can cause changes that ripple up the tree:

Invalidierung in der visuellen Struktur

Allerdings Layout versucht die-Klasse, die Auswirkungen einer Änderung in der Größe eines Kindes auf das Layout einer Seite einzuschränken.However, the Layout class attempts to restrict the impact of a change in a child's size on the layout of a page. Wenn das Layout der Größe eingeschränkt ist, wirkt sich eine Änderung der untergeordneten Größe nicht auf eine höhere Größe als das übergeordnete Layout in der visuellen Struktur aus.If the layout is size constrained, then a child size change does not affect anything higher than the parent layout in the visual tree. In der Regel wirkt sich eine Änderung der Größe eines Layouts jedoch darauf aus, wie das Layout seine untergeordneten Elemente anordnet.However, usually a change in the size of a layout affects how the layout arranges its children. Daher wird durch jede Änderung in der Größe eines Layouts ein layoutcycle für das Layout gestartet, und das Layout empfängt Aufrufe der OnMeasure -und- LayoutChildren Methoden.Therefore, any change in a layout's size will start a layout cycle for the layout, and the layout will receive calls to its OnMeasure and LayoutChildren methods.

Die- Layout Klasse definiert auch eine- InvalidateLayout Methode, die einen ähnlichen Zweck hat wie die- InvalidateMeasure Methode.The Layout class also defines an InvalidateLayout method that has a similar purpose to the InvalidateMeasure method. Die- InvalidateLayout Methode sollte immer dann aufgerufen werden, wenn eine Änderung vorgenommen wird, die sich darauf auswirkt, wie das Layout seine untergeordneten Elemente positioniert.The InvalidateLayout method should be invoked whenever a change is made that affects how the layout positions and sizes its children. Beispielsweise ruft die- Layout Klasse die- InvalidateLayout Methode auf, wenn ein untergeordnetes Element einem Layout hinzugefügt oder daraus entfernt wird.For example, the Layout class invokes the InvalidateLayout method whenever a child is added to or removed from a layout.

InvalidateLayoutKann überschrieben werden, um einen Cache zu implementieren, um wiederkehrende Aufrufe von [ Measure ] (Xref:) zu minimieren Xamarin.Forms . Visualelement. Measure (System. Double, System. Double, Xamarin.Forms . Accessreflags)) Methoden der untergeordneten Elemente des Layouts.The InvalidateLayout can be overridden to implement a cache to minimize repetitive invocations of the Measure methods of the layout's children. Durch das Überschreiben der- InvalidateLayout Methode wird eine Benachrichtigung darüber bereitgestellt, wann dem Layout untergeordnete Elemente hinzugefügt oder daraus entfernt werden.Overriding the InvalidateLayout method will provide a notification of when children are added to or removed from the layout. Auf ähnliche Weise OnChildMeasureInvalidated kann die-Methode überschrieben werden, um eine Benachrichtigung bereitzustellen, wenn sich eines der untergeordneten Layoutelemente ändert.Similarly, the OnChildMeasureInvalidated method can be overridden to provide a notification when one of the layout's children changes size. Bei beiden Methoden Überschreibungen sollte ein benutzerdefiniertes Layout reagieren, indem der Cache gelöscht wird.For both method overrides, a custom layout should respond by clearing the cache. Weitere Informationen finden Sie unter berechnen und Zwischenspeichern von Layoutdaten.For more information, see Calculate and Cache Layout Data.

Erstellen eines benutzerdefinierten LayoutsCreate a Custom Layout

Der Vorgang zum Erstellen eines benutzerdefinierten Layouts lautet wie folgt:The process for creating a custom layout is as follows:

  1. Erstellen Sie eine von der Layout<View>-Klasse abgeleitete Klasse.Create a class that derives from the Layout<View> class. Weitere Informationen finden Sie unter Erstellen eines wraplayout.For more information, see Create a WrapLayout.

  2. [optional] Fügen Sie für alle Parameter, die für die Layoutklasse festgelegt werden sollen, Eigenschaften hinzu, die von bindbaren Eigenschaften unterstützt werden.[optional] Add properties, backed by bindable properties, for any parameters that should be set on the layout class. Weitere Informationen finden Sie unter Hinzufügen von Eigenschaften, die von bindbaren Eigenschaften unterstütztwerden.For more information, see Add Properties Backed by Bindable Properties.

  3. Überschreiben OnMeasure Sie die-Methode, um [ Measure ] (Xref:) aufzurufen Xamarin.Forms . Visualelement. Measure (System. Double, System. Double, Xamarin.Forms . "Messreflags")-Methode für alle untergeordneten Layoutelemente und gibt eine angeforderte Größe für das Layout zurück.Override the OnMeasure method to invoke the Measure method on all the layout's children, and return a requested size for the layout. Weitere Informationen finden Sie unter über Schreiben der onmeasure-Methode.For more information, see Override the OnMeasure Method.

  4. Überschreiben LayoutChildren Sie die-Methode, um [ Layout ] (Xref:) aufzurufen Xamarin.Forms . Visualelement. Layout ( Xamarin.Forms . Rechteck))-Methode für alle untergeordneten Elemente des Layouts.Override the LayoutChildren method to invoke the Layout method on all the layout's children. Fehler beim Aufrufen von [ Layout ] (Xref: Xamarin.Forms . Visualelement. Layout ( Xamarin.Forms . Rechteck)) die Methode für jedes untergeordnete Element in einem Layout führt dazu, dass das untergeordnete Element niemals eine korrekte Größe oder Position erhält, sodass das untergeordnete Element auf der Seite nicht sichtbar wird.Failure to invoke the Layout method on each child in a layout will result in the child never receiving a correct size or position, and hence the child will not become visible on the page. Weitere Informationen finden Sie unter über Schreiben der layoutchildren-Methode.For more information, see Override the LayoutChildren Method.

    Hinweis

    Wenn Sie untergeordnete Elemente in der OnMeasure -und der- LayoutChildren Überschreibung aufzählen, überspringen Sie alle untergeordneten Elemente, deren- IsVisible Eigenschaft auf falseWhen enumerating children in the OnMeasure and LayoutChildren overrides, skip any child whose IsVisible property is set to false. Dadurch wird sichergestellt, dass das benutzerdefinierte Layout keinen Platz für unsichtbare untergeordnete Elemente hinterlässt.This will ensure that the custom layout won't leave space for invisible children.

  5. [optional] Überschreiben InvalidateLayout Sie die Methode, die benachrichtigt werden soll, wenn dem Layout untergeordnete Elemente hinzugefügt oder daraus entfernt werden.[optional] Override the InvalidateLayout method to be notified when children are added to or removed from the layout. Weitere Informationen finden Sie unter außer Kraft setzen der invalidatelayout-Methode.For more information, see Override the InvalidateLayout Method.

  6. [optional] Überschreiben OnChildMeasureInvalidated Sie die Methode, die benachrichtigt werden soll, wenn eine der untergeordneten Layoutelemente die Größe ändert.[optional] Override the OnChildMeasureInvalidated method to be notified when one of the layout's children changes size. Weitere Informationen finden Sie unter über Schreiben der onchildmessreinvaliveralteten-Methode.For more information, see Override the OnChildMeasureInvalidated Method.

Hinweis

Beachten Sie, dass die OnMeasure außer Kraft Setzung nicht aufgerufen wird, wenn die Größe des Layouts durch das übergeordnete Element und nicht durch seine untergeordneten Elemente geregelt wird.Note that the OnMeasure override won't be invoked if the size of the layout is governed by its parent, rather than its children. Die außer Kraft setzung wird jedoch aufgerufen, wenn eine oder beide der Einschränkungen unendlich sind oder wenn die Layoutklasse nicht standardmäßige- HorizontalOptions oder- VerticalOptions Eigenschaftswerte aufweist.However, the override will be invoked if one or both of the constraints are infinite, or if the layout class has non-default HorizontalOptions or VerticalOptions property values. Aus diesem Grund kann die LayoutChildren Überschreibung nicht auf untergeordnete Größen beruhen, die während des OnMeasure Methoden Aufrufes abgerufen wurden.For this reason, the LayoutChildren override can't rely on child sizes obtained during the OnMeasure method call. Stattdessen LayoutChildren muss das [ Measure ] (Xref:) aufrufen Xamarin.Forms . Visualelement. Measure (System. Double, System. Double, Xamarin.Forms . Accessreflags))-Methode für die untergeordneten Elemente des Layouts vor dem Aufrufen von [ Layout ] (Xref: Xamarin.Forms . Visualelement. Layout ( Xamarin.Forms . Rechteck))-Methode.Instead, LayoutChildren must invoke the Measure method on the layout's children, before invoking the Layout method. Alternativ kann die Größe der untergeordneten Elemente, die in der Überschreibung abgerufen OnMeasure werden, zwischengespeichert werden, um spätere Aufrufe in der Überschreibung zu vermeiden Measure LayoutChildren . die Layoutklasse muss jedoch wissen, wann die Größen wieder abgerufen werden müssen.Alternatively, the size of the children obtained in the OnMeasure override can be cached to avoid later Measure invocations in the LayoutChildren override, but the layout class will need to know when the sizes need to be obtained again. Weitere Informationen finden Sie unter berechnen und Zwischenspeichern von Layoutdaten.For more information, see Calculate and Cache Layout Data.

Die Layoutklasse kann dann verwendet werden, indem Sie zu einem hinzugefügt wird Page , und indem dem Layout untergeordnete Elemente hinzugefügt werden.The layout class can then be consumed by adding it to a Page, and by adding children to the layout. Weitere Informationen finden Sie unter Verwenden von wraplayout.For more information, see Consume the WrapLayout.

Erstellen eines wraplayoutCreate a WrapLayout

Die Beispielanwendung veranschaulicht eine Orientierungs abhängige Klasse, die die untergeordneten Elemente WrapLayout horizontal auf der Seite anordnet und dann die Anzeige der nachfolgenden untergeordneten Elemente in zusätzliche Zeilen umschließt.The sample application demonstrates an orientation-sensitive WrapLayout class that arranges its children horizontally across the page, and then wraps the display of subsequent children to additional rows.

Die- WrapLayout Klasse ordnet die gleiche Menge an Speicherplatz für jedes untergeordnete Element, das als Zellen Größebezeichnet wird, basierend auf der maximalen Größe der untergeordneten Elemente zu.The WrapLayout class allocates the same amount of space for each child, known as the cell size, based on the maximum size of the children. Untergeordnete Elemente, die kleiner als die Zellen Größe sind, können auf der Grundlage ihrer HorizontalOptions Eigenschaftswerte und innerhalb der Zelle positioniert werden VerticalOptions .Children smaller than the cell size can be positioned within the cell based on their HorizontalOptions and VerticalOptions property values.

Die WrapLayout Klassendefinition wird im folgenden Codebeispiel gezeigt:The WrapLayout class definition is shown in the following code example:

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

Berechnen und Zwischenspeichern von LayoutdatenCalculate and Cache Layout Data

Die- LayoutData Struktur speichert Daten zu einer Auflistung von untergeordneten Elementen in einer Reihe von Eigenschaften:The LayoutData structure stores data about a collection of children in a number of properties:

  • VisibleChildCount– die Anzahl der untergeordneten Elemente, die im Layout sichtbar sind.VisibleChildCount – the number of children that are visible in the layout.
  • CellSize– die maximale Größe aller untergeordneten Elemente entsprechend der Größe des Layouts.CellSize – the maximum size of all the children, adjusted to the size of the layout.
  • Rows– die Anzahl der Zeilen.Rows – the number of rows.
  • Columns– die Anzahl der Spalten.Columns – the number of columns.

Das- layoutDataCache Feld wird verwendet, um mehrere Werte zu speichern LayoutData .The layoutDataCache field is used to store multiple LayoutData values. Wenn die Anwendung gestartet wird, werden zwei LayoutData Objekte im layoutDataCache Wörterbuch für die aktuelle Ausrichtung zwischengespeichert – eine für die Einschränkungs Argumente der Überschreibung OnMeasure und eine für das width -Argument und das- height Argument für die LayoutChildren außer Kraft Setzung.When the application starts, two LayoutData objects will be cached into the layoutDataCache dictionary for the current orientation – one for the constraint arguments to the OnMeasure override, and one for the width and height arguments to the LayoutChildren override. Wenn Sie das Gerät in Querformat drehen, OnMeasure werden die außer Kraft Setzung und die LayoutChildren außer Kraft Setzung erneut aufgerufen, was dazu führt, dass weitere zwei LayoutData Objekte im Wörterbuch zwischengespeichert werden.When rotating the device into landscape orientation, the OnMeasure override and the LayoutChildren override will again be invoked, which will result in another two LayoutData objects being cached into the dictionary. Bei der Rückgabe des Geräts an die Hochformat Ausrichtung sind jedoch keine weiteren Berechnungen erforderlich, da layoutDataCache bereits über die erforderlichen Daten verfügt.However, when returning the device to portrait orientation, no further calculations are required because the layoutDataCache already has the required data.

Im folgenden Codebeispiel wird die- GetLayoutData Methode veranschaulicht, die die Eigenschaften der LayoutData strukturierten auf der Grundlage einer bestimmten Größe berechnet:The following code example shows the GetLayoutData method, which calculates the properties of the LayoutData structured based on a particular size:

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:The GetLayoutData method performs the following operations:

  • Es bestimmt, ob ein berechneter LayoutData Wert bereits im Cache vorhanden ist, und gibt ihn zurück, wenn er verfügbar ist.It determines whether a calculated LayoutData value is already in the cache and returns it if it's available.
  • Andernfalls werden alle untergeordneten Elemente aufgelistet, Measure wobei die-Methode für jedes untergeordnete Element mit einer unendlichen Breite und Höhe aufgerufen und die maximale untergeordnete Größe bestimmt wird.Otherwise, it enumerates through all the children, invoking the Measure method on each child with an infinite width and height, and determines the maximum child size.
  • Wenn mindestens ein untergeordnetes Element vorhanden ist, wird die Anzahl der erforderlichen Zeilen und Spalten berechnet und dann basierend auf den Dimensionen von eine Zellgröße für die untergeordneten Elemente berechnet WrapLayout .Provided that there's at least one visible child, it calculates the number of rows and columns required, and then calculates a cell size for the children based on the dimensions of the WrapLayout. Beachten Sie, dass die Zellen Größe in der Regel etwas breiter ist als die maximale untergeordnete Größe, aber Sie kann auch kleiner sein, wenn der für das größte untergeordnete Element WrapLayout nicht breit genug ist oder für das höchste untergeordnete Element hoch genug ist.Note that the cell size is usually slightly wider than the maximum child size, but that it could also be smaller if the WrapLayout isn't wide enough for the widest child or tall enough for the tallest child.
  • Der neue Wert wird LayoutData im Cache gespeichert.It stores the new LayoutData value in the cache.

Hinzufügen von Eigenschaften, die von bindbaren Eigenschaften gestützt werdenAdd Properties Backed by Bindable Properties

Die WrapLayout -Klasse definiert die ColumnSpacing -Eigenschaft und die-Eigenschaft RowSpacing , deren Werte zum Trennen der Zeilen und Spalten im Layout verwendet werden und die durch bindbare Eigenschaften gestützt werden.The WrapLayout class defines ColumnSpacing and RowSpacing properties, whose values are used to separate the rows and columns in the layout, and which are backed by bindable properties. Die bindbaren Eigenschaften werden im folgenden Codebeispiel gezeigt:The bindable properties are shown in the following code example:

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 von der Eigenschaft geänderte Handler jeder bindbaren Eigenschaft ruft die InvalidateLayout Methoden Überschreibung auf, um eine neue layoutdurchführung auf dem zu initiieren WrapLayout .The property-changed handler of each bindable property invokes the InvalidateLayout method override to trigger a new layout pass on the WrapLayout. Weitere Informationen finden Sie unter außer Kraft setzen der invalidatelayout-Methode und über Schreiben der onchildmessreinvaliveralteten-Methode.For more information, see Override the InvalidateLayout Method and Override the OnChildMeasureInvalidated Method.

Überschreiben der onmeasure-MethodeOverride the OnMeasure Method

Die OnMeasure außer Kraft setzung wird im folgenden Codebeispiel gezeigt:The OnMeasure override is shown in the following code example:

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-Überschreibung ruft die- GetLayoutData Methode auf und erstellt ein- SizeRequest Objekt aus den zurückgegebenen Daten, während die RowSpacing -und- ColumnSpacing Eigenschaftswerte berücksichtigt werden.The override invokes the GetLayoutData method and constructs a SizeRequest object from the returned data, while also taking into account the RowSpacing and ColumnSpacing property values. Weitere Informationen zur GetLayoutData -Methode finden Sie unter berechnen und Zwischenspeichern von Layoutdaten.For more information about the GetLayoutData method, see Calculate and Cache Layout Data.

Wichtig

[ Measure ] (Xref: Xamarin.Forms . Visualelement. Measure (System. Double, System. Double, Xamarin.Forms . "Messreflags") und OnMeasure Methoden sollten nie eine unendliche Dimension anfordern, indem SizeRequest Sie einen Wert zurückgeben, dessen-Eigenschaft auf festgelegt ist Double.PositiveInfinity .The Measure and OnMeasure methods should never request an infinite dimension by returning a SizeRequest value with a property set to Double.PositiveInfinity. Allerdings kann mindestens eines der Einschränkungs Argumente OnMeasure sein Double.PositiveInfinity .However, at least one of the constraint arguments to OnMeasure can be Double.PositiveInfinity.

Überschreiben der layoutchildren-MethodeOverride the LayoutChildren Method

Die LayoutChildren außer Kraft setzung wird im folgenden Codebeispiel gezeigt:The LayoutChildren override is shown in the following code example:

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ßer Kraft Setzung beginnt mit einem aufzurufenden- GetLayoutData Methode und listet dann alle untergeordneten Elemente auf, um die Größe und Position innerhalb der Zelle der einzelnen untergeordneten Elemente zu platzieren.The override begins with a call to the GetLayoutData method, and then enumerates all of the children to size and position them within each child's cell. Dies wird durch Aufrufen von [ LayoutChildIntoBoundingRegion ] (Xref:) erreicht Xamarin.Forms . Layout. layoutchildinteboundingregion ( Xamarin.Forms . Visualelement, Xamarin.Forms . Rechteck))-Methode, die verwendet wird, um ein untergeordnetes Element innerhalb eines Rechtecks auf der Grundlage seiner HorizontalOptions VerticalOptions Eigenschaftswerte zu positionieren.This is achieved by invoking the LayoutChildIntoBoundingRegion method, which is used to position a child within a rectangle based on its HorizontalOptions and VerticalOptions property values. Dies entspricht dem Abrufen des untergeordneten Elements [ Layout ] (Xref: Xamarin.Forms . Visualelement. Layout ( Xamarin.Forms . Rechteck))-Methode.This is equivalent to making a call to the child's Layout method.

Hinweis

Beachten Sie, dass das an die-Methode über gegebene Rechteck LayoutChildIntoBoundingRegion den gesamten Bereich enthält, in dem sich das untergeordnete Element befinden kann.Note that the rectangle passed to the LayoutChildIntoBoundingRegion method includes the whole area in which the child can reside.

Weitere Informationen zur GetLayoutData -Methode finden Sie unter berechnen und Zwischenspeichern von Layoutdaten.For more information about the GetLayoutData method, see Calculate and Cache Layout Data.

Überschreiben der invalidatelayout-MethodeOverride the InvalidateLayout Method

Die InvalidateLayout außer Kraft setzung 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:The InvalidateLayout override is invoked when children are added to or removed from the layout, or when one of the WrapLayout properties changes value, as shown in the following code example:

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

Die außer Kraft Setzung macht das Layout ungültig und verwirft alle zwischengespeicherten Layoutinformationen.The override invalidates the layout and discards all the cached layout information.

Hinweis

Um zu verhindern, dass die-Klasse die- Layout InvalidateLayout Methode aufruft, wenn ein untergeordnetes Element hinzugefügt oder aus einem Layout entfernt wird, überschreiben Sie [ ShouldInvalidateOnChildAdded ] (Xref: Xamarin.Forms . Layout. schulendvalidateonchildadded ( Xamarin.Forms . View)) und [ ShouldInvalidateOnChildRemoved ] (Xref: Xamarin.Forms . Layout. Schulter-validateonchildreverschohe ( Xamarin.Forms . View))-Methoden und geben zurück false .To stop the Layout class invoking the InvalidateLayout method whenever a child is added to or removed from a layout, override the ShouldInvalidateOnChildAdded and ShouldInvalidateOnChildRemoved methods, and return false. Die Layoutklasse kann dann einen benutzerdefinierten Prozess implementieren, wenn untergeordnete Elemente hinzugefügt oder entfernt werden.The layout class can then implement a custom process when children are added or removed.

Überschreiben der onchildmessreinvaliveralteten-MethodeOverride the OnChildMeasureInvalidated Method

Die OnChildMeasureInvalidated außer Kraft setzung wird aufgerufen, wenn eine der untergeordneten Layoutelemente die Größe ändert, und wird im folgenden Codebeispiel gezeigt:The OnChildMeasureInvalidated override is invoked when one of the layout's children changes size, and is shown in the following code example:

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

Die Überschreibung macht das untergeordnete Layout ungültig und verwirft alle zwischengespeicherten Layoutinformationen.The override invalidates the child layout, and discards all of the cached layout information.

Verwenden des wraplayoutConsume the WrapLayout

Die WrapLayout Klasse kann verwendet werden, indem Sie Sie Page in einem abgeleiteten Typ platzieren, wie im folgenden XAML-Codebeispiel veranschaulicht:The WrapLayout class can be consumed by placing it on a Page derived type, as demonstrated in the following XAML code example:

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

Der entsprechende c#-Code wird unten dargestellt:The equivalent C# code is shown below:

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 der nach Bedarf hinzugefügt werden WrapLayout .Children can then be added to the WrapLayout as required. Das folgende Codebeispiel zeigt Image Elemente, die dem hinzugefügt werden WrapLayout :The following code example shows Image elements being added to the WrapLayout:

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 angezeigt wird, greift die Beispielanwendung asynchron auf eine Remote-JSON-Datei mit einer Liste von Fotos zu, erstellt ein Image -Element für jedes Foto und fügt Sie hinzu WrapLayout .When the page containing the WrapLayout appears, the sample application asynchronously accesses a remote JSON file containing a list of photos, creates an Image element for each photo, and adds it to the WrapLayout. Dies ergibt die in den folgenden Screenshots gezeigte Darstellung:This results in the appearance shown in the following screenshots:

Beispiel Bildschirm Screenshots für Anwendungen

Die folgenden Screenshots zeigen die WrapLayout , nachdem Sie in Querformat gedreht wurde:The following screenshots show the WrapLayout after it's been rotated to landscape orientation:

Beispiel: Beispiel für IOS-Anwendungslandschaft Screenshot  Beispiel für Android-Anwendungslandschaft Screenshot  Beispiel für UWP Anwendungslandschaft ScreenshotSample iOS Application Landscape Screenshot Sample Android Application Landscape Screenshot Sample UWP Application Landscape Screenshot

Die Anzahl der Spalten in den einzelnen Zeilen hängt von der Fotogröße, der Bildschirmbreite und der Anzahl der Pixel pro geräteunabhängigen Einheit ab.The number of columns in each row depends on the photo size, the screen width, and the number of pixels per device-independent unit. Die Image Elemente werden von den-Elementen asynchron geladen, und daher empfängt die- WrapLayout Klasse Häufige Aufrufe an die- LayoutChildren Methode, da jedes Image Element auf der Grundlage des geladenen Fotos eine neue Größe erhält.The Image elements asynchronously load the photos, and therefore the WrapLayout class will receive frequent calls to its LayoutChildren method as each Image element receives a new size based on the loaded photo.