单击 DataGrid 中的按钮后,我尝试在 C# WPF MVVM 中添加另一行。问题是,一旦我单击其他位置(例如在文本框中),就会添加该行。这意味着在我单击“添加行”按钮后,DataGrid 不会立即更新。
NewDocumentView.xaml
<DataGrid x:Name="DataGrid"
AutoGenerateColumns="False"
HorizontalAlignment="Left"
VerticalAlignment="Top"
Grid.Row="1"
Margin="20,350,0,0"
ItemsSource="{Binding Items}">
<DataGrid.Columns>
<DataGridTextColumn Header="Description" Binding="{Binding Description}" Width="500"/>
<DataGridTextColumn Header="Unit" Binding="{Binding Unit}" Width="100"/>
<DataGridTextColumn Header="Price" Binding="{Binding Price}" Width="100"/>
<DataGridTextColumn Header="Total" Binding="{Binding Total}" Width="100"/>
<DataGridTemplateColumn Header="Actions">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<Button Content="Add Row" Command="{Binding AddItemCommand}" Width="100"/>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
NewDocumentViewModel.cs
namespace App.MVVM.ViewModel
{
internal class NewDocumentViewModel : ObservableObject
{
public ObservableCollection<Item> Items { get; set; } = new ObservableCollection<Item>();
public RelayCommand AddItemCommand { get; set; }
public NewDocumentViewModel()
{
Items = new ObservableCollection<Item>();
AddItemCommand = new RelayCommand(o => { AddNewItem(); });
}
private void AddNewItem()
{
Items.Add(new Item());
}
}
}
NewDocumentView.xaml.cs
namespace App.MVVM.View
{
public partial class NewDocumentView : UserControl
{
public NewDocumentView()
{
InitializeComponent();
DataContext = new NewDocumentViewModel();
}
}
}
Item.cs
namespace App.MVVM.Model
{
class Item : ObservableObject
{
private string _description;
public string Description
{
get { return _description; }
set
{
if (_description != value)
{
_description = value;
OnPropertyChanged(nameof(Description));
}
}
}
private string _unit;
public string Unit
{
get { return _unit; }
set
{
if (_unit != value)
{
_unit = value;
OnPropertyChanged(nameof(Unit));
}
}
}
private int _price;
public int Price
{
get { return _price; }
set
{
if (_price != value)
{
_price = value;
OnPropertyChanged(nameof(Price));
}
}
}
private int _total;
public int Total
{
get { return _total; }
set
{
if (_total != value)
{
_total = value;
OnPropertyChanged(nameof(Total));
}
}
}
}
}
ObservableObject.cs
namespace App.Core
{
internal class ObservableObject : INotifyPropertyChanged
{
public event PropertyChangedEventHandler? PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
}
RelayCommand.cs
namespace App.Core
{
internal class RelayCommand : ICommand
{
private readonly Action<object> execute;
private readonly Func<object, bool> canExecute;
public RelayCommand(Action<object> execute, Func<object, bool> canExecute = null)
{
this.execute = execute;
this.canExecute = canExecute;
}
public event EventHandler CanExecuteChanged
{
add => CommandManager.RequerySuggested += value;
remove => CommandManager.RequerySuggested -= value;
}
public bool CanExecute(object parameter)
{
return canExecute == null || canExecute(parameter);
}
public void Execute(object parameter)
{
execute(parameter);
}
}
}
我哪里做错了?如何使用按钮向 DataGrid 添加新行以立即更新 DataGrid?
当您将 ObservableCollection 绑定到数据网格的 Itemssource 时,该集合中的每个 Item 都会被模板化为一行。每行的数据上下文都是一个项目。
当您绑定时,它会在数据上下文中查找属性。您与 AddItemCommand 的绑定完全失败,因为 Item 没有 AddItemCommand 属性。
您可以使用relativesource在可视化树中搜索给定类型https://learn.microsoft.com/en-us/dotnet/desktop/wpf/advanced/relativesource-markupextension?view=netframeworkdesktop-4.8
如果您这样做是为了找到数据网格,那么它的数据上下文继承自NewDocumentView并且是NewDocumentViewModel。这当然是你的命令所在。
您需要明确告诉它使用数据上下文上的属性,因为否则它会查看 Datagrid UI 控件。
这是一个简化的示例,希望可以清楚地说明这一点。我在这里使用 mvvm 社区工具包,代码又快又脏,只是为了说明这一点。
主窗口
<Window.DataContext>
<local:MainWindowViewmodel/>
</Window.DataContext>
<Grid>
<DataGrid ItemsSource="{Binding Items}">
<DataGrid.Columns>
<DataGridTemplateColumn Header="Actions">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<Button Content="Add Row"
Command="{Binding DataContext.AddItemCommand, RelativeSource={RelativeSource AncestorType=DataGrid}}" Width="100"/>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
</Grid>
请注意,与relativesource 绑定,我正在使用 DataContext.AddItemCommand
查看模型:
public partial class MainWindowViewmodel : ObservableObject
{
[ObservableProperty]
private ObservableCollection<string> items = new ObservableCollection<string>{"A", "B" };
[RelayCommand]
private async Task AddItem()
{
Items.Add("X");
}
}
当我单击按钮时,会添加另一行。
如果您感兴趣,可能有一个想法,可以搜索并阅读有关该工具包的正确文章,但简单地说:
该工具包使用代码生成器在分部类中生成 Items 属性和 AddItemCommand。