如何根据 window 宽度自动调整 GridView 项目的外观和宽度

How to auto-adjust GridView item appearance and width depending on window width

我正在尝试让我的 GridView 根据屏幕宽度自动调整其项目的外观,但每次我拍摄我的应用程序 window 时,GridView 项目都会保留靠得太近了。如何确保每个 GridView 项目都出现在自己的行上,并在悬停时像 ListView 项目一样突出显示整行,当 window 对齐到一侧或小于一侧时一定的宽度(比如 720 - 这通常用于 MasterDetailsView - CompactModeThresholdWidth="720")?

宽window

抢购window

快照 window(预期结果)

MainPage.xaml

<Page
    x:Class="MyApp.MainPage"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    xmlns:data="using:MyApp.Models"
    mc:Ignorable="d">

    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto"/>
            <RowDefinition Height="*"/>
        </Grid.RowDefinitions>
        <StackPanel Margin="0,0,0,20" Grid.Row="0">
            <TextBlock Text="Hello World"/>
        </StackPanel>

        <GridView Grid.Row="1" 
                  IsItemClickEnabled="True"
                  ItemsSource="{x:Bind GridItems}" 
                  ItemClick="GridView_ItemClick">
            <GridView.ItemContainerStyle>
                <Style TargetType="GridViewItem">
                    <Setter Property="HorizontalContentAlignment" Value="Left"/>
                </Style>
            </GridView.ItemContainerStyle>
            <GridView.ItemsPanel>
                <ItemsPanelTemplate>
                    <data:MyGridViewPanel/>
                </ItemsPanelTemplate>
            </GridView.ItemsPanel>
            <GridView.ItemTemplate>
                <DataTemplate x:DataType="data:GridItemMain">
                    <StackPanel Orientation="Horizontal">
                        <TextBlock Grid.Column="0"
                                   Text="{Binding Icon}" 
                                   Style="{StaticResource TitleTextBlockStyle}" 
                                   FontFamily="Segoe MDL2 Assets"/>
                        <TextBlock Grid.Column="1"
                                   Text="{Binding Title}" 
                                   Style="{StaticResource TitleTextBlockStyle}"/>
                    </StackPanel>
                </DataTemplate>
            </GridView.ItemTemplate>
        </GridView>
    </Grid>
</Page>

MainPage.xaml.cs

    public sealed partial class MainPage : Page
    {
        public List<GridItemMain> GridItems;

        public MainPage()
        {
            this.InitializeComponent();

            GridItems = GridItemManager.GetGridItems();
        }
    }

MainGridItem.cs

    public class MainGridItem
    {
        public string Icon { get; set; }
        public string Title { get; set; }
    }

    public class GridItemManager
    {
        public static List<MainGridItem> GetGridItems()
        {
            var gridItems = new List<MainGridItem>();

            gridItems.Add(new MainGridItem { Icon = "\uE770", Title = "System" });
            gridItems.Add(new MainGridItem { Icon = "\uE772", Title = "Devices" });
            gridItems.Add(new MainGridItem { Icon = "\uEC75", Title = "Phone" });
            gridItems.Add(new MainGridItem { Icon = "\uE774", Title = "Network & Internet" });
            gridItems.Add(new MainGridItem { Icon = "\uE771", Title = "Personalisation" });

            return gridItems;
        }
    }

MyGridViewPanel.cs

public class MyGridViewPanel : Panel
{
    private double _maxWidth;
    private double _maxHeight;
    protected override Size ArrangeOverride(Size finalSize)
    {
        var x = 0.0;
        var y = 0.0;
        double width = Window.Current.Bounds.Width;
        if (width <= 720)
        {
            foreach (var child in Children)
            {  
               
                var newpos = new Rect(0, y, width, _maxHeight);
                child.Arrange(newpos);
                y += _maxHeight;
            }
            return finalSize;
        }
        else {
            foreach (var child in Children)
            {
                if ((_maxWidth + x) > finalSize.Width)
                {
                    x = 0;
                    y += _maxHeight;
                }
                var newpos = new Rect(x, y, _maxWidth, _maxHeight);
                child.Arrange(newpos);
                x += _maxWidth;
            }
            return finalSize;
        }
            
    }
    protected override Size MeasureOverride(Size availableSize)
    {
        double width = Window.Current.Bounds.Width;
        if (width <= 720)
        {
            foreach (var child in Children)
            {
                child.Measure(new Size(width, availableSize.Height));
                var desiredheight = child.DesiredSize.Height;
                if (desiredheight > _maxHeight)
                    _maxHeight = desiredheight;
            }
            return new Size(width, _maxHeight * Children.Count);
        }
        else {
            foreach (var child in Children)
            {
                child.Measure(availableSize);
                var desirtedwidth = child.DesiredSize.Width;
                if (desirtedwidth > _maxWidth)
                    _maxWidth = desirtedwidth;
                var desiredheight = child.DesiredSize.Height;
                if (desiredheight > _maxHeight)
                    _maxHeight = desiredheight;
            }
            var itemperrow = Math.Floor(availableSize.Width / _maxWidth);
            var rows = Math.Ceiling(Children.Count / itemperrow);
            return new Size(itemperrow * _maxWidth, _maxHeight * rows);
        }
        
    }
}

更新

如果想在window小于720px时每行显示一个item,可以在ArrangeOverride和MeasureOverride方法中获取当前window的宽度,然后resize项目并重新布局每个项目以在每一行中显示它们。例如:

MyGridViewPanel.cs

public class MyGridViewPanel : Panel
{
    private double _maxWidth;
    private double _maxHeight;
    protected override Size ArrangeOverride(Size finalSize)
    {
        var x = 0.0;
        var y = 0.0;
        double width = Window.Current.Bounds.Width;
        if (width <= 720)
        {
            foreach (var child in Children)
            {  
               
                var newpos = new Rect(0, y, width, _maxHeight);
                child.Arrange(newpos);
                y += _maxHeight;
            }
            return finalSize;
        }
        else {
            foreach (var child in Children)
            {
                if ((_maxWidth + x) > finalSize.Width)
                {
                    x = 0;
                    y += _maxHeight;
                }
                var newpos = new Rect(x, y, _maxWidth, _maxHeight);
                child.Arrange(newpos);
                x += _maxWidth;
            }
            return finalSize;
        }
            
    }
    protected override Size MeasureOverride(Size availableSize)
    {
        double width = Window.Current.Bounds.Width;
        if (width <= 720)
        {
            foreach (var child in Children)
            {
                child.Measure(new Size(width, availableSize.Height));
                var desiredheight = child.DesiredSize.Height;
                if (desiredheight > _maxHeight)
                    _maxHeight = desiredheight;
            }
            return new Size(width, _maxHeight * Children.Count);
        }
        else {
            foreach (var child in Children)
            {
                child.Measure(availableSize);
                var desirtedwidth = child.DesiredSize.Width;
                if (desirtedwidth > _maxWidth)
                    _maxWidth = desirtedwidth;
                var desiredheight = child.DesiredSize.Height;
                if (desiredheight > _maxHeight)
                    _maxHeight = desiredheight;
            }
            var itemperrow = Math.Floor(availableSize.Width / _maxWidth);
            var rows = Math.Ceiling(Children.Count / itemperrow);
            return new Size(itemperrow * _maxWidth, _maxHeight * rows);
        }
        
    }
}