Как создать адаптивную сетку с квадратами?

В настоящее время я создаю сетку, заполненную кнопками. Теперь я хочу, чтобы эта сетка была отзывчивой, а кнопки были квадратными. То, что я хочу создать, будет выглядеть как домашний экран iOS или Android (просто кнопки).

Grid grid = new Grid
        {

            HorizontalOptions = LayoutOptions.StartAndExpand,
            RowSpacing = 15,
            ColumnSpacing = 15,
            ColumnDefinitions = 
            {
                new ColumnDefinition { Width = new GridLength(1, GridUnitType.Star) },
                new ColumnDefinition { Width = new GridLength(1, GridUnitType.Star) },
                new ColumnDefinition { Width = new GridLength(1, GridUnitType.Star) }
            }
        };

        for (int i = 0; i < 12; i++)
        {
            MonitorButton button = new MonitorButton();
            grid.Children.Add(button, i % 3, i / 3);
        }

        this.Content = new ScrollView
        {
            Padding = new Thickness(15),
            Orientation = ScrollOrientation.Vertical,
            Content = grid
        };

Итак, здесь я создаю сетку с 3 столбцами, заполненную 12 MonitorButtons (кнопками с изображением на фоне). Это выглядит нормально. Теперь, когда я открываю экран в ландшафтном режиме, экран заполняется прямоугольниками, потому что количество столбцов остается прежним... Как я могу решить эту проблему? Хотелось бы иметь какую-то динамическую возможность (например: itemscontrol@XAML).


person bkardol    schedule 26.06.2014    source источник
comment
В настоящее время в Xamarin.Forms нет GridView. вам нужно будет рассчитать ширину столбца (и интервал) и динамически добавить определения столбца. Это будет сделано с помощью оператора модуля %   -  person Chase Florell    schedule 16.07.2014


Ответы (2)


Когда вы поворачиваете экран, текущая страница изменяет размер, поэтому она вызывает метод OnSizeRequest на вашей странице. Если вы переопределите этот метод, вы сможете определить и размер, и ориентацию.

Ваша страница.cs || Ваша страница.xaml.cs

protected override SizeRequest OnSizeRequest(double widthConstraint, double heightConstraint)
{
    if(widthConstraint>heightConstraint)
    {
        //landscape mode
        //set your grid row & columndefinitions
    }
    else
    {
        //portait mode
        //set your grid row & columndefinitions
    }
    return base.OnSizeRequest(widthConstraint, heightConstraint);
}
person eakgul    schedule 14.04.2015

Я думаю, что эту настраиваемую сетку (Custom Grid Control) будет лучше использовать, у которой есть команда, чтобы вы могли распознать, какой элемент нажат, поэтому я думаю, что вам может не понадобиться так много кнопок в вашем пользовательском интерфейсе. и вы можете добавить любые элементы управления, которые хотите, внутри ячейки сетки (в Square, как вы добавили кнопки)

GridView.Xaml

<?xml version="1.0" encoding="utf-8" ?>
<Grid xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="Test.Controls.GridView">

</Grid>

GridView.Xaml.cs

using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Input;
using Test.Views;
using Xamarin.Forms;
using Xamarin.Forms.Xaml;

namespace Test.Controls
{
    [XamlCompilation(XamlCompilationOptions.Compile)]
    public partial class GridView : Grid
    {
        public GridView()
        {
            InitializeComponent();
            for (var i = 0; i < MaxColumns; i++)
                ColumnDefinitions.Add(new ColumnDefinition());
        }

        public static readonly BindableProperty CommandParameterProperty = BindableProperty.Create<GridView, object>(p => p.CommandParameter, null);
        public static readonly BindableProperty CommandProperty = BindableProperty.Create<GridView, ICommand>(p => p.Command, null);
        public static readonly BindableProperty ItemsSourceProperty = BindableProperty.Create<GridView, IEnumerable<object>>(p => p.ItemsSource, null, BindingMode.OneWay, null, (bindable, oldValue, newValue) => { ((GridView)bindable).BuildTiles(newValue); });

        private int _maxColumns = 2;
        private float _tileHeight = 0;

        public Type ItemTemplate { get; set; } = typeof(SubControlsView);

        public int MaxColumns
        {
            get { return _maxColumns; }
            set { _maxColumns = value; }
        }

        public float TileHeight
        {
            get { return _tileHeight; }
            set { _tileHeight = value; }
        }

        public object CommandParameter
        {
            get { return GetValue(CommandParameterProperty); }
            set { SetValue(CommandParameterProperty, value); }
        }

        public ICommand Command
        {
            get { return (ICommand)GetValue(CommandProperty); }
            set { SetValue(CommandProperty, value); }
        }

        public IEnumerable<object> ItemsSource
        {
            get { return (IEnumerable<object>)GetValue(ItemsSourceProperty); }
            set { SetValue(ItemsSourceProperty, value); }
        }

        public void BuildTiles(IEnumerable<object> tiles)
        {
            try
            {
                if (tiles == null || tiles.Count() == 0)
                    Children?.Clear();

                // Wipe out the previous row definitions if they're there.
                RowDefinitions?.Clear();

                var enumerable = tiles as IList ?? tiles.ToList();
                var numberOfRows = Math.Ceiling(enumerable.Count / (float)MaxColumns);

                for (var i = 0; i < numberOfRows; i++)
                    RowDefinitions?.Add(new RowDefinition { Height = TileHeight });

                for (var index = 0; index < enumerable.Count; index++)
                {
                    var column = index % MaxColumns;
                    var row = (int)Math.Floor(index / (float)MaxColumns);

                    var tile = BuildTile(enumerable[index]);

                    Children?.Add(tile, column, row);
                }
            }
            catch
            { // can throw exceptions if binding upon disposal
            }
        }

        private Layout BuildTile(object item1)
        {
            var buildTile = (Layout)Activator.CreateInstance(ItemTemplate, item1);
            buildTile.InputTransparent = false;

            var tapGestureRecognizer = new TapGestureRecognizer
            {
                Command = Command,
                CommandParameter = item1,
                NumberOfTapsRequired = 1
            };

            buildTile?.GestureRecognizers.Add(tapGestureRecognizer);


            return buildTile;
        }
    }
}

Здесь вы можете создать свой внутренний пользовательский интерфейс (View). то есть внутренний вид каждого квадрата сетки. В этом примере я добавил только одну метку.

SubControlsView.Xaml

<?xml version="1.0" encoding="utf-8" ?>
<Grid xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="Test.Views.SubControlsView" BackgroundColor="CornflowerBlue">
    <StackLayout VerticalOptions="CenterAndExpand">
        <!--You can add any controls which you want--> 
        <!--<Label Text="{Binding id}" HorizontalOptions="CenterAndExpand" TextColor="White" />-->

    </StackLayout>
</Grid>

SubControlsView.Xaml.cs

using Xamarin.Forms;
using Xamarin.Forms.Xaml;

namespace Test.Views
{
    [XamlCompilation(XamlCompilationOptions.Compile)]
    public partial class SubControlsView : Grid
    {
        public SubControlsView()
        {
            InitializeComponent();
        }

        public SubControlsView(object item)
        {
            InitializeComponent();
            BindingContext = item;
        }
    }
}

Затем с помощью этого управления, где вы хотите. т. е. на любой странице контента. Это будет ваш фактический вид (приборная панель)

SubCarriers.Xaml

<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:control="clr-namespace:Test.Controls"
            x:Class="Test.Views.SubCarriers" Title="Details Dashboard">
    <ContentPage.Content>
        <ScrollView Margin="5">
            <control:GridView HorizontalOptions="FillAndExpand"
                        Grid.Row="1"
                        VerticalOptions="FillAndExpand"
                        RowSpacing="5"
                        ColumnSpacing="5"
                        MaxColumns="2"
                        ItemsSource="{Binding SubCarriersCnt}"
                        CommandParameter="{Binding SubCarriersCnt}"
                        Command="{Binding ClickCommand}"
                        IsClippedToBounds="False">
                <control:GridView.TileHeight>
                    <OnPlatform x:TypeArguments="x:Single"
                      iOS="60"
                      Android="90"
                      WinPhone="90" />
                </control:GridView.TileHeight>
            </control:GridView>
        </ScrollView>
    </ContentPage.Content>
</ContentPage>

Затем привяжите ViewModel к вашему представлению. и реализовать ClickCommand в YourViewModel

SubCarriers.Xaml.cs

using System;
using Test.ViewModels;
using Xamarin.Forms;
using Xamarin.Forms.Xaml;
using CommonUtility;

namespace Test.Views
{
    [XamlCompilation(XamlCompilationOptions.Compile)]
    public partial class SubCarriers : ContentPage
    {
        private YourViewModel viewModel;

        public SubCarriers()
        {
            InitializeComponent();
            this.BindingContext = new SubCarriersViewModel();
            viewModel = (YourViewModel)this.BindingContext;
        }

    }
}
person Ganesh Parikh    schedule 26.07.2017
comment
Привет, я реализовал все, и это работает, кроме ClickCommand. Я пробовал несколько вещей, но не смог получить действие щелчка. Если можно, поделитесь кодом. - person Singhal2; 17.11.2017