Подтвердить что ты не робот

Как приложение W10 News растягивает элементы в gridview?

Я пытаюсь создать gridview, как в приложении новостей по умолчанию в Windows 10. Насколько я знаю, мне нужно установить ItemHeight для параметра ItemWidth для VariableSizedWrapGrid. Но тогда он не растягивает элементы, чтобы соответствовать полной ширине сетки, в то время как приложение "Новости" делает это, как вы можете видеть на рисунках ниже. Как они это делают? Это специальный пользовательский контроль? Широкоэкранный, 4 столбца и элементы довольно широкие

Narrowscreen, 3 small columns

4b9b3361

Ответ 1

Я просто дам вам концепцию:

+---------+ +---------+ +---------+ 
|  small  | |         | |         |
+---------+ |         | |         |
--- gap --- | medium  | |         |
+---------+ |         | |         |
|  small  | |         | |   big   |
+---------+ +---------+ |         |
--- gap --- --- gap --- |         |
+---------+ +---------+ |         |
|  small  | |  small  | |         |
+---------+ +---------+ +---------+
--- gap --- --- gap --- --- gap ---

Итак, у нас есть только 3 разных поля для презентации с известной высотой. Само представление может решить (codebehind), какой шаблон используется для представления содержимого.

  • small: заголовок с маленьким эскизом
  • medium: небольшое изображение с заголовком
  • large: изображение с заголовком и некоторым текстом из статьи

Все элементы упорядочены группами по 5-9 элементам для каждой группы. Группы представлены внутри элемента ItemsControl, и каждая группа представлена ​​WrapPanel (вертикальная ориентация).

Посмотрим, что для некоторых строк:

  • 2 столбца, 6 элементов
lllll  mmmmm
lllll  mmmmm
lllll  mmmmm
lllll
lllll  sssss

lllll  mmmmm
lllll  mmmmm
lllll  mmmmm
lllll
lllll  sssss
  • 3 столбца, 6 элементов
lllll  lllll  mmmmm
lllll  lllll  mmmmm
lllll  lllll  mmmmm
lllll  lllll  
lllll  lllll  lllll
              lllll
mmmmm  mmmmm  lllll
mmmmm  mmmmm  lllll
mmmmm  mmmmm  lllll
  • 4 столбца, 6 элементов
lllll  lllll  lllll  sssss
lllll  lllll  lllll
lllll  lllll  lllll  sssss
lllll  lllll  lllll
lllll  lllll  lllll  sssss
  • 5 столбцов, 6 элементов
mmmmm  mmmmm  mmmmm  mmmmm  sssss
mmmmm  mmmmm  mmmmm  mmmmm
mmmmm  mmmmm  mmmmm  mmmmm  sssss

Итак, нам нужны три DataTemplates для элементов контента, некоторые шаблоны для WrapPanel и некоторая логика внутри представления для группировки элементов в строки и шаблона для WrapPanel и элементов внутри.

Здесь простой XAML PoC просто для проверки концепции:

<Grid>
    <ScrollViewer>
        <ItemsControl>
            <ItemsControl.Resources>
                <Style x:Key="col" TargetType="WrapPanel">
                    <Setter Property="Orientation" Value="Vertical"/>
                    <Setter Property="ItemWidth" Value="80"/>
                </Style>

                <Style x:Key="content" TargetType="Border">
                    <Setter Property="Margin" Value="5,5"/>
                </Style>
                <Style x:Key="small" TargetType="Border" BasedOn="{StaticResource content}">
                    <Setter Property="Background" Value="Orange"/>
                    <Setter Property="Height" Value="30"/>
                </Style>
                <Style x:Key="medium" TargetType="Border" BasedOn="{StaticResource content}">
                    <Setter Property="Background" Value="Green"/>
                    <Setter Property="Height" Value="70"/>
                </Style>
                <Style x:Key="large" TargetType="Border" BasedOn="{StaticResource content}">
                    <Setter Property="Background" Value="Red"/>
                    <Setter Property="Height" Value="110"/>
                </Style>

                <Style x:Key="2col6items" TargetType="WrapPanel" BasedOn="{StaticResource col}">
                    <Setter Property="Height" Value="240"/>
                </Style>

                <Style x:Key="3col6items" TargetType="WrapPanel" BasedOn="{StaticResource col}">
                    <Setter Property="Height" Value="200"/>
                </Style>

                <Style x:Key="4col6items" TargetType="WrapPanel" BasedOn="{StaticResource col}">
                    <Setter Property="Height" Value="120"/>
                </Style>

                <Style x:Key="5col6items" TargetType="WrapPanel" BasedOn="{StaticResource col}">
                    <Setter Property="Height" Value="80"/>
                </Style>


            </ItemsControl.Resources>
            <!-- first row -->
            <WrapPanel Style="{StaticResource 5col6items}">
                <Border Style="{StaticResource medium}"/>
                <Border Style="{StaticResource medium}"/>
                <Border Style="{StaticResource medium}"/>
                <Border Style="{StaticResource medium}"/>
                <Border Style="{StaticResource small}"/>
                <Border Style="{StaticResource small}"/>
            </WrapPanel>
            <!-- second row -->
            <WrapPanel Style="{StaticResource 4col6items}">
                <Border Style="{StaticResource large}"/>
                <Border Style="{StaticResource large}"/>
                <Border Style="{StaticResource large}"/>
                <Border Style="{StaticResource small}"/>
                <Border Style="{StaticResource small}"/>
                <Border Style="{StaticResource small}"/>
            </WrapPanel>
            <!-- third row -->
            <WrapPanel Style="{StaticResource 3col6items}">
                <Border Style="{StaticResource large}"/>
                <Border Style="{StaticResource medium}"/>
                <Border Style="{StaticResource large}"/>
                <Border Style="{StaticResource medium}"/>
                <Border Style="{StaticResource medium}"/>
                <Border Style="{StaticResource large}"/>
            </WrapPanel>
            <!-- fourth row -->
            <WrapPanel Style="{StaticResource 2col6items}">
                <Border Style="{StaticResource large}"/>
                <Border Style="{StaticResource large}"/>
                <Border Style="{StaticResource medium}"/>
                <Border Style="{StaticResource small}"/>
                <Border Style="{StaticResource medium}"/>
                <Border Style="{StaticResource small}"/>
            </WrapPanel>
        </ItemsControl>
    </ScrollViewer>
</Grid>

и посмотрите

введите описание изображения здесь

Update

Очень простой ProofOfConcept с растяжением и шаблоном при изменении размера

<Window x:Class="WpfApp1.MainWindow"
        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:local="clr-namespace:WpfApp1"
        mc:Ignorable="d"
        Title="MainWindow" Height="350" Width="525">
    <Grid>
        <ScrollViewer>
            <ItemsControl x:Name="ItemsPresenter" SizeChanged="ItemsPresenter_SizeChanged">
                <ItemsControl.Resources>
                    <Style x:Key="col" TargetType="WrapPanel">
                        <Setter Property="Orientation" Value="Vertical"/>
                        <Setter Property="ItemWidth" Value="{Binding ColumnWidth, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type local:MainWindow}}}"/>
                    </Style>

                    <Style x:Key="content" TargetType="TextBlock">
                        <Setter Property="Margin" Value="5"/>
                        <Setter Property="HorizontalAlignment" Value="Stretch"/>
                        <Setter Property="TextAlignment" Value="Center"/>
                        <Setter Property="VerticalAlignment" Value="Stretch"/>
                    </Style>
                    <Style x:Key="small" TargetType="TextBlock" BasedOn="{StaticResource content}">
                        <Setter Property="Background" Value="LightBlue"/>
                        <Setter Property="Height" Value="30"/>
                    </Style>
                    <Style x:Key="medium" TargetType="TextBlock" BasedOn="{StaticResource content}">
                        <Setter Property="Background" Value="LightGreen"/>
                        <Setter Property="Height" Value="70"/>
                    </Style>
                    <Style x:Key="large" TargetType="TextBlock" BasedOn="{StaticResource content}">
                        <Setter Property="Background" Value="LightSalmon"/>
                        <Setter Property="Height" Value="110"/>
                    </Style>

                    <Style x:Key="1col6items" TargetType="WrapPanel" BasedOn="{StaticResource col}">
                        <Setter Property="Height" Value="480"/>
                    </Style>

                    <Style x:Key="2col6items" TargetType="WrapPanel" BasedOn="{StaticResource col}">
                        <Setter Property="Height" Value="240"/>
                    </Style>

                    <Style x:Key="3col6items" TargetType="WrapPanel" BasedOn="{StaticResource col}">
                        <Setter Property="Height" Value="200"/>
                    </Style>

                    <Style x:Key="4col6items" TargetType="WrapPanel" BasedOn="{StaticResource col}">
                        <Setter Property="Height" Value="120"/>
                    </Style>

                    <Style x:Key="5col6items" TargetType="WrapPanel" BasedOn="{StaticResource col}">
                        <Setter Property="Height" Value="80"/>
                    </Style>


                </ItemsControl.Resources>
                <!-- first row -->
                <WrapPanel >
                    <TextBlock>1</TextBlock>
                    <TextBlock>2</TextBlock>
                    <TextBlock>3</TextBlock>
                    <TextBlock>4</TextBlock>
                    <TextBlock>5</TextBlock>
                    <TextBlock>6</TextBlock>
                </WrapPanel>
                <!-- second row -->
                <WrapPanel >
                    <TextBlock>7</TextBlock>
                    <TextBlock>8</TextBlock>
                    <TextBlock>9</TextBlock>
                    <TextBlock>10</TextBlock>
                    <TextBlock>11</TextBlock>
                    <TextBlock>12</TextBlock>
                </WrapPanel>
                <!-- third row -->
                <WrapPanel >
                    <TextBlock>13</TextBlock>
                    <TextBlock>14</TextBlock>
                    <TextBlock>15</TextBlock>
                    <TextBlock>16</TextBlock>
                    <TextBlock>17</TextBlock>
                    <TextBlock>18</TextBlock>
                </WrapPanel>
            </ItemsControl>
        </ScrollViewer>
    </Grid>
</Window>

и codebehind

public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent( );
    }

    public int ColumnCount
    {
        get { return (int) GetValue( ColumnCountProperty ); }
        private set { SetValue( ColumnCountProperty, value ); }
    }

    // Using a DependencyProperty as the backing store for ColumnCount.  This enables animation, styling, binding, etc...
    public static readonly DependencyProperty ColumnCountProperty =
        DependencyProperty.Register( "ColumnCount", typeof( int ), typeof( MainWindow ), new PropertyMetadata( 1 ) );

    public double ColumnWidth
    {
        get { return (double) GetValue( ColumnWidthProperty ); }
        private set { SetValue( ColumnWidthProperty, value ); }
    }

    // Using a DependencyProperty as the backing store for ColumnWidth.  This enables animation, styling, binding, etc...
    public static readonly DependencyProperty ColumnWidthProperty =
        DependencyProperty.Register( "ColumnWidth", typeof( double ), typeof( MainWindow ), new PropertyMetadata( (double) 100 ) );

    public double ColumnMinWidth
    {
        get { return (double) GetValue( ColumnMinWidthProperty ); }
        set
        {
            SetValue( ColumnMinWidthProperty, value );
            CalculateColumnLayout( );
        }
    }

    // Using a DependencyProperty as the backing store for ColumnMinWidth.  This enables animation, styling, binding, etc...
    public static readonly DependencyProperty ColumnMinWidthProperty =
        DependencyProperty.Register( "ColumnMinWidth", typeof( double ), typeof( MainWindow ), new PropertyMetadata( (double) 200 ) );

    public double ColumnMaxWidth
    {
        get { return (double) GetValue( ColumnMaxWidthProperty ); }
        set
        {
            SetValue( ColumnMaxWidthProperty, value );
            CalculateColumnLayout( );
        }
    }

    // Using a DependencyProperty as the backing store for ColumnMaxWidth.  This enables animation, styling, binding, etc...
    public static readonly DependencyProperty ColumnMaxWidthProperty =
        DependencyProperty.Register( "ColumnMaxWidth", typeof( double ), typeof( MainWindow ), new PropertyMetadata( (double) 250 ) );

    private void CalculateColumnLayout()
    {
        int colCount = ColumnCount;
        double totalWidth = ItemsPresenter.ActualWidth;
        double colWidth = totalWidth / colCount;

        while ( colCount > 1 && colWidth < Math.Min( ColumnMinWidth, ColumnMaxWidth ) )
        {
            colCount--;
            colWidth = totalWidth / colCount;
        }

        while ( colCount < 5 && colWidth > Math.Max( ColumnMinWidth, ColumnMaxWidth ) )
        {
            colCount++;
            colWidth = totalWidth / colCount;
        }

        if ( ColumnCount != colCount )
        {
            ColumnCount = colCount;
            StyleItemsPresenterItems( );
        }
        ColumnWidth = colWidth;
    }

    private Dictionary<int, string[]> _styles = new Dictionary<int, string[]>
    {
        [ 1 ] = new string[] { "medium", "medium", "medium", "medium", "medium", "medium" },
        [ 2 ] = new string[] { "large", "medium", "small", "small", "medium", "large" },
        [ 3 ] = new string[] { "large", "medium", "medium", "large", "large", "medium" },
        [ 4 ] = new string[] { "large", "large", "large", "small", "small", "small" },
        [ 5 ] = new string[] { "medium", "medium", "medium", "medium", "small", "small" },
    };


    private void StyleItemsPresenterItems()
    {
        foreach ( var pnl in ItemsPresenter.Items.OfType<WrapPanel>( ) )
        {
            if ( pnl != null )
            {
                pnl.Style = ItemsPresenter.Resources[ $"{ColumnCount}col6items" ] as Style;

                foreach ( var item in pnl.Children.OfType<TextBlock>( ).Zip( _styles[ ColumnCount ], ( border, stylename ) => new { border, stylename } ) )
                {
                    item.border.Style = ItemsPresenter.Resources[ item.stylename ] as Style;
                }

            }
        }
    }

    private void ItemsPresenter_SizeChanged( object sender, SizeChangedEventArgs e )
    {
        CalculateColumnLayout( );
    }
}

и, наконец, результат

введите описание изображения здесь

Ответ 2

В соответствии с MSDN ItemWidth может быть установлен в Auto.

Значение по умолчанию ItemHeight и ItemWidth не равно 0, это Double.NaN. ItemHeight и ItemWidth поддерживают способность быть unset "Auto" значение. Поскольку ItemHeight и ItemWidth являются Double значения, Double.NaN используется как специальное значение для представления этого "Авто", поведение. Система макета интерпретирует значение "Авто" в целом означает, что объект должен иметь размер до имеющегося размера в макете, вместо определенного значения пикселя.

Я не знаю, приведет ли это к поведению, которое вы хотите. Если это не так, вы можете получить его, привязав ItemWidth к свойству, в котором вы вычисляете ширину элемента в зависимости от ширины сетки. Он будет выглядеть примерно так:

float DynamicItemWidth {
  get {
    int ItemMinimumWidth = 300, margin = 16; //just some guesses
    var gridWidth = ...;

    var numberOfColumns = gridWidth % ItemMinimumWidth;
    var itemWidth = (gridWidth - margin * (numberOfColumns - 1)) / numberOfColumns;

    return itemWidth;
  }
}

Ответ 3

UWP

В дополнение к моему предыдущему ответу, где я показываю основную концепцию здесь решение для платформы UWP с использованием VariableSizedWrapPanel, как указано в вопросе:

Основное задание выполняется

<local:MyGridView 
    ItemsSource="{Binding}" 
    ItemTemplateSelector="{StaticResource MyGridTemplateSelector}" 
    MinItemWidth="300" MaxItemWidth="600" 
    ScrollViewer.VerticalScrollBarVisibility="Hidden">
    <GridView.ItemsPanel>
        <ItemsPanelTemplate>
            <VariableSizedWrapGrid ItemHeight="180" Orientation="Horizontal"/>
        </ItemsPanelTemplate>
    </GridView.ItemsPanel>
</local:MyGridView>

вместе с

MyGridView.cs

using System;
using System.Collections.Generic;
using System.Linq;
using Windows.Foundation;
using Windows.UI.Core;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;

namespace App1
{
    public class MyGridView : GridView
    {
        private int _columnCount = 1;
        private double _itemWidth = 100;

        public double MinItemWidth
        {
            get { return (double) GetValue( MinItemWidthProperty ); }
            set { SetValue( MinItemWidthProperty, value ); }
        }

        // Using a DependencyProperty as the backing store for MinItemWidth.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty MinItemWidthProperty =
            DependencyProperty.Register( "MinItemWidth", typeof( double ), typeof( MyGridView ), new PropertyMetadata( 100.0 ) );


        public double MaxItemWidth
        {
            get { return (double) GetValue( MaxItemWidthProperty ); }
            set { SetValue( MaxItemWidthProperty, value ); }
        }

        // Using a DependencyProperty as the backing store for MaxItemWidth.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty MaxItemWidthProperty =
            DependencyProperty.Register( "MaxItemWidth", typeof( double ), typeof( MyGridView ), new PropertyMetadata( 200.0 ) );


        private long _itemsPanelPropertyChangedToken;

        public MyGridView()
        {
            _itemsPanelPropertyChangedToken = RegisterPropertyChangedCallback( ItemsPanelProperty, ItemsPanelChangedAsync );
        }

        private async void ItemsPanelChangedAsync( DependencyObject sender, DependencyProperty dp )
        {
            UnregisterPropertyChangedCallback( ItemsPanelProperty, _itemsPanelPropertyChangedToken );
            await this.Dispatcher.RunIdleAsync( ItemsPanelChangedCallback );
        }

        private void ItemsPanelChangedCallback( IdleDispatchedHandlerArgs e )
        {
            var wg = ItemsPanelRoot as VariableSizedWrapGrid;
            if (wg != null)
            {
                wg.ItemWidth = _itemWidth;
            }
        }

        protected override void PrepareContainerForItemOverride( DependencyObject element, object item )
        {
            var itemIndex = this.Items.IndexOf( item );

            element.SetValue( VariableSizedWrapGrid.RowSpanProperty, GetRowSpanByColumnCountAndIndex( _columnCount, itemIndex ) );
            element.SetValue( VerticalContentAlignmentProperty, VerticalAlignment.Stretch );
            element.SetValue( HorizontalContentAlignmentProperty, HorizontalAlignment.Stretch );
            base.PrepareContainerForItemOverride( element, item );
        }

        private static readonly Dictionary<int, int[]> _rowSpanLayout = new Dictionary<int, int[]>
        {
            [ 1 ] = new int[] { /* 5 */ 2, 2, 2, 2, 2, /* 6 */ 2, 2, 2, 2, 2, 2, /* 7 */ 2, 2, 2, 2, 2, 2, 2, /* 8 */ 2, 2, 2, 2, 2, 2, 2, 2, /* 9 */ 2, 2, 2, 2, 2, 2, 2, 2, 2 },
            [ 2 ] = new int[] { /* 5 */ 2, 1, 2, 2, 1, /* 6 */ 3, 3, 3, 2, 2, 2, /* 7 */ 3, 3, 1, 2, 3, 1, 1, /* 8 */ 2, 3, 2, 3, 3, 3, 3, 1, /* 9 */ 3, 2, 1, 3, 2, 2, 3, 1, 1 },
            [ 3 ] = new int[] { /* 5 */ 3, 2, 2, 1, 1, /* 6 */ 2, 3, 2, 3, 3, 2, /* 7 */ 3, 3, 3, 2, 1, 2, 1, /* 8 */ 2, 3, 3, 1, 2, 1, 2, 1, /* 9 */ 3, 3, 3, 1, 2, 1, 3, 3, 2 },
            [ 4 ] = new int[] { /* 5 */ 2, 2, 1, 2, 1, /* 6 */ 3, 3, 2, 2, 1, 1, /* 7 */ 3, 2, 2, 2, 1, 1, 1, /* 8 */ 3, 3, 3, 3, 2, 2, 2, 2, /* 9 */ 3, 3, 3, 2, 2, 2, 2, 2, 1 },
            [ 5 ] = new int[] { /* 5 */ 2, 2, 2, 2, 2, /* 6 */ 2, 2, 2, 1, 2, 1, /* 7 */ 3, 3, 3, 2, 2, 1, 1, /* 8 */ 3, 3, 2, 2, 2, 1, 1, 1, /* 9 */ 3, 2, 2, 2, 2, 1, 1, 1, 1 },
        };

        private int GetRowSpanByColumnCountAndIndex( int columnCount, int itemIndex )
        {
            return _rowSpanLayout[ columnCount ][ itemIndex % 35 ];
        }

        protected override Size MeasureOverride( Size availableSize )
        {
            System.Diagnostics.Debug.WriteLine( availableSize );

            int columnCount = _columnCount;
            double availableWidth = availableSize.Width;

            double itemWidth = availableWidth / columnCount;

            while ( columnCount > 1 && itemWidth < Math.Min( MinItemWidth, MaxItemWidth ) )
            {
                columnCount--;
                itemWidth = availableWidth / columnCount;
            }

            while ( columnCount < 5 && itemWidth > Math.Max( MinItemWidth, MaxItemWidth ) )
            {
                columnCount++;
                itemWidth = availableWidth / columnCount;
            }

            var wg = this.ItemsPanelRoot as VariableSizedWrapGrid;

            _itemWidth = itemWidth;
            if ( _columnCount != columnCount )
            {
                _columnCount = columnCount;
                if ( wg != null )
                {
                    Update( );
                }
            }

            if ( wg != null )
            {
                wg.ItemWidth = itemWidth;
            }

            return base.MeasureOverride( availableSize );
        }

        // refresh the variablesizedwrapgrid layout
        private void Update()
        {
            if ( !( this.ItemsPanelRoot is VariableSizedWrapGrid ) )
                throw new ArgumentException( "ItemsPanel is not VariableSizedWrapGrid" );

            int itemIndex = 0;
            foreach ( var container in this.ItemsPanelRoot.Children.Cast<GridViewItem>( ) )
            {
                int rowSpan = GetRowSpanByColumnCountAndIndex( _columnCount, itemIndex );
                VariableSizedWrapGrid.SetRowSpan( container, rowSpan );
                itemIndex++;
            }

            this.ItemsPanelRoot.InvalidateMeasure( );
        }
    }
}

и

MyGridViewTemplateSelector.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;

namespace App1
{
    public class MyGridViewTemplateSelector : DataTemplateSelector
    {
        public DataTemplate Small { get; set; }
        public DataTemplate Medium { get; set; }
        public DataTemplate Large { get; set; }

        protected override DataTemplate SelectTemplateCore( object item, DependencyObject container )
        {
            var rowSpan = container.GetValue( VariableSizedWrapGrid.RowSpanProperty );

            int index;
            try
            {
                dynamic model = item;
                index = model.Index;
            }
            catch ( Exception )
            {
                index = -1;
            }
            long token = 0;

            DependencyPropertyChangedCallback lambda = ( sender, dp ) =>
            {
                container.UnregisterPropertyChangedCallback( VariableSizedWrapGrid.RowSpanProperty, token );

                var cp = (ContentControl) container;
                cp.ContentTemplateSelector = null;
                cp.ContentTemplateSelector = this;
            };

            token = container.RegisterPropertyChangedCallback( VariableSizedWrapGrid.RowSpanProperty, lambda );

            switch ( rowSpan )
            {
                case 1:
                    return Small;
                case 2:
                    return Medium;
                case 3:
                    return Large;
                default:
                    throw new InvalidOperationException( );
            }
        }

        private void Foo( DependencyObject sender, DependencyProperty dp )
        {
            throw new NotImplementedException( );
        }
    }
}

Чтобы завершить здесь другие файлы

MainPage.xaml

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

        <DataTemplate x:Key="Small">
            <Grid Margin="5">
                <Grid.Background>
                    <SolidColorBrush Color="{Binding Path=Color}"/>
                </Grid.Background>
                <StackPanel VerticalAlignment="Top">
                    <StackPanel.Background>
                        <SolidColorBrush Color="White" Opacity="0.75"/>
                    </StackPanel.Background>

                    <TextBlock FontSize="15" Margin="10">
                        <Run Text="{Binding Path=Index}"/>. <Run Text="{Binding Path=Name}"/>
                    </TextBlock>
                    <TextBlock Text="Small" TextAlignment="Center"/>
                </StackPanel>
            </Grid>
        </DataTemplate>

        <DataTemplate x:Key="Medium">
            <Grid Margin="5">
                <Grid.Background>
                    <SolidColorBrush Color="{Binding Path=Color}"/>
                </Grid.Background>
                <StackPanel VerticalAlignment="Top">
                    <StackPanel.Background>
                        <SolidColorBrush Color="White" Opacity="0.75"/>
                    </StackPanel.Background>

                    <TextBlock FontSize="15" Margin="10">
                        <Run Text="{Binding Path=Index}"/>. <Run Text="{Binding Path=Name}"/>
                    </TextBlock>
                    <TextBlock Text="Medium" TextAlignment="Center"/>
                </StackPanel>
            </Grid>
        </DataTemplate>

        <DataTemplate x:Key="Large">
            <Grid Margin="5">
                <Grid.Background>
                    <SolidColorBrush Color="{Binding Path=Color}"/>
                </Grid.Background>
                <StackPanel VerticalAlignment="Top">
                    <StackPanel.Background>
                        <SolidColorBrush Color="White" Opacity="0.75"/>
                    </StackPanel.Background>

                    <TextBlock FontSize="15" Margin="10">
                        <Run Text="{Binding Path=Index}"/>. <Run Text="{Binding Path=Name}"/>
                    </TextBlock>
                    <TextBlock Text="Large" TextAlignment="Center"/>
                </StackPanel>
            </Grid>

        </DataTemplate>
        <local:MyGridViewTemplateSelector x:Key="MyGridTemplateSelector"
                                          Small="{StaticResource Small}"
                                          Medium="{StaticResource Medium}"
                                          Large="{StaticResource Large}"/>
    </Page.Resources>

    <Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
        <Grid.RowDefinitions>
            <RowDefinition Height="48"/>
            <RowDefinition Height="*"/>
        </Grid.RowDefinitions>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="48"/>
            <ColumnDefinition Width="*"/>
        </Grid.ColumnDefinitions>

        <!-- top left section -->

        <Border Background="#D13438">

        </Border>

        <!-- top bar -->

        <Border Grid.Column="1" Grid.Row="0" Padding="5" Background="#F2F2F2">
            <TextBlock Text="MenuBar" VerticalAlignment="Center"/>
        </Border>

        <!-- left bar -->

        <Border Grid.Column="0" Grid.Row="1" Width="48" Background="#2B2B2B">

        </Border>

        <!-- content -->

        <Border Grid.Column="1" Grid.Row="1" Background="#E6E6E6">
            <Grid>
                <Grid.RowDefinitions>
                    <RowDefinition Height="48"/>
                    <RowDefinition Height="*"/>
                </Grid.RowDefinitions>
                <Border Grid.Row="0" Padding="5" Background="#F2F2F2">
                    <TextBlock Text="SectionBar" VerticalAlignment="Center"/>
                </Border>
                <ScrollViewer Grid.Row="1">
                    <Border Margin="7,7,10,7">

                        <!-- the wrapped news items -->

                        <local:MyGridView ItemsSource="{Binding}" ItemTemplateSelector="{StaticResource MyGridTemplateSelector}" MinItemWidth="300" MaxItemWidth="600" ScrollViewer.VerticalScrollBarVisibility="Hidden">
                            <GridView.ItemsPanel>
                                <ItemsPanelTemplate>
                                    <VariableSizedWrapGrid ItemHeight="180" Orientation="Horizontal"/>
                                </ItemsPanelTemplate>
                            </GridView.ItemsPanel>
                        </local:MyGridView>

                    </Border>
                </ScrollViewer>
            </Grid>
        </Border>
    </Grid>
</Page>

MainPage.xaml.cs

using System.Linq;
using Windows.UI;
using Windows.UI.Xaml.Controls;
using System.Reflection;

namespace App1
{
    public sealed partial class MainPage : Page
    {
        public MainPage()
        {
            this.InitializeComponent( );

            // just some sample data

            var colors = typeof( Colors )
                .GetRuntimeProperties( )
                .Take( 140 )
                .Select( ( x, index ) => new
                {
                    Color = (Color) x.GetValue( null ),
                    Name = x.Name,
                    Index = index,
                } );
            this.DataContext = colors;
        }

    }
}

Если вы когда-нибудь подумаете: "Я знаю это откуда-то", вы должны взглянуть на блог Джерри Никсона: o)

Ответ 4

Оказывается, лучший способ добиться желаемого результата - написать пользовательскую панель и использовать ее как ItemPanel в GridView (или ListView).

Вот как это выглядит: введите описание изображения здесь

Этот элемент управления позволяет вам выполнить следующее:

  • Все блоки в одной строке имеют одинаковую ширину и высоту.
  • Количество столбцов и столбцов, вычисляемых автоматически на основе свойства ItemMinWidth (по умолчанию 300 пикселей).
  • Отсутствие зазора в правой части GridView
  • Высота ячейки вычисляется на ходу на основе содержимого (поэтому, если содержимое изменилось - например, если пользователь изменил размер шрифта в приложении, он автоматически пересчитан).
  • Каждая строка имеет разную высоту (sic!) (не для всей панели, как в VariableSizedWrapGrid)
  • Вы также можете установить свойство MaxColumnCount, если хотите ограничить количество столбцов (например, если установлено значение 4, количество столбцов может быть 1, 2, 3 или 4).

Моя пользовательская панель (я назвал ее SmartPanel, извините за это):

using System;
using System.Linq;
using Windows.Foundation;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;

class SmartPanel : Panel
        {
            int _colCount;
            double _cellWidth;
            double[] _cellHeights;


            public static readonly DependencyProperty ItemMinWidthProperty = DependencyProperty.Register("ItemMinWidth", typeof(double), typeof(SmartPanel), new PropertyMetadata(300D));
            public double ItemMinWidth
            {
                get { return (double)GetValue(ItemMinWidthProperty); }
            }


            public static readonly DependencyProperty MaxColumnCountProperty = DependencyProperty.Register("MaxColumnCount", typeof(int), typeof(SmartPanel), new PropertyMetadata(10));
            public int MaxColumnCount
            {
                get { return (int)GetValue(MaxColumnCountProperty); }
                set { SetValue(MaxColumnCountProperty, value); }
            }


            protected override Size MeasureOverride(Size availableSize)
            {
                _colCount = (int)(availableSize.Width / ItemMinWidth);
                if (_colCount > MaxColumnCount) _colCount = MaxColumnCount;

                _cellWidth = (int)(availableSize.Width / _colCount);

                var rowCount = (int)Math.Ceiling((float)Children.Count / _colCount);

                _cellHeights = new double[rowCount];

                var y = 0;
                var x = 0;
                foreach (UIElement child in Children)
                {
                    child.Measure(new Size(_cellWidth, double.PositiveInfinity));
                    _cellHeights[y] = Math.Max(_cellHeights[y], child.DesiredSize.Height);

                    x++;
                    if (x >= _colCount)
                    {
                        x = 0;
                        y++;
                    }
                }

                y = 0;
                x = 0;
                foreach (UIElement child in Children)
                {
                    child.Measure(new Size(_cellWidth, _cellHeights[y]));

                    x++;
                    if (x >= _colCount)
                    {
                        x = 0;
                        y++;
                    }
                }

                if (double.IsInfinity(availableSize.Height))
                {
                    availableSize.Height = _cellHeights.Sum();
                }

                return availableSize;
            }



            protected override Size ArrangeOverride(Size finalSize)
            {
                double x = 0;
                double y = 0;
                int colNum = 0;
                int rowNum = 0;
                foreach (UIElement child in Children)
                {

                    child.Arrange(new Rect(x, y, _cellWidth, _cellHeights[rowNum]));
                    //child.Arrange(new Rect(new Point(x, y), child.DesiredSize));
                    x += _cellWidth;
                    colNum++;

                    if (colNum >= _colCount)
                    {
                        x = 0;
                        y += _cellHeights[rowNum];
                        colNum = 0;
                        rowNum++;
                    }
                }
                return finalSize;
            }


        }

Применение:

<GridView x:Name="MainGrid" Grid.Row="1" Margin="0,20,0,20"
                                       ItemsSource="{Binding Tiles}"
                                       ItemTemplateSelector="{StaticResource TemplateSelector}" 
                                       >
    <GridView.ItemContainerStyle>
        <Style TargetType="GridViewItem">
            <Setter Property="Margin" Value="10" />
            <Setter Property="Background" Value="White" />
            <Setter Property="HorizontalAlignment" Value="Stretch" />
            <Setter Property="VerticalAlignment" Value="Stretch" />
            <Setter Property="VerticalContentAlignment" Value="Top" />
            <Setter Property="HorizontalContentAlignment" Value="Left" />
            <Setter Property="Padding" Value="20" />
        </Style>
    </GridView.ItemContainerStyle>
    <GridView.ItemsPanel>
        <ItemsPanelTemplate>
            <ctl:SmartPanel Margin="10,0,10,0" MaxColumnCount="4" />
        </ItemsPanelTemplate>
    </GridView.ItemsPanel>

</GridView>