Я пытаюсь создать gridview, как в приложении новостей по умолчанию в Windows 10. Насколько я знаю, мне нужно установить ItemHeight для параметра ItemWidth для VariableSizedWrapGrid. Но тогда он не растягивает элементы, чтобы соответствовать полной ширине сетки, в то время как приложение "Новости" делает это, как вы можете видеть на рисунках ниже. Как они это делают? Это специальный пользовательский контроль?
Как приложение W10 News растягивает элементы в gridview?
Ответ 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>