65.9K
CodeProject 正在变化。 阅读更多。
Home

UWP Stretch WrapGrid

starIconstarIconstarIconstarIconstarIcon

5.00/5 (1投票)

2016年3月24日

CPOL

2分钟阅读

viewsIcon

17532

downloadIcon

260

一个为通用 Windows 平台设计的行为,用于强制 List/GridView 的 WrapGrid 控件中的子项动态改变宽度,从而填充整行空间。

引言

内置的 UWP GridView 会在单行空间不足时将项目换行到新行。然而,这可能会在溢出发生之前或之后在控件右侧留下难看的空白。此行为通过计算每行可以容纳的项目数量(基于最小项目尺寸),然后设置所有项目宽度以填充整行来解决此问题。

背景

请参阅以下文章,了解附加行为的概念介绍。

https://codeproject.org.cn/Articles/28959/Introduction-to-Attached-Behaviors-in-WPF

请注意,包含的行为类还包含本文中概述的代码。

https://marcominerva.wordpress.com/2013/03/07/how-to-bind-the-itemclick-event-to-a-command-and-pass-the-clicked-item-to-it/

使用代码

该行为暴露了两个属性

第一个是 MinItemWidth,它指定您希望每个项目的最小宽度。

        /// <summary>
        /// Declare new attached property. This property specifies the minium width
        /// for child items in an items wrap grid panel. Setting this property
        /// to an non zero value will enable dynamic sizing of items so that
        /// when items are wrapped the items control is always filled out horizontally
        /// i.e. the width of items are increased to fill the empty space.
        /// </summary>
        public static readonly DependencyProperty MinItemWidthProperty =
            DependencyProperty.RegisterAttached("MinItemWidth", typeof(double),
            typeof(ListViewBehaviour), new PropertyMetadata(0, OnMinItemWidthChanged));

第二个属性是命名不佳的 FillBeforeWrap,它本质上意味着即使在溢出发生之前也会填充行。这将在项目少于单行可以容纳的数量时使用。

 /// <summary>
        /// Only applicable when MinItemWidth is non zero. Typically the logic
        /// behind MinItemWidth will only trigger if the number of items is
        /// more than or equal to what a single row will accomodate. This property
        /// specifies that the layout logic is also performed when there are
        /// less items than what a single row will accomodate.
        /// </summary>
        public static readonly DependencyProperty FillBeforeWrapProperty =
           DependencyProperty.RegisterAttached("FillBeforeWrap", typeof(bool),
           typeof(ListViewBehaviour), new PropertyMetadata(false));

在属性更改回调中,附加了大小更改事件的处理程序。使用 ListView 基类是为了与 ListView 和 GridView 兼容。

 /// <summary>
        /// This method will be called when the NavigateTo
        /// property is changed
        /// </summary>
        /// <param name="s">The sender (the Frame)</param>
        /// <param name="e">Some additional information</param>
        public static void OnMinItemWidthChanged(DependencyObject s, DependencyPropertyChangedEventArgs e)
        {
            //If the host object is a frame.
            if (s is ListViewBase)
            {
                //Unbox.
                ListViewBase f = s as ListViewBase;


                //Remove previous handler (if any.)
                f.SizeChanged -= listView_SizeChanged;


                //If property is a positive value.
                if (((double)e.NewValue) > 0)
                {
                    //Attach handling.
                    f.SizeChanged += listView_SizeChanged;
                }
            }
        }

在大小更改事件处理程序中执行项目宽度的计算。请注意,控件的项目面板根必须是 ItemsWrapGrid。

  /// <summary>
        ///
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private static void listView_SizeChanged(object sender, SizeChangedEventArgs e)
        {
            //Unbox the sender.
            var itemsControl = sender as ListViewBase;


            //Retrieve items panel.
            ItemsWrapGrid itemsPanel = itemsControl.ItemsPanelRoot as ItemsWrapGrid;


            //If the items panel is a wrap grid.
            if (itemsPanel != null)
            {
                //Get total size (leave room for scrolling.)
                var total = e.NewSize.Width - 10;


                //Minimum item size.
                var itemMinSize = (double)itemsControl.GetValue(MinItemWidthProperty);


                //How many items can be fit whole.
                var canBeFit = Math.Floor(total / itemMinSize);


                //logic that if the total items
                //are less then the number of items that
                //would fit then devide the total size by
                //the number of items rather than the number
                //of items that would actually fit.
                if ((bool)itemsControl.GetValue(FillBeforeWrapProperty) &&
                    itemsControl.Items.Count > 0 &&
                    itemsControl.Items.Count < canBeFit)
                {
                    canBeFit = itemsControl.Items.Count;
                }


                //Set the items Panel item width appropriately.
                //Note you will need your container to stretch
                //along with the items panel or it will look
                //strange.
                //  <GridView.ItemContainerStyle>
                //< Style TargetType = "GridViewItem" >
                //< Setter Property = "HorizontalContentAlignment" Value = "Stretch" />
                // < Setter Property = "HorizontalAlignment" Value = "Stretch" />
                //</ Style >
                // </ GridView.ItemContainerStyle >
                itemsPanel.ItemWidth = total / canBeFit;
            }
        }

要在视图中使用该行为类,首先添加命名空间引用

 xmlns:behave="using:UtilitiesUniversal.Behaviours"

然后,您可以使用附加属性如下。请注意,您还需要覆盖容器样式,以便其随项目一起拉伸。

 <GridView Name="gridData" Margin="14,10,0,10" behave:ListViewBehaviour.MinItemWidth="{Binding FakeBinding, FallbackValue=250}" behave:ListViewBehaviour.FillBeforeWrap="{Binding FakeBinding, FallbackValue=True}">
                <GridView.ItemContainerStyle>
                    <Style TargetType="GridViewItem">
                        <Setter Property="HorizontalContentAlignment" Value="Stretch"/>
                        <Setter Property="HorizontalAlignment" Value="Stretch"/>
                    </Style>
                </GridView.ItemContainerStyle>
                <GridView.ItemTemplate>
                    <DataTemplate>
                      
                    </DataTemplate>
                </GridView.ItemTemplate>
        </GridView>

 

关注点

我的经验是,与 WPF 不同,UWP 仅允许通过绑定设置附加属性值。这就是上面 XAML 中 FakeBinding 符号的原因。我利用不存在的绑定的 FallBackValue 参数来设置值。

历史

版本 1.0

© . All rights reserved.