1
votes

In my WPF application, I have a collection to which I would like to bind a grid of controls. An example of the desired layout (TL;DR I want this from an ItemsSource):

Desired layout http://imagebin.ca/img/27UTke

<Grid ShowGridLines="True">
    <Grid.ColumnDefinitions>
        <ColumnDefinition Width="Auto" />
        <ColumnDefinition Width="*" />
        <ColumnDefinition Width="Auto" />
    </Grid.ColumnDefinitions>

    <Grid.RowDefinitions>
        <RowDefinition Height="Auto" />
        <RowDefinition Height="Auto" />
        <RowDefinition Height="Auto" />
        <RowDefinition Height="Auto" />
    </Grid.RowDefinitions>

    <Label Grid.Row="0" Grid.Column="0">FileUploader</Label>
    <ComboBox Grid.Row="0" Grid.Column="1" />
    <Button Grid.Row="0" Grid.Column="2">Edit</Button>

    <Label Grid.Row="1" Grid.Column="0">TextUploader</Label>
    <ComboBox Grid.Row="1" Grid.Column="1" />
    <Button Grid.Row="1" Grid.Column="2">Edit</Button>

    <Label Grid.Row="2" Grid.Column="0">UrlShortener</Label>
    <ComboBox Grid.Row="2" Grid.Column="1" />
    <Button Grid.Row="2" Grid.Column="2">Edit</Button>

    <Label Grid.Row="3" Grid.Column="0">ImageUploader</Label>
    <ComboBox Grid.Row="3" Grid.Column="1" />
    <Button Grid.Row="3" Grid.Column="2">Edit</Button>
</Grid>

This example is has hardcoded values; I want to bind the collection dynamically, like using a ListView. However, using a ListView with a GridView isn't what I want; I don't need selection, headers, sorting, etc. To the user, the layout is a neatly organized set of options, not a data set in a grid.


I haven't been able to do this binding properly. I've tried using an ItemsControl with a Grid on the outside, but the Grid is ignored:

Failed attempt 1 http://imagebin.ca/img/kTUordk

<Grid ShowGridLines="True">
    <Grid.ColumnDefinitions>
        <ColumnDefinition Width="Auto" />
        <ColumnDefinition Width="*" />
        <ColumnDefinition Width="Auto" />
    </Grid.ColumnDefinitions>

    <ItemsControl ItemsSource="{Binding MyData}">
        <ItemsControl.ItemTemplate>
            <DataTemplate>
                <ItemsControl>
                    <Label Grid.Column="0">Text here</Label>
                    <ComboBox Grid.Column="1" />
                    <Button Grid.Column="2">Edit</Button>
                </ItemsControl>
            </DataTemplate>
        </ItemsControl.ItemTemplate>
    </ItemsControl>
</Grid>

I've also used an ItemsControl with a Grid in the panel, but this creates one Grid per item, which is ugly:

Failed attempt 2 http://imagebin.ca/img/xHo__-JD

<ItemsControl ItemsSource="{Binding Data}">
    <ItemsControl.ItemTemplate>
        <DataTemplate>
            <ItemsControl>
                <ItemsControl.ItemsPanel>
                    <ItemsPanelTemplate>
                        <Grid ShowGridLines="True">
                            <Grid.ColumnDefinitions>
                                <ColumnDefinition Width="Auto" />
                                <ColumnDefinition Width="*" />
                                <ColumnDefinition Width="Auto" />
                            </Grid.ColumnDefinitions>
                        </Grid>
                    </ItemsPanelTemplate>
                </ItemsControl.ItemsPanel>

                <Label Grid.Column="0" Content="{Binding Features}" />
                <ComboBox Grid.Column="1" />
                <Button Grid.Column="2">Edit</Button>
            </ItemsControl>
        </DataTemplate>
    </ItemsControl.ItemTemplate>
</ItemsControl>

After some research, I discovered the SharedSizeGroup property of column and row grid definitions. I have tried this with the previous attempt; however, the middle column (with width *) does not seem to resize itself to fill the full width of the window:

Failed attempt 3 http://imagebin.ca/img/nEoMJmy

<ItemsControl ItemsSource="{Binding Data}" Grid.IsSharedSizeScope="True">
    <ItemsControl.ItemTemplate>
        <DataTemplate>
            <ItemsControl>
                <ItemsControl.ItemsPanel>
                    <ItemsPanelTemplate>
                        <Grid ShowGridLines="True">
                            <Grid.ColumnDefinitions>
                                <ColumnDefinition Width="Auto" SharedSizeGroup="A" />
                                <ColumnDefinition Width="*" SharedSizeGroup="B" />
                                <ColumnDefinition Width="Auto" SharedSizeGroup="C" />
                            </Grid.ColumnDefinitions>
                        </Grid>
                    </ItemsPanelTemplate>
                </ItemsControl.ItemsPanel>

                <Label Grid.Column="0" Content="{Binding Features}" />
                <ComboBox Grid.Column="1" />
                <Button Grid.Column="2">Edit</Button>
            </ItemsControl>
        </DataTemplate>
    </ItemsControl.ItemTemplate>
</ItemsControl>

What is the best way to accomplish the first layout?

1

1 Answers

1
votes

John Bowen wrote a blog entry about a pitfall of using * with SharedSizeGroups. Using his suggested workaround, I was able to get the desired layout:

<Grid Name="grid">
    <Grid.ColumnDefinitions>
        <ColumnDefinition Width="Auto" SharedSizeGroup="A" />
        <ColumnDefinition Width="*" />
        <ColumnDefinition Width="Auto" SharedSizeGroup="C" />
    </Grid.ColumnDefinitions>
</Grid>

<ItemsControl ItemsSource="{Binding Data}" Grid.IsSharedSizeScope="True">
    <ItemsControl.ItemTemplate>
        <DataTemplate>
            <ItemsControl>
                <ItemsControl.ItemsPanel>
                    <ItemsPanelTemplate>
                        <Grid ShowGridLines="True">
                            <Grid.ColumnDefinitions>
                                <ColumnDefinition Width="Auto" SharedSizeGroup="A" />
                                <ColumnDefinition Width="{Binding ColumnDefinitions[1].Width, ElementName=grid}" />
                                <ColumnDefinition Width="Auto" SharedSizeGroup="C" />
                            </Grid.ColumnDefinitions>
                        </Grid>
                    </ItemsPanelTemplate>
                </ItemsControl.ItemsPanel>

                <Label Grid.Column="0" Content="{Binding Features}" />
                <ComboBox Grid.Column="1" />
                <Button Grid.Column="2">Edit</Button>
            </ItemsControl>
        </DataTemplate>
    </ItemsControl.ItemTemplate>
</ItemsControl>

Sadly, this seems like a hack; I'm sure there's a better solution out there for accomplishing what I want.