3

I'm using Rachel Lim's GridHelper to get dynamic number of rows. What I wanted to achieve is to have each row displayed one below another (done), to be able to resize them (done - using GridSplitter) and to have content resized proportionally to the screen size.

Result:

enter image description here

What I would like to have: enter image description here

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>

ViewModel:

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

RowInfo:

public class RowInfo
{
    public int RowNumber { get; internal set; }
}
Ivan Milosavljevic
  • 839
  • 1
  • 7
  • 19
  • I am not sure I get the question well, what you want is that the rows by default should have some height even if there is no content in them. Is that what's the need? – iam.Carrot Oct 17 '17 at 04:47
  • Yes, I want rows to be resized proportionally to window size. @mm8 got that part right but resizing (with GridSplitter) stopped to work correctly. – Ivan Milosavljevic Oct 17 '17 at 04:51
  • Oh so basically, you want the content to be spread across the whole view and when I resize the window then you want to scale the rows and columns as well? – iam.Carrot Oct 17 '17 at 04:52
  • Exactly. But also I want to use GridSplitter to resize each row individually. – Ivan Milosavljevic Oct 17 '17 at 05:12
  • Well I see. I'll try to write up a control. – iam.Carrot Oct 17 '17 at 05:32
  • Is the data contained in `RowSource` truly changeable and dynamic and it grows/shrinks? Or in reality there would be no more than non changing five static items? In other words are there actual limitation totals of the actual data which will be used? – ΩmegaMan Oct 17 '17 at 16:28
  • Also why does it have to be a datagrid? If it is only a line of data, why not create a custom layout of textboxes of headers/data? – ΩmegaMan Oct 17 '17 at 18:24
  • RowSource is dynamic. I have removed irrelevant part of that code so that I can paste it on SO. I don't have problem with DataGrid but with Grid. I want to have content of grid spread across whole view and to be able to resize each row. – Ivan Milosavljevic Oct 18 '17 at 05:58

2 Answers2

1

I think your approach is all wrong. You cannot use an ItemsControl, as the GridSplitter items need to be at the ItemsPanel level rather than in the DataTemplate - otherwise, it won't work.

You are better off using a custom behavior on the Grid Itself - see example code below:

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

Then in your XAML you code it up like this:

<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>

I have tested this and it works exactly as you need.

The only additional thing you need to do is add the Nuget package Systems.Windows.Interactivity.WPF to your project

EyasSH
  • 3,679
  • 22
  • 36
Dean Chalk
  • 20,076
  • 6
  • 59
  • 90
  • Great work Dean! Where can I read more about interactivity library in depth? – Ivan Milosavljevic Oct 18 '17 at 06:40
  • this is a good starting point https://msdn.microsoft.com/en-us/library/dn195669(v=vs.110).aspx – Dean Chalk Oct 18 '17 at 07:08
  • Just a small update for someone that might read this: if you have Behavior inside ItemControl (like I have but it is not shown in this sample) you have to override OnAttached and call ResetGrid inside of it. protected override void OnAttached() { base.OnAttached(); ResetGrid(); } Also check if AssociatedObject is null inside ResetGrid method. – Ivan Milosavljevic Oct 18 '17 at 08:21
0

Use star-sizing for the RowDefintions that you created in the GridHelper class:

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

And set the Height of your first RowDefinition to *:

<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>
mm8
  • 163,881
  • 10
  • 57
  • 88