ItemsControl - 网格子元素自动调整大小

Posted

技术标签:

【中文标题】ItemsControl - 网格子元素自动调整大小【英文标题】:ItemsControl - Grid child elements auto resize 【发布时间】:2017-10-12 08:47:57 【问题描述】:

我正在使用 Rachel Lim 的 GridHelper 来获取动态行数。我想要实现的是让每一行显示在另一行下方(完成),能够调整它们的大小(完成 - 使用 GridSplitter)并根据屏幕大小按比例调整内容大小。

结果:

我想要的:

Xaml:

<Grid>
    <ItemsControl ItemsSource="Binding RowSource" >
        <ItemsControl.ItemsPanel>
            <ItemsPanelTemplate>
                <Grid local:GridHelper.RowCount="Binding RowCount" />
            </ItemsPanelTemplate>
        </ItemsControl.ItemsPanel>
        <ItemsControl.ItemContainerStyle>
            <Style TargetType="ContentPresenter">
                <Setter Property="Grid.Row" Value="Binding RowNumber"/>
            </Style>
        </ItemsControl.ItemContainerStyle>
        <ItemsControl.ItemTemplate>
            <DataTemplate>
                <Grid>
                    <Grid.RowDefinitions>
                        <RowDefinition Height="Auto"/>
                        <RowDefinition Height="Auto"/>
                    </Grid.RowDefinitions>
                    <Grid>
                        <Grid.ColumnDefinitions>
                            <ColumnDefinition Width="*" />
                            <ColumnDefinition Width="Auto" />
                        </Grid.ColumnDefinitions>
                        <DataGrid>
                            <DataGrid.Columns>
                                <DataGridTextColumn Header="Col 1" />
                                <DataGridTextColumn Header="Col 2" />
                                <DataGridTextColumn Header="Col 3" />
                            </DataGrid.Columns>
                        </DataGrid>
                        <Button Grid.Column="1" Content="Btn" />
                    </Grid>
                    <GridSplitter Height="5" VerticalAlignment="Bottom" HorizontalAlignment="Stretch" Grid.Row="0" ResizeDirection="Rows" ResizeBehavior="CurrentAndNext"/>
                </Grid>
            </DataTemplate>
        </ItemsControl.ItemTemplate>
    </ItemsControl>
</Grid>

视图模型:

internal class MyViewModel

    public ObservableCollection<RowInfo> RowSource  get; set; 

    public int RowCount  get  return RowSource.Count;  

    public MyViewModel()
    
        RowSource = new ObservableCollection<RowInfo>()
        
            new RowInfo()  RowNumber = 0 ,
            new RowInfo()  RowNumber = 1 ,
            new RowInfo()  RowNumber = 2 
        ;
    

行信息:

public class RowInfo

    public int RowNumber  get; internal set; 

【问题讨论】:

我不确定我是否能很好地解决这个问题,您想要的是,即使其中没​​有内容,默认情况下行也应该有一定的高度。这就是需要吗? 是的,我希望行的大小与窗口大小成比例。 @mm8 得到了正确的部分,但调整大小(使用 GridSplitter)停止正常工作。 哦,基本上,您希望内容分布在整个视图中,当我调整窗口大小时,您还想缩放行和列? 没错。但我也想使用 GridSplitter 单独调整每一行的大小。 嗯,我明白了。我会试着写一个控件。 【参考方案1】:

我认为你的方法完全错误。您不能使用ItemsControl,因为GridSplitter 项目需要位于ItemsPanel 级别而不是DataTemplate - 否则,它将不起作用。

最好在网格本身上使用自定义行为 - 请参阅下面的示例代码:

public class GridAutoRowChildBehavior : Behavior<Grid>

    public static readonly DependencyProperty ItemTemplateProperty =
        DependencyProperty.Register("ItemTemplate", typeof(DataTemplate), typeof(GridAutoRowChildBehavior),
            new PropertyMetadata(null, OnGridPropertyChanged));

    public static readonly DependencyProperty ItemsSourceProperty =
        DependencyProperty.Register("ItemsSource", typeof(object), typeof(GridAutoRowChildBehavior),
            new PropertyMetadata(null, OnGridPropertyChanged));

    private static void OnGridPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    
        ((GridAutoRowChildBehavior) d).ResetGrid();
    

    private void ResetGrid()
    
        var source = ItemsSource as IEnumerable;
        if (source == null || ItemTemplate == null)
            return;
        AssociatedObject.Children.Clear();
        AssociatedObject.RowDefinitions.Clear();
        var count = 0;
        foreach (var item in source)
        
            var content = new ContentPresenter
            
                ContentTemplate = ItemTemplate,
                Content = item
            ;
            var splitter = new GridSplitter
            
                Height = 5,
                VerticalAlignment = VerticalAlignment.Bottom,
                HorizontalAlignment = HorizontalAlignment.Stretch
            ;
            AssociatedObject.RowDefinitions.Add(new RowDefinition  Height = new GridLength(1, GridUnitType.Star) );
            Grid.SetRow(content,count);
            Grid.SetRow(splitter,count);
            AssociatedObject.Children.Add(content);
            AssociatedObject.Children.Add(splitter);
            count++;
        

    

    public DataTemplate ItemTemplate
    
        get  return (DataTemplate) GetValue(ItemTemplateProperty); 
        set  SetValue(ItemTemplateProperty, value); 
    


    public object ItemsSource
    
        get  return GetValue(ItemsSourceProperty); 
        set  SetValue(ItemsSourceProperty, value); 
    

然后在您的 XAML 中,您将其编码如下:

<Grid>
    <i:Interaction.Behaviors>
        <local:GridAutoRowChildBehavior ItemsSource="Binding RowsSource">
            <local:GridAutoRowChildBehavior.ItemTemplate>
                <DataTemplate>
                    <Grid>
                        <Grid.ColumnDefinitions>
                            <ColumnDefinition Width="*" />
                            <ColumnDefinition Width="Auto" />
                        </Grid.ColumnDefinitions>
                        <DataGrid>
                            <DataGrid.Columns>
                                <DataGridTextColumn Header="Col 1" />
                                <DataGridTextColumn Header="Col 2" />
                                <DataGridTextColumn Header="Col 3" />
                            </DataGrid.Columns>
                        </DataGrid>
                        <Button Grid.Column="1" Content="Btn" />
                    </Grid>
                </DataTemplate>
            </local:GridAutoRowChildBehavior.ItemTemplate>
        </local:GridAutoRowChildBehavior>
    </i:Interaction.Behaviors>
</Grid>

我已经对此进行了测试,它完全符合您的需要。

您唯一需要做的就是将 Nuget 包 Systems.Windows.Interactivity.WPF 添加到您的项目中

【讨论】:

干得好,院长!在哪里可以深入了解交互库? 这是一个很好的起点msdn.microsoft.com/en-us/library/dn195669(v=vs.110).aspx 对于可能会阅读此内容的人来说只是一个小更新:如果您在 ItemControl 中有 Behavior(就像我有,但在此示例中未显示),您必须覆盖 OnAttached 并在其中调用 ResetGrid。受保护的覆盖无效 OnAttached() base.OnAttached();重置网格();还要检查 ResetGrid 方法中的 AssociatedObject 是否为空。【参考方案2】:

对您在GridHelper 类中创建的RowDefintions 使用星号:

public static void RowCountChanged(
    DependencyObject obj, DependencyPropertyChangedEventArgs e)

    if (!(obj is Grid) || (int)e.NewValue < 0)
        return;

    Grid grid = (Grid)obj;
    grid.RowDefinitions.Clear();

    for (int i = 0; i < (int)e.NewValue; i++)
        grid.RowDefinitions.Add(
            new RowDefinition()  Height = new GridLength(1, GridUnitType.Star) ); //<--

    SetStarRows(grid);

并将您的第一个RowDefinitionHeight 设置为*

<ItemsControl.ItemTemplate>
    <DataTemplate>
        <Grid Background="Yellow">
            <Grid.RowDefinitions>
                <RowDefinition Height="*"/>
                <RowDefinition Height="Auto"/>
            </Grid.RowDefinitions>
            <Grid>
                <Grid.ColumnDefinitions>
                    <ColumnDefinition Width="*" />
                    <ColumnDefinition Width="Auto" />
                </Grid.ColumnDefinitions>
                <DataGrid>
                    <DataGrid.Columns>
                        <DataGridTextColumn Header="Col 1" />
                        <DataGridTextColumn Header="Col 2" />
                        <DataGridTextColumn Header="Col 3" />
                    </DataGrid.Columns>
                </DataGrid>
                <Button Grid.Column="1" Content="Btn" />
            </Grid>
            <GridSplitter Height="5" VerticalAlignment="Bottom" HorizontalAlignment="Stretch" Grid.Row="0" ResizeDirection="Rows" ResizeBehavior="CurrentAndNext"/>
        </Grid>
    </DataTemplate>
</ItemsControl.ItemTemplate>

【讨论】:

这确实使内容按比例调整大小,但调整大小无法正常工作。 resizing 是什么意思? 我想使用 GridSplitter 来调整行的大小。 我希望内容分布在整个视图中,当我调整窗口大小时,您也想缩放行和列。而且我还想使用 GridSplitter 单独调整每一行的大小。

以上是关于ItemsControl - 网格子元素自动调整大小的主要内容,如果未能解决你的问题,请参考以下文章

动态添加的元素无法在动态调整的网格中正确显示[重复]

PHP格子在线自动发卡网源码-一键安装版

内部是不是有任何自动调整项目大小的控件

自动调整网格列以占用父级中的剩余空间

使用 WrapPanel 添加一个 TextBox 作为 ItemsControl 的最后一个元素

如何跟踪 ItemsControl 的哪个元素导致上下文菜单打开?