首页 > 解决方案 > 如何对 DataGridView 中添加的每个列求和?

问题描述

我创建了一个包含 SubPoDetailViewModel.cs 的SubPODetailView.xaml 而该模型又包含用于数据网格的SubInvoice.cs和用于其他详细信息的 SubPO.cs。我想要的是每次我添加一行时,总 TextBlock 中显示的“金额”列的总和。

运行程序查看页面截图

SubPODetailView.xaml

<DataGrid ItemsSource="{Binding Invoices}" 
            SelectedItem="{Binding SelectedInvoice,Mode=TwoWay}"
            AutoGenerateColumns="False" RowHeaderWidth="0" >
            
            <DataGrid.Columns>
                <DataGridTextColumn Header="Invoices" Width="*"
                Binding="{Binding InvoiceName,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}"/>

            <DataGridTemplateColumn Header="Date" Width="*">
                    <DataGridTemplateColumn.CellTemplate>
                        <DataTemplate>
                            <DatePicker SelectedDate="{Binding Date,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}" />
                        </DataTemplate>
                    </DataGridTemplateColumn.CellTemplate>
            </DataGridTemplateColumn>

            <DataGridTextColumn Header="Amount" Width="*" Binding="{Binding Amount,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}"/>

            <DataGridTemplateColumn Header="Status" Width="*">
                    <DataGridTemplateColumn.CellTemplate>
                        <DataTemplate>
                            <ComboBox ItemsSource="{Binding Status,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}" />
                        </DataTemplate>
                    </DataGridTemplateColumn.CellTemplate>
                </DataGridTemplateColumn>
            </DataGrid.Columns>
            
        </DataGrid>

                <TextBlock Text="{Binding Total}" Margin="10,0" Background="#FFC9CD7D" Foreground="Black"/>

SubPODetailViewModel.cs

public class SubPODetailViewModel : DetailViewModelBase, ISubPODetailViewModel
{
    private ISubPORepository _subPORepository;
    private IMessageDialogService _messageDialogService;
    private SubPOWrapper _subPO;
    private SubInvoiceWrapper _selectedInvoice;

    public SubPODetailViewModel(IEventAggregator eventAggregator,
        IMessageDialogService messageDialogService,
        ISubPORepository subPORepository) : base(eventAggregator)
    {
        _subPORepository = subPORepository;
        _messageDialogService = messageDialogService;

        AddInvoiceCommand = new DelegateCommand(OnAddInvoiceExecute);
        RemoveInvoiceCommand = new DelegateCommand(OnRemoveInvoiceExecute, OnRemoveInvoiceCanExecute);

        Invoices = new ObservableCollection<SubInvoiceWrapper>();

    }
// SubPOWrapper for SubPO.cs class
    public SubPOWrapper SubPO
    {
        get { return _subPO; }
        private set
        {
            _subPO = value;
            OnPropertyChanged();
        }
    }

// SubInvoiceWrapper for SubInvoice.cs class
    public SubInvoiceWrapper SelectedInvoice
    {
        get { return _selectedInvoice; }
        set
        {
            _selectedInvoice = value;
            OnPropertyChanged();
            ((DelegateCommand)RemoveInvoiceCommand).RaiseCanExecuteChanged();
        }
    }

    public ICommand AddInvoiceCommand { get; }
    public ICommand RemoveInvoiceCommand { get; }
    public ObservableCollection<SubInvoiceWrapper> Invoices { get; }

    public override async Task LoadAsync(int? subPOId)
    {
        var subPO = subPOId.HasValue
            ? await _subPORepository.GetByIdAsync(subPOId.Value)
            : CreateNewSubPO();
        InitializeSubPO(subPO);
        InitializeSubInvoice(subPO.Invoices);
    }

    private SubPO CreateNewSubPO()
    {
        var subPO = new SubPO();
        _subPORepository.Add(subPO);
        return subPO;
    }

    private void InitializeSubInvoice(ICollection<SubInvoice> invoices)
    {
        foreach (var wrapper in Invoices)
        {
            wrapper.PropertyChanged -= SubInvoiceWrapper_PropertyChanged;
        }
        Invoices.Clear();
        foreach (var subInvoice in invoices)
        {
            var wrapper = new SubInvoiceWrapper(subInvoice);
            Invoices.Add(wrapper);
            wrapper.PropertyChanged += SubInvoiceWrapper_PropertyChanged;
        }
    }

    private void SubInvoiceWrapper_PropertyChanged(object sender, PropertyChangedEventArgs e)
    {
        if (!HasChanges)
        {
            HasChanges = _subPORepository.HasChanges();
        }
        if (e.PropertyName == nameof(SubInvoiceWrapper.HasErrors))
        {
            ((DelegateCommand)SaveCommand).RaiseCanExecuteChanged();
        }
    }

    private void InitializeSubPO(SubPO subPO)
    {
        SubPO = new SubPOWrapper(subPO);
        SubPO.PropertyChanged += (s, e) =>
        {
            if (!HasChanges)
            {
                HasChanges = _subPORepository.HasChanges();
            }
            if (e.PropertyName == nameof(SubPO.HasErrors))
            {
                ((DelegateCommand)SaveCommand).RaiseCanExecuteChanged();
            }
        };
        ((DelegateCommand)SaveCommand).RaiseCanExecuteChanged();
        if (SubPO.Id == 0)
        {
            // Little trick to trigger the validation
            SubPO.Title = "";
        }
    }

    protected override void OnDeleteExecute()
    {
        var result = _messageDialogService.ShowOkCancelDialog($"Do you really want to delete the Subcontractor {SubPO.Title}?", "Question");
        if (result == MessageDialogResult.OK)
        {
            _subPORepository.Remove(SubPO.Model);
            _subPORepository.SaveAsync();
            RaiseDetailDeletedEvent(SubPO.Id);
        }
    }

    protected override bool OnSaveCanExecute()
    {
        return SubPO != null
           && !SubPO.HasErrors
           && Invoices.All(i=>!i.HasErrors)
           && HasChanges;
    }

    protected override async void OnSaveExecute()
    {
        await _subPORepository.SaveAsync();
        HasChanges = _subPORepository.HasChanges();
        RaiseDetailSavedEvent(SubPO.Id, SubPO.Title);
    }

    private void OnAddInvoiceExecute()
    {
        var newInvoice = new SubInvoiceWrapper(new SubInvoice());
        newInvoice.PropertyChanged += SubInvoiceWrapper_PropertyChanged;
        Invoices.Add(newInvoice);
        SubPO.Model.Invoices.Add(newInvoice.Model);
        //newInvoice.InvoiceName = "";
        newInvoice.Date = DateTime.Now.Date;
        // Trigger validation :-) 
    }

    private void OnRemoveInvoiceExecute()
    {
        SelectedInvoice.PropertyChanged -= SubInvoiceWrapper_PropertyChanged;
        _subPORepository.RemoveInvoice(SelectedInvoice.Model);
        Invoices.Remove(SelectedInvoice);
        SelectedInvoice = null;
        HasChanges = _subPORepository.HasChanges();
        ((DelegateCommand)SaveCommand).RaiseCanExecuteChanged();
    }

    private bool OnRemoveInvoiceCanExecute()
    {
        return SelectedInvoice !=null;
    }
}
}

注意:要检查整个程序为 zip 文件,这里你去(只需点击慢速下载然后继续):http ://www.filefactory.com/file/681du53ow7us/SubBVZip.zip

更新

运行程序时遇到的问题的屏幕截图,如何在不添加另一个新行的情况下对行求和。

标签: c#wpfmvvm

解决方案


SubPODetailView 的最后一个 TextBlock 已经绑定到视图模型中的 Total 属性。在视图模型上创建属性 Total 并将 CollectionChanged 处理程序添加到 Invoices:

public SubPODetailViewModel(IEventAggregator eventAggregator,
    IMessageDialogService messageDialogService,
    ISubPORepository subPORepository) : base(eventAggregator)
   {
        ...
        Invoices.CollectionChanged += Invoices_CollectionChanged;
    }

    private void Invoices_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
    {
        Total = Invoices.Sum(x => x.Amount);
    }

    private decimal _total;
    public decimal Total
    {
        get => _total;
        set {
            _total = value;
            OnPropertyChanged();
        }
    }

推荐阅读