【问题标题】:How does the W10 News app stretch the items in the gridview?W10 新闻应用程序如何拉伸网格视图中的项目?
【发布时间】:2015-12-02 20:44:29
【问题描述】:

我正在尝试在 Windows 10 的默认新闻应用程序中创建一个网格视图。据我所知,我必须为 VariableSizedWrapGrid 设置 ItemHeight 和 ItemWidth。但是它不会拉伸项目以适应整个网格宽度,而新闻应用程序会这样做,如下图所示。他们是怎么做到的?它是一个特殊的自定义控件吗?

【问题讨论】:

    标签: c# xaml gridview


    【解决方案1】:

    根据MSDN,可以将ItemWidth设置为Auto。

    ItemHeight 和 ItemWidth 的默认值不是 0,而是 双NaN。 ItemHeight 和 ItemWidth 支持成为 取消设置“自动”值。因为 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;
      }
    }
    

    【讨论】:

    • 这段代码与我目前使用的类似,但我一直在寻找更优雅的解决方案。我还手动重新计算 ItemHeight 以使内容都适合高度并且不会留下额外的间隙。
    • 应该怎样更优雅?它只是一小段代码,很容易理解。或许可以通过创建 VariableSizedWrapGrid 的子类(即 DynamicSizedWrapGrid)将其抽象出来,然后改用它?
    【解决方案2】:

    我只是给你一个概念:

    +---------+ +---------+ +---------+ |小| | | | | +---------+ | | | | --- 差距 --- |中 | | | +---------+ | | | | |小| | | |大| +---------+ +---------+ | | --- 差距 --- --- 差距 --- | | +---------+ +---------+ | | |小| |小| | | +---------+ +---------+ +---------+ --- 差距 --- --- 差距 --- --- 差距 ---

    所以我们只有 3 个不同的盒子来展示已知高度。 视图本身可以决定(代码隐藏)使用哪个模板来呈现内容。

    • small:带有小缩略图的标题
    • 中:带标题的小图片
    • 大:图片带有标题和文章中的一些文字

    所有项目按每组 5 到 9 个项目分组排列。这些组在 ItemsControl 中呈现,每个组由 WrapPanel(垂直方向)呈现。

    让我们看看一些行:

    • 2 列,6 项
    嗯嗯嗯 嗯嗯嗯 嗯嗯嗯 呸呸呸 呸呸呸 嗯嗯嗯 嗯嗯嗯 嗯嗯嗯 呸呸呸 呸呸呸
    • 3 列,6 项
    嗯嗯嗯嗯嗯嗯嗯 嗯嗯嗯嗯嗯嗯嗯 嗯嗯嗯嗯嗯嗯嗯 呸呸呸 呸呸呸呸呸呸 呸呸呸 嗯嗯嗯嗯嗯嗯嗯嗯 嗯嗯嗯嗯嗯嗯嗯嗯 嗯嗯嗯嗯嗯嗯嗯嗯
    • 4 列,6 项
    lllll lllll lllll sssss 呸呸呸呸呸呸 lllll lllll lllll sssss 呸呸呸呸呸呸 lllll lllll lllll sssss
    • 5 列,6 项
    嗯嗯嗯嗯嗯嗯嗯嗯嗯sssss 嗯嗯嗯嗯嗯嗯嗯嗯嗯嗯嗯 嗯嗯嗯嗯嗯嗯嗯嗯嗯sssss

    所以我们需要三个用于内容项的 DataTemplate、一些用于 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>
    

    和相似的

    更新

    一个非常简单的 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>
    

    和代码隐藏

    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( );
        }
    }
    

    最后是结果

    【讨论】:

    • 这似乎没有拉伸(调整大小)各个项目,因此没有回答具体问题。还是我错了?
    • 但是虚拟化呢?
    【解决方案3】:

    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)

    【讨论】:

      【解决方案4】:

      事实证明,达到预期结果的最佳方法是编写自定义面板并将其用作GridView(或ListView)中的ItemPanel

      看起来是这样的:

      此控件可让您实现以下目标:

      • 单行上的所有块都具有相同的宽度和高度。
      • 根据ItemMinWidth 属性自动计算的列数和列宽(默认为300px)。
      • 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>
      

      【讨论】:

        猜你喜欢
        • 2016-05-27
        • 1970-01-01
        • 2015-05-30
        • 1970-01-01
        • 2021-10-31
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2015-11-27
        相关资源
        最近更新 更多