Как настроить сетку как шаблон элемента управления Items?

Я пытаюсь создать ItemsControl, который использует сетку как ее ItemsPanel таким образом, что у нее есть два столбца, где ширина первых столбцов - ширина самого широкого элемента в этом столбце, и как строки, необходимые для отображения всех элементов

В принципе, я хочу следующее, но как-то внутри ItemsControl, чтобы я мог привязываться к коллекции объектов:

<Grid>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="auto"/>
            <ColumnDefinition Width="*"/>
        </Grid.ColumnDefinitions>

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

        <Label Content="{Binding Items[0].Header}"/>
        <TextBox Text="{Binding Items[0].Content}" Grid.Column="1"/>

        <Label Content="{Binding Items[1].Header}" Grid.Row="1"/>
        <TextBox Text="{Binding Items[1].Content}" Grid.Row="1" Grid.Column="1"/>

        <Label Content="{Binding Items[2].Header}" Grid.Row="2"/>
        <TextBox Text="{Binding Items[2].Content}" Grid.Row="2" Grid.Column="1"/>
    </Grid> 

Изменить: ответ Рейчел отлично работает, вот рабочий пример.

(я переместил Grid.IsSharedSizeScope = "True" в ItemsPanel, не уверен, что Rachel собирался поместить его в ItemTemplate (который не работал))

namespace WpfApplication23
{
    public partial class Window1 : Window
    {
        public List<Item> Items { get; set; }

        public Window1()
        {
            Items = new List<Item>() 
            { 
                new Item(){ Header="Item0", Content="someVal" },
                new Item(){ Header="Item1", Content="someVal" },
                new Item(){ Header="Item267676", Content="someVal" },
                new Item(){ Header="a", Content="someVal" },
                new Item(){ Header="bbbbbbbbbbbbbbbbbbbbbbbbbb", Content="someVal" },
                new Item(){ Header="ccccccc", Content="someVal" } 
            };

            InitializeComponent();

            DataContext = this;
        }
    }

    public class Item
    {
        public string Header { get; set; }
        public string Content { get; set; }
    }
}

<Window x:Class="WpfApplication23.Window1"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">

    <ItemsControl ItemsSource="{Binding Items}">

        <ItemsControl.ItemsPanel>
            <ItemsPanelTemplate>
                <StackPanel Grid.IsSharedSizeScope="True"/>
            </ItemsPanelTemplate>
        </ItemsControl.ItemsPanel>

        <ItemsControl.ItemTemplate>
            <DataTemplate>
                <Grid>
                    <Grid.ColumnDefinitions>
                        <ColumnDefinition SharedSizeGroup="ColumnOne" />
                        <ColumnDefinition Width="*" />
                    </Grid.ColumnDefinitions>
                    <Label Content="{Binding Header}"/>
                    <TextBox Text="{Binding Content}" Grid.Column="1"/>
                </Grid>
            </DataTemplate>
        </ItemsControl.ItemTemplate>
    </ItemsControl>
</Window>

Ответ 1

Здесь существует несколько проблем для ItemsControl:

  • Получение первого столбца в соответствии с шириной самого большого элемента
  • Создание динамического числа строк
  • Создание более чем одного элемента для каждой итерации ItemsControl

Последняя самая большая проблема, потому что ItemsControl обертывает каждый ItemTemplate в ContentPresenter, поэтому нет способа по умолчанию для создания более чем одного элемента на панели для каждой итерации ItemsControl. Ваш конечный результат будет выглядеть следующим образом:

<Grid>
    ...

    <ContentPresenter>
        <Label Content="{Binding Items[0].Header}"/>
        <TextBox Text="{Binding Items[0].Content}" Grid.Column="1"/>
    </ContentPresenter>
    <ContentPresenter>
        <Label Content="{Binding Items[1].Header}" Grid.Row="1"/>
        <TextBox Text="{Binding Items[1].Content}" Grid.Row="1" Grid.Column="1"/>
    </ContentPresenter>
    <ContentPresenter>
        <Label Content="{Binding Items[2].Header}" Grid.Row="2"/>
        <TextBox Text="{Binding Items[2].Content}" Grid.Row="2" Grid.Column="1"/>
    </ContentPresenter>
</Grid> 

Мое лучшее предложение было бы создать ItemTemplate, который содержит 1x2 Grid, и использовать Grid.IsSharedSizeScope, чтобы сделать ширину первого общего раздела. (ItemsPanelTemplate останется по умолчанию StackPanel.)

Таким образом, конечный результат будет выглядеть следующим образом:

<StackPanel>
    <ContentPresenter>
        <Grid IsSharedSizeScope="True">
            <Grid.ColumnDefinitions>
                <ColumnDefinition SharedSizeGroup="ColumnOne" />
                <ColumnDefinition Width="*" />
            </Grid.ColumnDefinitions>
            <Label Content="{Binding Header}"/>
            <TextBox Text="{Binding Content}" Grid.Column="1"/>
        </Grid>
    </ContentPresenter>
    <ContentPresenter>
        <Grid IsSharedSizeScope="True">
            <Grid.ColumnDefinitions>
                <ColumnDefinition SharedSizeGroup="ColumnOne" />
                <ColumnDefinition Width="*" />
            </Grid.ColumnDefinitions>
            <Label Content="{Binding Header}"/>
            <TextBox Text="{Binding Content}" Grid.Column="1"/>
        </Grid>
    </ContentPresenter>
    ...
</StackPanel> 

Ответ 2

Вы можете использовать ListView

<ListView ItemsSource="{Binding MyList}">
    <ListView.View>
        <GridView>
            <GridView.ColumnHeaderContainerStyle>
                <Style TargetType="{x:Type GridViewColumnHeader}">
                    <Setter Property="Visibility" Value="Collapsed" />
                </Style>
            </GridView.ColumnHeaderContainerStyle>
            <GridViewColumn 
                Header="" 
                Width="Auto" 
                DisplayMemberBinding="{Binding Header}"/>
            <GridViewColumn 
                Header="" 
                DisplayMemberBinding="{Binding Value}"/>
        </GridView>
    </ListView.View>
</ListView>

ColumnHeaderContainerStyle скрывает GridViewHeader