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

WPF MVVM模式下如何将DataGrid选中行同步至TextBox与ViewModel

解决WPF MVVM中DataGrid选中行无法传递到ViewModel的问题

问题核心

你当前的TextBox直接绑定DataGrid的SelectedItem,这种方式绕开了ViewModel,导致ViewModel无法获取选中数据。正确的MVVM模式应该让DataGrid的选中项直接绑定到ViewModel的属性,再让TextBox绑定该属性。

修复步骤

  1. 修改DataGrid的SelectedItem绑定
    给DataGrid添加SelectedItem绑定,关联到ViewModel中的CurrentCar属性,同时保持ItemsSource绑定不变。

  2. 调整TextBox的绑定目标
    将所有TextBox的绑定路径从SelectedItem.XXX改为CurrentCar.XXX,不再直接引用DataGrid的元素名。

  3. 确保ViewModel和实体类实现通知接口
    ViewModel需要实现INotifyPropertyChanged接口,CurrentCar属性变更时触发通知;Car实体类也需要实现该接口,保证TextBox修改后能同步回实体。

修改后的XAML代码

<StackPanel>
    <ScrollViewer Grid.Row="2" Grid.Column="1" Height="300" Width="auto">
        <DataGrid x:Name="dtGrd" Width="800" AutoGenerateColumns="False" Background="Azure" 
                  ItemsSource="{Binding Path=Cars, Mode=TwoWay}" 
                  IsReadOnly="True" SelectionMode="Single"
                  SelectedItem="{Binding CurrentCar, Mode=TwoWay}"> <!-- 新增SelectedItem绑定 -->
            <DataGrid.Columns>
                <DataGridTextColumn Header="Id" Width="auto" Binding="{Binding Path=Id}"/>
                <DataGridTextColumn Header="Brand" Width="auto" Binding="{Binding Path=Brand}"/>
                <DataGridTextColumn Header="Model" Width="auto" Binding="{Binding Path=Model}"/>
                <DataGridTextColumn Header="Fuel" Width="auto" Binding="{Binding Path=Fuel}"/>
                <DataGridTextColumn Header="MaxPassenger" Width="auto" Binding="{Binding Path=MaxPassenger}"/>
                <DataGridTextColumn Header="NumberOfDoors" Width="auto" Binding="{Binding Path=NumberOfDoors}"/>
                <DataGridTextColumn Header="AvailableType" Width="auto" Binding="{Binding Path=AvailableType}"/>
                <DataGridTextColumn Header="Price" Width="auto" Binding="{Binding Path=Price}"/>
            </DataGrid.Columns>
        </DataGrid>
    </ScrollViewer>
</StackPanel>
<Grid Margin="5" Height="150">
    <Grid.ColumnDefinitions>
        <ColumnDefinition Width="225"/>
        <ColumnDefinition Width="225"/>
        <ColumnDefinition Width="225"/>
        <ColumnDefinition Width="225"/>
    </Grid.ColumnDefinitions>
    <StackPanel Grid.Column="0" Orientation="Vertical">
        <Label Content="ID" HorizontalAlignment="Center"/>
        <TextBox x:Name="txtBxId" Padding="5" Margin="5" Text="{Binding Path=CurrentCar.Id, Mode=TwoWay}"/>
        <Label Content="Brand" HorizontalAlignment="Center"/>
        <TextBox x:Name="txtBxBrand" Padding="5" Margin="5" Text="{Binding Path=CurrentCar.Brand, Mode=TwoWay}"/>
    </StackPanel>
    <StackPanel Grid.Column="1" Orientation="Vertical">
        <Label Content="Model" HorizontalAlignment="Center"/>
        <TextBox x:Name="txtBxModel" Padding="5" Margin="5" Text="{Binding Path=CurrentCar.Model, Mode=TwoWay}"/>
        <Label Content="Fuel" HorizontalAlignment="Center"/>
        <TextBox x:Name="txtBxFuel" Padding="5" Margin="5" Text="{Binding Path=CurrentCar.Fuel, Mode=TwoWay}"/>
    </StackPanel>
    <StackPanel Grid.Column="2" Orientation="Vertical">
        <Label Content="MaxPassanger" HorizontalAlignment="Center"/>
        <TextBox x:Name="txtBxMaxPassanger" Padding="5" Margin="5" Text="{Binding Path=CurrentCar.MaxPassenger, Mode=TwoWay}"/>
        <Label Content="NumberOfDoors" HorizontalAlignment="Center"/>
        <TextBox x:Name="txtBxNumberOfDoors" Padding="5" Margin="5" Text="{Binding Path=CurrentCar.NumberOfDoors, Mode=TwoWay}"/>
    </StackPanel>
    <StackPanel Grid.Column="3" Orientation="Vertical">
        <Label Content="AvailableType" HorizontalAlignment="Center"/>
        <TextBox x:Name="txtBxAvailableType" Padding="5" Margin="5" Text="{Binding Path=CurrentCar.AvailableType, Mode=TwoWay}"/>
        <Label Content="Price" HorizontalAlignment="Center"/>
        <TextBox x:Name="txtBxPrice" Padding="5" Margin="5" Text="{Binding Path=CurrentCar.Price, Mode=TwoWay}"/>
    </StackPanel>
</Grid>

ViewModel示例代码

using System.ComponentModel;
using System.Collections.ObjectModel;

public class CarViewModel : INotifyPropertyChanged
{
    private Car _currentCar;
    public ObservableCollection<Car> Cars { get; set; }

    public Car CurrentCar
    {
        get => _currentCar;
        set
        {
            _currentCar = value;
            OnPropertyChanged(nameof(CurrentCar));
        }
    }

    public CarViewModel()
    {
        // 初始化Cars集合,模拟数据
        Cars = new ObservableCollection<Car>
        {
            new Car { Id = 1, Brand = "Toyota", Model = "Camry", Fuel = "Gasoline", MaxPassenger = 5, NumberOfDoors = 4, AvailableType = "Sedan", Price = 25000 },
            new Car { Id = 2, Brand = "Honda", Model = "CR-V", Fuel = "Hybrid", MaxPassenger = 5, NumberOfDoors = 4, AvailableType = "SUV", Price = 30000 }
        };
    }

    public event PropertyChangedEventHandler PropertyChanged;
    protected void OnPropertyChanged(string propertyName)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }
}

Car实体类示例代码

using System.ComponentModel;

public class Car : INotifyPropertyChanged
{
    private int _id;
    private string _brand;
    private string _model;
    private string _fuel;
    private int _maxPassenger;
    private int _numberOfDoors;
    private string _availableType;
    private decimal _price;

    public int Id
    {
        get => _id;
        set
        {
            _id = value;
            OnPropertyChanged(nameof(Id));
        }
    }

    public string Brand
    {
        get => _brand;
        set
        {
            _brand = value;
            OnPropertyChanged(nameof(Brand));
        }
    }

    public string Model
    {
        get => _model;
        set
        {
            _model = value;
            OnPropertyChanged(nameof(Model));
        }
    }

    public string Fuel
    {
        get => _fuel;
        set
        {
            _fuel = value;
            OnPropertyChanged(nameof(Fuel));
        }
    }

    public int MaxPassenger
    {
        get => _maxPassenger;
        set
        {
            _maxPassenger = value;
            OnPropertyChanged(nameof(MaxPassenger));
        }
    }

    public int NumberOfDoors
    {
        get => _numberOfDoors;
        set
        {
            _numberOfDoors = value;
            OnPropertyChanged(nameof(NumberOfDoors));
        }
    }

    public string AvailableType
    {
        get => _availableType;
        set
        {
            _availableType = value;
            OnPropertyChanged(nameof(AvailableType));
        }
    }

    public decimal Price
    {
        get => _price;
        set
        {
            _price = value;
            OnPropertyChanged(nameof(Price));
        }
    }

    public event PropertyChangedEventHandler PropertyChanged;
    protected void OnPropertyChanged(string propertyName)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }
}

额外注意事项

  • 确保View的DataContext已正确设置为ViewModel实例(可以在XAML的Window/UserControl标签中设置,或者在后台代码中赋值)
  • 如果不需要TextBox修改后同步回DataGrid,可以把TextBox的Mode改为OneWay,但保持CurrentCar的双向绑定以便ViewModel获取选中数据

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.13 02:05:16