我正在尝试创建一个网格视图,就像Windows10中默认的新闻应用程序一样。据我所知,我必须为VariableSizedWrapGrid设置一个ItemHeight的ItemWidth。但它不会拉伸项目以适应整个网格宽度,而新闻应用程序确实可以做到这一点,如下图所示。他们如何做到这一点呢?它是一个特殊的自定义控件吗?
发布于 2017-01-10 03:09:58
UWP
作为我之前的回答的补充,我在这里展示了基本概念,这是一个使用问题中提到的VariableSizedWrapPanel
的UWP平台的解决方案:
主要工作由
<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;
}
}
}
如果你曾经想过“我从某个地方知道”,你应该看看Jerry Nixon's blog :o)
https://stackoverflow.com/questions/32436208
复制相似问题