如何向DataGrid添加一行以立即显示?

问题描述 投票:0回答:1

单击 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?

c# wpf mvvm datagrid
1个回答
0
投票

当您将 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。

© www.soinside.com 2019 - 2024. All rights reserved.