【问题标题】:How to fill a row of an ItemsControl when there is still an extra space remaining?当仍有额外空间剩余时如何填充一行 ItemsControl?
【发布时间】:2021-06-27 09:01:46
【问题描述】:

我的目标是在 ItemsControl 中显示我的数据时拥有 3 列。我发现大多数关于这个问题的答案是使用UniformGrid。像这样:

<ItemsControl x:Name="Tasks"
                ItemsSource="{Binding Tasks}">
    <ItemsControl.ItemsPanel>
        <ItemsPanelTemplate>
            <UniformGrid Columns="3"
                            Background="Green" />
        </ItemsPanelTemplate>
    </ItemsControl.ItemsPanel>

    <ItemsControl.ItemTemplate>
        <DataTemplate>
            <Button  Margin="0,10,0,16"
                        Padding="18,5,18,11"
                        Height="153"
                        Width="192"
                        Background="{StaticResource Card}"
                        Style="{StaticResource PlainButtonTheme}">
                        
                <!--Button Content-->
                
            </Button>
        </DataTemplate>
    </ItemsControl.ItemTemplate>
</ItemsControl>

我的 UI 现在看起来像这样(我刚刚添加了绿色背景,以便可以看到整个 UniformGrid):


我在这里想要的是占据整个空间的中间列(有点像网格的 * 行为)。我怎样才能做到这一点?

【问题讨论】:

  • 使用 UniformGrid 的重点在于其单元格的一致性(大小)。特别是,UniformGrid 不支持寻址单元格,而是按集合中的顺序顺序排列它们。如果您需要不同的单元格和/或集合元素需要设置为特定的单元格,那么您需要使用 Grid。将 Row 和 Column 属性添加到集合的元素以将它们放置在所需的单元格中,在 ItemContainerStyle 中设置与这些属性的绑定。
  • 我在使用UniformGrid 之前尝试过使用Grid,但我的子控件只是堆叠在一起,所以渲染后我只能看到一个控件。你能给我一些指点吗?
  • 有什么特殊原因不能使用 DataGrid?

标签: wpf xaml itemscontrol


【解决方案1】:

我在使用 UniformGrid 之前尝试过使用 Grid,但我的子控件只是堆叠在一起,所以渲染后我只能看到一个控件。

需要显式设置单元格的地址(行列)。

例子:

namespace GridItems
{
    public class TileData
    {
        public object Content { get; set; }
        public int Row { get; set; }
        public int Column { get; set; }

        public TileData() { }

        public TileData(object content, int row, int column)
        {
            Content = content;
            Row = row;
            Column = column;
        }
    }
}
namespace GridItems
{
    public class TilesViewModel
    {
        public TileData[] Tiles { get; } =
        {
            new TileData("First", 0, 0),
            new TileData("Second", 0, 1),
            new TileData("Third", 0, 2),
            new TileData("Fourth", 1, 0),
            new TileData("Fifth", 1, 1),
            new TileData("Sixth", 1, 2)
        };
    }
}
<Window x:Class="GridItems.GridItemsWindow"
        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:GridItems"
        mc:Ignorable="d"
        Title="GridItemsWindow" Height="450" Width="800">
    <FrameworkElement.DataContext>
        <local:TilesViewModel/>
    </FrameworkElement.DataContext>
    <FrameworkElement.Resources>
        <DataTemplate x:Key="TileTemplate" DataType="local:TileData">
            <Viewbox Margin="20">
                <TextBlock Text="{Binding Content}"
                           FontSize="20"
                           Padding="20"
                           Background="Aqua"/>
            </Viewbox>
        </DataTemplate>
        <Style x:Key="ItemStyle" TargetType="ContentPresenter">
            <Setter Property="Grid.Row" Value="{Binding Row}"/>
            <Setter Property="Grid.Column" Value="{Binding Column}"/>
        </Style>
        <ItemsPanelTemplate x:Key="GridTemplate">
            <Grid>
                <Grid.ColumnDefinitions>
                    <ColumnDefinition Width="100"/>
                    <ColumnDefinition />
                    <ColumnDefinition Width="100"/>
                </Grid.ColumnDefinitions>
                <Grid.RowDefinitions>
                    <RowDefinition/>
                    <RowDefinition/>
                </Grid.RowDefinitions>
            </Grid>
        </ItemsPanelTemplate>
    </FrameworkElement.Resources>
    <Grid>
        <ItemsControl ItemsSource="{Binding Tiles}"
                      ItemTemplate="{DynamicResource TileTemplate}"
                      ItemContainerStyle="{DynamicResource ItemStyle}"
                      ItemsPanel="{DynamicResource GridTemplate}"/>
    </Grid>
</Window>

补充:回答有关动态更改集合中元素数量以及相应地更改 Grid 行的问题。

使用附加属性的实现选项。

using System;
using System.Windows;
using System.Windows.Controls;

namespace GridItems
{
    public static class AutoRowsGrid
    {
        /// <summary> Returns the value of the ChildrenCount attached property for <paramref name = "grid" />. </summary>
        /// <param name = "grid"> <see cref = "Grid" /> whose property value will be returned. </param>
        /// <returns> <see cref = "int" /> property value. </returns>
        public static int? GetChildrenCount(Grid grid)
        {
            return (int?)grid.GetValue(ChildrenCountProperty);
        }

        /// <summary> Sets the ChildrenCount attached property for <paramref name = "grid" />. </summary>
        /// <param name = "grid"> <see cref = "Grid" /> whose property value will be returned. </param>
        /// <param name = "value"> <see cref = "int" /> value for the property. </param>
        public static void SetChildrenCount(Grid grid, int value)
        {
            grid.SetValue(ChildrenCountProperty, value);
        }

        /// <summary><see cref="DependencyProperty"/> for methods <see cref="GetChildrenCount(Grid)"/> и <see cref="SetChildrenCount(Grid, int)"/>.</summary>
        public static readonly DependencyProperty ChildrenCountProperty =
            DependencyProperty.RegisterAttached(nameof(GetChildrenCount).Substring(3), typeof(int?), typeof(AutoRowsGrid), new PropertyMetadata(null, CountChanged));

        private static void CountChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            Grid grid = (Grid)d;
            double count = (int)e.NewValue;

            int columns = grid.ColumnDefinitions.Count;
            if (columns < 1)
                columns = 1;

            int newRows = (int)Math.Ceiling(count / columns);

            int rows = grid.RowDefinitions.Count;

            if (newRows != rows)
            {
                if (newRows > rows)
                {
                    for (; newRows > rows; rows++)
                    {
                        grid.RowDefinitions.Add(new RowDefinition());
                    }
                }
                else
                {
                    for (rows--; newRows <= rows; rows--)
                    {
                        grid.RowDefinitions.RemoveAt(rows);
                    }
                }
            }
        }
    }
}
<Window x:Class="GridItems.GridItemsWindow"
        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:GridItems"
        mc:Ignorable="d"
        Title="GridItemsWindow" Height="450" Width="800">
    <FrameworkElement.DataContext>
        <local:TilesViewModel/>
    </FrameworkElement.DataContext>
    <FrameworkElement.Resources>
        <DataTemplate x:Key="TileTemplate" DataType="local:TileData">
            <Viewbox Margin="20">
                <TextBlock Text="{Binding Content}"
                           FontSize="20"
                           Padding="20"
                           Background="Aqua"/>
            </Viewbox>
        </DataTemplate>
        <Style x:Key="ItemStyle" TargetType="ContentPresenter">
            <Setter Property="Grid.Row" Value="{Binding Row}"/>
            <Setter Property="Grid.Column" Value="{Binding Column}"/>
        </Style>
        <ItemsPanelTemplate x:Key="GridTemplate">
            <Grid local:AutoRowsGrid.ChildrenCount="{Binding Items.Count, ElementName=listBox}">
                <Grid.ColumnDefinitions>
                    <ColumnDefinition Width="100"/>
                    <ColumnDefinition />
                    <ColumnDefinition Width="100"/>
                </Grid.ColumnDefinitions>
            </Grid>
        </ItemsPanelTemplate>
    </FrameworkElement.Resources>
    <Grid>
        <ItemsControl x:Name="listBox"
                      ItemsSource="{Binding Tiles}"
                      ItemTemplate="{DynamicResource TileTemplate}"
                      ItemContainerStyle="{DynamicResource ItemStyle}"
                      ItemsPanel="{DynamicResource GridTemplate}"/>
    </Grid>
</Window>

但如果你“非常正确”地做到这一点,那么在你的收藏中,它的元素就会出现明显的异质性。
左列、中列和右列的项目需要不同的呈现方式。
因此,基于 OOP 的原则,最正确的方法是创建一个包含一行的所有元素的类型,然后用行填充集合,而不是单独的单元格。

【讨论】:

  • 我真的让这个工作,但是......如果我只是设置例如两个 在我的视图中,然后我看不到我的所有记录,我什至无法滚动浏览所有记录。有没有办法根据检索到的项目使 RowDefinition “动态”?
  • 您想设置某种行为逻辑 - 这是可能的。但是你需要清楚地算法化你对这个逻辑的想法。这个逻辑可以稍后在 ViewModel 和/或附加属性中实现。
  • 我需要什么来实现这一点?顺便说一句,我不熟悉“行为”...
  • 对我来说,比方说,完全无法理解您想要为您的收藏获得什么样的演示文稿。您最初只问了一个关于如何使中间列自动宽度的问题。现在您需要更改列数。你想如何改变它们?从 ViewMode 动态地?还是“处理 XAML”?如果是动态的,那么您需要在 VM 中设置适当的属性,并在视图中 - 设置更改这些属性的行为。
  • 阅读我的答案的补充。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-10-29
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-11-11
相关资源
最近更新 更多