Özel düzenler

Browse sample. Örneğe göz atın

.NET Çok Platformlu Uygulama Kullanıcı Arabirimi (.NET MAUI), her birinin alt öğelerini farklı bir şekilde yerleştiren birden çok düzen sınıfı tanımlar. Düzen, bu görünümlerin düzen içinde nasıl düzenlendiğini tanımlayan kurallar ve özelliklere sahip görünümlerin listesi olarak düşünülebilir. Düzenlere örnek olarak Grid, AbsoluteLayoutve VerticalStackLayoutverilebilir.

.NET MAUI düzen sınıfları soyut Layout sınıftan türetilir. Bu sınıf, platformlar arası düzeni ve ölçümü düzen yöneticisi sınıfına devreder. sınıfı Layout , türetilmiş düzenlerin düzen yöneticisini belirtmek için kullanabileceği geçersiz kılınabilir CreateLayoutManager() bir yöntem de içerir.

Her düzen yöneticisi sınıfı, ve ArrangeChildren uygulamalarının ILayoutManager sağlanması gerektiğini belirten Measure arabirimini uygular:

  • Uygulama Measure , düzendeki her görünümü çağırır IView.Measure ve kısıtlamalara göre düzenin toplam boyutunu döndürür.
  • Uygulama, ArrangeChildren her görünümün düzenin sınırları içinde nereye yerleştirileceğini belirler ve her görünüme uygun sınırlarıyla çağrılar Arrange . Dönüş değeri, düzenin gerçek boyutudur.

.NET MAUI'nin düzenleri, düzenlerini işlemek için önceden tanımlanmış düzen yöneticilerine sahiptir. Ancak bazen sayfa içeriğini .NET MAUI tarafından sağlanmayan bir düzen kullanarak düzenlemek gerekir. Bu, .NET MAUI'nin platformlar arası düzen sürecinin nasıl çalıştığını anlamanızı gerektiren kendi özel düzeninizi oluşturarak elde edilebilir.

Düzen işlemi

.NET MAUI'nin platformlar arası düzen süreci, her platformdaki yerel düzen işleminin üzerine inşa eder. Genellikle, düzen işlemi yerel düzen sistemi tarafından başlatılır. Platformlar arası işlem, yerel düzen sistemi tarafından ölçülmesinin veya düzenlenmesinin bir sonucu olarak bir düzen veya içerik denetimi tarafından başlatıldığında çalıştırılır.

Not

Her platform düzeni biraz farklı işler. Ancak.NET MAUI'nin platformlar arası düzen süreci mümkün olduğunca platformdan bağımsız olmayı hedeflemektedir.

Aşağıdaki diyagramda yerel düzen sisteminin düzen ölçümlerini başlattığı işlem gösterilmektedir:

The process for layout measurement in .NET MAUI

Tüm .NET MAUI düzenleri her platformda tek bir yedekleme görünümüne sahiptir:

  • Android'de bu destek görünümü şeklindedir LayoutViewGroup.
  • iOS ve Mac Catalyst'te bu destek görünümü şeklindedir LayoutView.
  • Windows'ta bu destek görünümü şeklindedir LayoutPanel.

Bir platform için yerel düzen sistemi bu yedekleme görünümlerinden birinin ölçümünün istendiğinde, yedekleme görünümü yöntemini çağırır Layout.CrossPlatformMeasure . Bu, denetimin yerel düzen sisteminden .NET MAUI'nin düzen sistemine geçirildiği noktadır. Layout.CrossPlatformMeasure düzen yöneticilerinin Measure yöntemini çağırır. Bu yöntem, düzendeki her görünümü çağırarak IView.Measure alt görünümleri ölçmekle sorumludur. Görünüm yerel denetimini ölçer ve özelliğini bu ölçüme göre güncelleştirir DesiredSize . Bu değer, yönteminin sonucu olarak yedekleme görünümüne CrossPlatformMeasure döndürülür. Yedekleme görünümü, yapması gereken iç işlemeyi gerçekleştirir ve ölçülen boyutunu platforma döndürür.

Aşağıdaki diyagramda yerel düzen sisteminin düzen düzenlemesini başlattığı işlem gösterilmektedir:

The process for layout arrangement in .NET MAUI

Bir platform için yerel düzen sistemi bu yedekleme görünümlerinden birinin düzenlemesini veya düzenini istediğinde, yedekleme görünümü yöntemini çağırır Layout.CrossPlatformArrange . Bu, denetimin yerel düzen sisteminden .NET MAUI'nin düzen sistemine geçirildiği noktadır. Layout.CrossPlatformArrange düzen yöneticilerinin ArrangeChildren yöntemini çağırır. Bu yöntem, her görünümün düzenin sınırları içinde nereye yerleştirilmesi gerektiğini belirlemekle sorumludur ve konumunu ayarlamak için her görünüme çağrılar Arrange . Düzenin boyutu, yönteminin sonucu olarak yedekleme görünümüne CrossPlatformArrange döndürülür. Yedekleme görünümü, yapması gereken tüm iç işlemleri gerçekleştirir ve gerçek boyutu platforma döndürür.

Not

ILayoutManager.Measure çağrılmadan önce ArrangeChildren birden çok kez çağrılabilir, çünkü bir platformun görünümleri düzenlemeden önce bazı tahmini ölçümler yapması gerekebilir.

Özel düzen yaklaşımları

Özel düzen oluşturmak için iki ana yaklaşım vardır:

  1. Genellikle var olan bir düzen türünün veya Layoutöğesinin alt sınıfı olan özel bir düzen türü oluşturun ve özel düzen türünüzde geçersiz kılın CreateLayoutManager() . Ardından, özel düzen mantığınızı içeren bir ILayoutManager uygulama sağlayın. Daha fazla bilgi için bkz . Özel düzen türü oluşturma.
  2. uygulayan ILayoutManagerFactorybir tür oluşturarak mevcut düzen türünün davranışını değiştirin. Ardından, .NET MAUI'nin var olan düzen için varsayılan düzen yöneticisini özel düzen mantığınızı içeren kendi ILayoutManager uygulamanızla değiştirmek için bu düzen yöneticisi fabrikasını kullanın. Daha fazla bilgi için bkz . Var olan düzenin davranışını değiştirme.

Özel düzen türü oluşturma

Özel düzen türü oluşturma işlemi şu şekildedir:

  1. Var olan bir düzen türünü veya sınıfı alt sınıfa ekleyen Layout ve özel düzen türünüzde geçersiz kılan CreateLayoutManager() bir sınıf oluşturun. Daha fazla bilgi için bkz . Düzeni alt sınıflama.

  2. Mevcut bir düzen yöneticisinden türetilen veya arabirimi doğrudan uygulayan ILayoutManager bir düzen yöneticisi sınıfı oluşturun. Düzen yöneticisi sınıfınızda şunları yapmalısınız:

    1. Kısıtlamalarına göre düzenin Measure toplam boyutunu hesaplamak için yöntemini geçersiz kılın veya uygulayın.
    2. Düzenin ArrangeChildren içindeki tüm alt öğeleri boyutlandırmak ve konumlandırmak için yöntemini geçersiz kılın veya uygulayın.

    Daha fazla bilgi için bkz . Düzen yöneticisi oluşturma.

  3. Özel düzen türünüzü bir Pageöğesine ekleyerek ve düzene alt öğeler ekleyerek kullanın. Daha fazla bilgi için bkz . Düzen türünü kullanma.

Bu işlemi göstermek için yönlendirmeye duyarlı HorizontalWrapLayout bir kullanılır. HorizontalWrapLayout , alt öğelerini sayfada yatay olarak düzenlemesine benzer HorizontalStackLayout . Ancak kapsayıcısının sağ kenarıyla karşılaştığında alt öğeleri yeni bir satıra sarmalar

Not

Örnek, özel düzen oluşturmayı anlamak için kullanılabilecek ek özel düzenler tanımlar.

Düzeni alt sınıflama

Özel bir düzen türü oluşturmak için önce var olan bir düzen türünü veya sınıfını alt sınıflamanız Layout gerekir. Ardından, düzen türünüzde geçersiz kılın CreateLayoutManager() ve düzen türünüz için düzen yöneticisinin yeni bir örneğini döndürin:

using Microsoft.Maui.Layouts;

public class HorizontalWrapLayout : HorizontalStackLayout
{
    protected override ILayoutManager CreateLayoutManager()
    {
        return new HorizontalWrapLayoutManager(this);
    }
}

HorizontalWrapLayout , düzen işlevselliğini kullanmak için 'den HorizontalStackLayout türetilir. .NET MAUI düzenleri, platformlar arası düzeni ve ölçümü düzen yöneticisi sınıfına devreder. Bu nedenle geçersiz kılma, CreateLayoutManager() sonraki bölümde ele alınan düzen yöneticisi olan sınıfın yeni bir örneğini HorizontalWrapLayoutManager döndürür.

Düzen yöneticisi oluşturma

Düzen yöneticisi sınıfı, özel düzen türünüz için platformlar arası düzen ve ölçüm gerçekleştirmek için kullanılır. Var olan bir düzen yöneticisinden türetilmelidir veya doğrudan arabirimi uygulamalıdır ILayoutManager . HorizontalWrapLayoutManager , temel işlevselliğini kullanabilmesi ve devralma hiyerarşisindeki üyelere erişebilmesi için şu kaynaktan HorizontalStackLayoutManager türetilir:

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)
    {
    }
}

HorizontalWrapLayoutManager Oluşturucu, türün HorizontalWrapLayout bir örneğini bir alanda depolar, böylece düzen yöneticisinin tamamında erişilebilir. Düzen yöneticisi ayrıca sınıfından Measure ve ArrangeChildren yöntemlerini HorizontalStackLayoutManager geçersiz kılar. Bu yöntemler, özel düzeninizi uygulamak için mantığı tanımlayabileceğiniz yöntemlerdir.

Düzen boyutunu ölçme

Uygulamanın amacı ILayoutManager.Measure , düzenin toplam boyutunu hesaplamaktır. Bunu, düzendeki her alt öğeyi çağırarak IView.Measure yapmalıdır. Ardından bu verileri kullanarak kısıtlamalarına göre düzenin toplam boyutunu hesaplamalı ve döndürmelidir.

Aşağıdaki örnekte sınıfı için Measure uygulama gösterilmektedir HorizontalWrapLayoutManager :

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

Measure yöntemi, düzendeki tüm görünür alt öğeleri numaralandırır ve yöntemi her alt öğede çağırırIView.Measure. Ardından ve özelliklerinin kısıtlamalarını ve değerlerini dikkate alarak düzenin PaddingSpacing toplam boyutunu döndürür. Düzenin ResolveConstraints toplam boyutunun kısıtlamalarına uyduğundan emin olmak için yöntemi çağrılır.

Önemli

Uygulamadaki ILayoutManager.Measure alt öğeleri numaralandırırken, özelliği olarak ayarlanmış Collapsedolan Visibility tüm alt öğeleri atlayın. Bu, özel düzenin görünmez çocuklar için alan bırakmamasını sağlar.

Düzende alt öğeleri düzenleme

Uygulamanın amacı ArrangeChildren , tüm alt öğeleri düzen içinde boyutlandırmak ve konumlandırmaktır. Her çocuğun düzenin sınırları içinde nereye yerleştirilmesi gerektiğini belirlemek için, her alt öğeyi uygun sınırlarıyla çağırmalıdır Arrange . Daha sonra düzenin gerçek boyutunu temsil eden bir değer döndürmelidir.

Uyarı

Düzendeki her alt öğede yönteminin çağrılmaması ArrangeChildren , çocuğun hiçbir zaman doğru boyut veya konum almamasına neden olur ve bu nedenle çocuk sayfada görünmez.

Aşağıdaki örnekte sınıfı için ArrangeChildren uygulama gösterilmektedir HorizontalWrapLayoutManager :

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

yöntemi, ArrangeChildren düzendeki görünür tüm alt öğeleri düzen içinde boyutlandırmak ve konumlandırmak için numaralandırır. Bunu, temel alınan düzenin ve Spacing değerlerini dikkate Padding alan uygun sınırlara sahip her alt öğeyi çağırarak Arrange yapar. Ardından düzenin gerçek boyutunu döndürür. yöntemi AdjustForFill , boyutun düzenin HorizontalLayoutAlignment ve VerticalLayoutAlignment özelliklerinin olarak ayarlanıp ayarlanmadığını hesaba katmasını sağlamak için çağrılır LayoutOptions.Fill.

Önemli

Uygulamadaki ArrangeChildren alt öğeleri numaralandırırken, özelliği olarak ayarlanmış Collapsedolan Visibility tüm alt öğeleri atlayın. Bu, özel düzenin görünmez çocuklar için alan bırakmamasını sağlar.

Düzen türünü kullanma

Sınıf HorizontalWrapLayout , türetilmiş bir Page türe yerleştirilerek kullanılabilir:

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

Denetimler öğesine gerektiği gibi eklenebilir HorizontalWrapLayout . Bu örnekte, öğesini içeren sayfa göründüğünde HorizontalWrapLayoutImage denetimler görüntülenir:

Screenshot of the horizontal wrap layout on a Mac with two columns.

Her satırdaki sütun sayısı, görüntü boyutuna, sayfanın genişliğine ve cihazdan bağımsız birim başına piksel sayısına bağlıdır:

Screenshot of the horizontal wrap layout on a Mac with five columns.

Not

Kaydırma, öğesini bir ScrollViewiçinde kaydırarak HorizontalWrapLayout desteklenir.

Mevcut düzenin davranışını değiştirme

Bazı senaryolarda, özel düzen türü oluşturmak zorunda kalmadan var olan bir düzen türünün davranışını değiştirmek isteyebilirsiniz. Bu senaryolar için uygulayan ILayoutManagerFactory bir tür oluşturabilir ve bunu kullanarak mevcut düzen için .NET MAUI'nin varsayılan düzen yöneticisini kendi ILayoutManager uygulamanızla değiştirebilirsiniz. Bu, var olan bir düzen için yeni bir düzen yöneticisi tanımlamanızı sağlar; örneğin için Gridözel bir düzen yöneticisi sağlar. Bu, bir düzene yeni bir davranış eklemek istediğiniz ancak uygulamanızda yaygın olarak kullanılan mevcut bir düzenin türünü güncelleştirmek istemediğiniz senaryolar için yararlı olabilir.

Düzen yöneticisi fabrikasıyla var olan bir düzenin davranışını değiştirme işlemi şöyledir:

  1. .NET MAUI'nin düzen yöneticisi türlerinden birinden türetilen bir düzen yöneticisi oluşturun. Daha fazla bilgi için bkz . Özel düzen yöneticisi oluşturma.
  2. uygulayan ILayoutManagerFactorybir tür oluşturun. Daha fazla bilgi için bkz . Düzen yöneticisi fabrikası oluşturma.
  3. Düzen yöneticisi fabrikanızı uygulamanın hizmet sağlayıcısına kaydedin. Daha fazla bilgi için bkz . Düzen yöneticisi fabrikasını kaydetme.

Özel düzen yöneticisi oluşturma

Düzen yöneticisi, bir düzen için platformlar arası düzen ve ölçüm gerçekleştirmek için kullanılır. Mevcut bir düzenin davranışını değiştirmek için, düzenin düzen yöneticisinden türetilen özel bir düzen yöneticisi oluşturmanız gerekir:

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

Bu örnekte.NET CustomGridLayoutManager MAUI'nin GridLayoutManager sınıfından türetilir ve yöntemini geçersiz kılar Measure . Bu özel düzen yöneticisi, çalışma zamanında RowDefinitions için Grid öğesinin alt görünümde ayarlanan her Grid.Row ekli özelliği hesaba eklemek için yeterli satır içermesini sağlar. Bu değişiklik olmadan için öğesinin RowDefinitionsGrid tasarım zamanında belirtilmesi gerekir.

Önemli

Mevcut düzen yöneticisinin davranışını değiştirirken, yöntemi uygulamanızdan Measure çağırdığınızdan base.Measure emin olun.

Düzen yöneticisi fabrikası oluşturma

Özel düzen yöneticisi bir düzen yöneticisi fabrikasında oluşturulmalıdır. Bu, arabirimi uygulayan bir tür oluşturularak elde edilir ILayoutManagerFactory :

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

Bu örnekte, düzen bir ise bir CustomGridLayoutManagerGridörnek döndürülür.

Düzen yöneticisi fabrikasını kaydetme

Düzen yöneticisi fabrikası, sınıfınızdaki uygulamanızın hizmet sağlayıcısına MauiProgram kayıtlı olmalıdır:

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

Ardından, uygulama bir Grid işlendiğinde, çalışma zamanında RowDefinitions için öğesinin alt görünümlerde ayarlanan her Grid.Row ekli özellik için hesaba eklemek için yeterli satır içerdiğinden emin olmak için Grid özel düzen yöneticisini kullanır.

Aşağıdaki örnekte, alt görünümlerde ekli özelliği ayarlayan Grid.Row ancak özelliğini ayarlamayan RowDefinitions bir Grid gösterilir:

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

Düzen yöneticisi fabrikası, özelliği ayarlanmasa da bu örnekteki öğesinin Grid doğru şekilde görüntülendiğinden emin olmak için özel düzen yöneticisini RowDefinitions kullanır:

Screenshot of a Grid customized by using a layout manager factory.