如何从DataGrid的列中获取值?

编程语言 2026-07-09

我正在开发一个WPF应用,并准备将其重构为MVVM模式。我遇到了一个数据网格的问题,它由SQL查询提供数据,在XAML的某一列为每条记录放置了按钮,如下所示:

<DataGrid Name="NestCards" FontSize="12" 
     ScrollViewer.CanContentScroll="True" MaxHeight="400" ColumnWidth="*">
    <DataGrid.Columns>
        <DataGridTemplateColumn>
            <DataGridTemplateColumn.CellTemplate>
                <DataTemplate>
                    <Button Click="GetDetails">Details</Button>
                </DataTemplate>
            </DataGridTemplateColumn.CellTemplate>
        </DataGridTemplateColumn>
    </DataGrid.Columns>
</DataGrid>

过去我可以直接把sender当作按钮来处理,没问题:

Button pressed = sender as Button;
var nestName = TypeDescriptor.GetProperties(pressed.DataContext)["nestName"].GetValue(pressed.DataContext);
var nestDate = TypeDescriptor.GetProperties(pressed.DataContext)["dueDate"].GetValue(pressed.DataContext);
var orderStatus = TypeDescriptor.GetProperties(pressed.DataContext)["orderStatus"].GetValue(pressed.DataContext);

NestStatus.Text = orderStatus.ToString();
NestName.Text = nestName.ToString();
SchedFor.Text = nestDate.ToString();

我需要获取被点击按钮所在行的至少 NestName 的值,以便把它传给另一条SQL查询,但在MVVM下并不那么直接。

解决方案

在MVVM中,不应该直接从 DataGridColumn 读取值。

一个 DataGrid 只是数据的可视化表示。值应该来自绑定到该行的数据项。

为该行创建一个模型:

public sealed class NestCard
{
    public string NestName { get; set; }
    public DateTime DueDate { get; set; }
    public string OrderStatus { get; set; }
}

DataGrid 绑定到ViewModel中的一个集合:

public ObservableCollection<NestCard> NestCards { get; } = new();

然后绑定按钮命令,并把当前行项作为 CommandParameter 传递:

<DataGrid ItemsSource="{Binding NestCards}"
          AutoGenerateColumns="False">

    <DataGrid.Columns>
        <DataGridTextColumn Header="Nest Name"
                            Binding="{Binding NestName}" />

        <DataGridTemplateColumn Header="Details">
            <DataGridTemplateColumn.CellTemplate>
                <DataTemplate>
                    <Button Content="Details"
                            Command="{Binding DataContext.GetDetailsCommand,
                                              RelativeSource={RelativeSource AncestorType=DataGrid}}"
                            CommandParameter="{Binding}" />
                </DataTemplate>
            </DataGridTemplateColumn.CellTemplate>
        </DataGridTemplateColumn>
    </DataGrid.Columns>
</DataGrid>

使用 RelativeSource 绑定,是因为 Button 位于行模板内。按钮本身的 DataContext 是行项,因此绑定会向上传到父级 DataGrid,通过 DataContext.GetDetailsCommand 来访问ViewModel的命令。

在ViewModel中:

public ICommand GetDetailsCommand { get; }

public MyViewModel()
{
    GetDetailsCommand = new RelayCommand<NestCard>(GetDetails);
}

private void GetDetails(NestCard row)
{
    if (row is null)
        return;

    string nestName = row.NestName;

    // Use nestName for your next query here.
}

RelayCommand<T> 只是一个示例命令实现。你可以使用你自己的 ICommand 实现,或者来自 CommunityToolkit.Mvvm 的实现。

重要的部分是:

CommandParameter="{Binding}"

在一个 DataGridTemplateColumn.CellTemplate 内,当前的 DataContext 就是行项。因此 {Binding} 将整行对象传递给命令。

这意味着你无需在代码背后访问 DataGrid、列、按钮,或 TextBox.Text

所以不要这样:

Button pressed = sender as Button;
var nestName = TypeDescriptor.GetProperties(pressed.DataContext)["nestName"].GetValue(...);

改用强类型的行模型:

private void GetDetails(NestCard row)
{
    var nestName = row.NestName;
}

这保持了视图负责显示,视图模型负责行为和数据流。

站内所有文章版权归属LeftHeroAI导航站,无授权禁止任何主体转载、抄袭、复制内容,亦不得私自架设镜像站点。一经侵权,本站将通过法律途径追责。

相关文章