You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

WPF如何带表头在网格中显示ObservableCollection?

解决方案:实现绑定ObservableCollection的带表头网格布局

针对你的需求,提供两种实用方案,分别适配不同场景:


方案一:用ItemsControl实现完全自定义网格布局(推荐,无DataGrid冗余特性)

这种方式无需依赖DataGrid,完全通过嵌套ItemsControl实现行列布局,灵活度最高,适合仅展示、不需要DataGrid交互特性的场景。

1. 调整ViewModel结构(按行分组)

先定义行分组的ViewModel,方便绑定每行的表头和单元格数据:

// 单个指标的ViewModel(保持你原有的定义,补充必要属性)
public class ReadOnlyIndicatorWithUnitsViewModel : INotifyPropertyChanged
{
    public string Header { get; set; }
    public string Value { get; set; }
    public string Units { get; set; }

    // 实现INotifyPropertyChanged接口(省略具体代码)
    public event PropertyChangedEventHandler PropertyChanged;
}

// 行分组ViewModel
public class IndicatorRowGroupViewModel : INotifyPropertyChanged
{
    public string RowHeader { get; set; }
    public ObservableCollection<ReadOnlyIndicatorWithUnitsViewModel> RowIndicators { get; set; }

    public IndicatorRowGroupViewModel()
    {
        RowIndicators = new ObservableCollection<ReadOnlyIndicatorWithUnitsViewModel>();
    }

    // 实现INotifyPropertyChanged接口(省略具体代码)
    public event PropertyChangedEventHandler PropertyChanged;
}

// 主ViewModel
public class NavigationViewModel : INotifyPropertyChanged
{
    public ObservableCollection<IndicatorRowGroupViewModel> IndicatorGridData { get; set; }

    public NavigationViewModel()
    {
        // 示例数据
        IndicatorGridData = new ObservableCollection<IndicatorRowGroupViewModel>
        {
            new IndicatorRowGroupViewModel
            {
                RowHeader = "环境参数",
                RowIndicators =
                {
                    new ReadOnlyIndicatorWithUnitsViewModel { Header = "温度", Value = "26", Units = "℃" },
                    new ReadOnlyIndicatorWithUnitsViewModel { Header = "湿度", Value = "58", Units = "%" }
                }
            },
            new IndicatorRowGroupViewModel
            {
                RowHeader = "设备状态",
                RowIndicators =
                {
                    new ReadOnlyIndicatorWithUnitsViewModel { Header = "转速", Value = "1450", Units = "rpm" },
                    new ReadOnlyIndicatorWithUnitsViewModel { Header = "压力", Value = "0.75", Units = "MPa" }
                }
            }
        };
    }

    // 实现INotifyPropertyChanged接口(省略具体代码)
    public event PropertyChangedEventHandler PropertyChanged;
}

2. XAML布局实现

通过外层ItemsControl渲染每行,内层ItemsControl渲染该行的所有单元格:

<Window.DataContext>
    <local:NavigationViewModel />
</Window.DataContext>

<!-- 外层ItemsControl:渲染每一行 -->
<ItemsControl ItemsSource="{Binding IndicatorGridData}">
    <ItemsControl.ItemsPanel>
        <ItemsPanelTemplate>
            <StackPanel Margin="10" />
        </ItemsPanelTemplate>
    </ItemsControl.ItemsPanel>

    <ItemsControl.ItemTemplate>
        <DataTemplate>
            <Grid Margin="0,5">
                <Grid.ColumnDefinitions>
                    <ColumnDefinition Width="Auto" /> <!-- 行表头列 -->
                    <ColumnDefinition Width="*" /> <!-- 单元格容器列 -->
                </Grid.ColumnDefinitions>

                <!-- 行表头 -->
                <TextBlock Grid.Column="0" 
                           Text="{Binding RowHeader}" 
                           FontWeight="Bold" 
                           Margin="5,2" 
                           VerticalAlignment="Center"/>

                <!-- 内层ItemsControl:渲染该行的所有单元格 -->
                <ItemsControl Grid.Column="1" ItemsSource="{Binding RowIndicators}">
                    <ItemsControl.ItemsPanel>
                        <ItemsPanelTemplate>
                            <UniformGrid Rows="1" Columns="{Binding RowIndicators.Count}" />
                        </ItemsPanelTemplate>
                    </ItemsControl.ItemsPanel>

                    <ItemsControl.ItemTemplate>
                        <DataTemplate>
                            <!-- 绑定你的自定义UserControl -->
                            <local:ReadOnlyIndicatorWithUnits 
                                Header="{Binding Header}"
                                Value="{Binding Value}"
                                Units="{Binding Units}"
                                Margin="3"/>
                        </DataTemplate>
                    </ItemsControl.ItemTemplate>
                </ItemsControl>
            </Grid>
        </DataTemplate>
    </ItemsControl.ItemTemplate>
</ItemsControl>

方案二:自定义DataGrid模板(保留DataGrid交互特性)

如果需要DataGrid的排序、选择等原生特性,可以通过自定义列模板和分组样式实现,同时适配你的自定义UserControl。

1. XAML实现(含分组和自定义列)

<Window.DataContext>
    <local:NavigationViewModel />
</Window.DataContext>

<!-- 定义分组用的CollectionViewSource -->
<Window.Resources>
    <CollectionViewSource x:Key="GroupedIndicatorData" Source="{Binding IndicatorGridData}">
        <CollectionViewSource.GroupDescriptions>
            <PropertyGroupDescription PropertyName="RowHeader" />
        </CollectionViewSource.GroupDescriptions>
    </CollectionViewSource>
</Window.Resources>

<DataGrid ItemsSource="{Binding Source={StaticResource GroupedIndicatorData}}"
          AutoGenerateColumns="False"
          CanUserAddRows="False"
          CanUserDeleteRows="False"
          IsReadOnly="True"
          Margin="10">

    <!-- 自定义分组表头样式 -->
    <DataGrid.GroupStyle>
        <GroupStyle>
            <GroupStyle.HeaderTemplate>
                <DataTemplate>
                    <TextBlock Text="{Binding Name}" 
                               FontWeight="Bold" 
                               Padding="5"
                               Background="#F0F0F0"/>
                </DataTemplate>
            </GroupStyle.HeaderTemplate>
        </GroupStyle>
    </DataGrid.GroupStyle>

    <!-- 手动定义列,绑定自定义UserControl -->
    <DataGridTemplateColumn Header="温度">
        <DataGridTemplateColumn.CellTemplate>
            <DataTemplate>
                <local:ReadOnlyIndicatorWithUnits 
                    Header="温度"
                    Value="{Binding RowIndicators[0].Value}"
                    Units="{Binding RowIndicators[0].Units}"/>
            </DataTemplate>
        </DataGridTemplateColumn.CellTemplate>
    </DataGridTemplateColumn>

    <DataGridTemplateColumn Header="湿度">
        <DataGridTemplateColumn.CellTemplate>
            <DataTemplate>
                <local:ReadOnlyIndicatorWithUnits 
                    Header="湿度"
                    Value="{Binding RowIndicators[1].Value}"
                    Units="{Binding RowIndicators[1].Units}"/>
            </DataTemplate>
        </DataGridTemplateColumn.CellTemplate>
    </DataGridTemplateColumn>

    <!-- 按需添加更多列 -->
</DataGrid>

2. 动态列扩展(可选)

如果列数不固定,可以通过附加属性实现动态生成列:

// 自定义附加属性,用于DataGrid动态绑定列
public static class DataGridDynamicColumns
{
    public static readonly DependencyProperty BindableColumnsProperty =
        DependencyProperty.RegisterAttached(
            "BindableColumns", 
            typeof(ObservableCollection<DataGridColumn>), 
            typeof(DataGridDynamicColumns), 
            new UIPropertyMetadata(null, OnBindableColumnsChanged));

    private static void OnBindableColumnsChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        if (d is not DataGrid dataGrid) return;
        if (e.OldValue is ObservableCollection<DataGridColumn> oldCols)
        {
            foreach (var col in oldCols) dataGrid.Columns.Remove(col);
            oldCols.CollectionChanged -= ColumnsCollectionChanged;
        }
        if (e.NewValue is ObservableCollection<DataGridColumn> newCols)
        {
            foreach (var col in newCols) dataGrid.Columns.Add(col);
            newCols.CollectionChanged += ColumnsCollectionChanged;
        }
    }

    private static void ColumnsCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
    {
        if (sender is not ObservableCollection<DataGridColumn> cols || e.Action == NotifyCollectionChangedAction.Reset) return;
        var dataGrid = cols.FirstOrDefault()?.DataGridOwner;
        if (dataGrid == null) return;

        switch (e.Action)
        {
            case NotifyCollectionChangedAction.Add:
                foreach (DataGridColumn col in e.NewItems) dataGrid.Columns.Add(col);
                break;
            case NotifyCollectionChangedAction.Remove:
                foreach (DataGridColumn col in e.OldItems) dataGrid.Columns.Remove(col);
                break;
        }
    }

    public static void SetBindableColumns(DependencyObject element, ObservableCollection<DataGridColumn> value)
        => element.SetValue(BindableColumnsProperty, value);

    public static ObservableCollection<DataGridColumn> GetBindableColumns(DependencyObject element)
        => (ObservableCollection<DataGridColumn>)element.GetValue(BindableColumnsProperty);
}

然后在ViewModel中添加列集合,动态生成列:

public ObservableCollection<DataGridColumn> DynamicColumns { get; set; }

public NavigationViewModel()
{
    DynamicColumns = new ObservableCollection<DataGridColumn>();
    // 动态添加列示例
    DynamicColumns.Add(new DataGridTemplateColumn
    {
        Header = "转速",
        CellTemplate = new DataTemplate
        {
            VisualTree = new FrameworkElementFactory(typeof(ReadOnlyIndicatorWithUnits))
            {
                Setters =
                {
                    new Setter(ReadOnlyIndicatorWithUnits.HeaderProperty, "转速"),
                    new Setter(ReadOnlyIndicatorWithUnits.ValueProperty, new Binding("RowIndicators[0].Value")),
                    new Setter(ReadOnlyIndicatorWithUnits.UnitsProperty, new Binding("RowIndicators[0].Units"))
                }
            }
        }
    });
}

最后在XAML中使用:

<DataGrid local:DataGridDynamicColumns.BindableColumns="{Binding DynamicColumns}"
          ItemsSource="{Binding Source={StaticResource GroupedIndicatorData}}"
          AutoGenerateColumns="False"
          CanUserAddRows="False"
          CanUserDeleteRows="False"
          IsReadOnly="True"
          Margin="10">
    <!-- 分组样式同上 -->
</DataGrid>

内容的提问来源于stack exchange,提问作者Curtwagner1984

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.24 21:39:18