首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >WPF:当子DataGrid列中的值和发生变化时,更新主DataGrid行中的单元格值

WPF:当子DataGrid列中的值和发生变化时,更新主DataGrid行中的单元格值
EN

Stack Overflow用户
提问于 2020-02-13 21:59:44
回答 1查看 380关注 0票数 0

我有一个主DataGrid (绑定到ObservableCollection<MainItem>),在RowDetailsTemplate中定义了另一个子DataGrid (绑定到ObservableCollection<SubItem>)。MainItem.Total属性只是返回SubItems.Sum(s => s.Amount)的派生值。这对显示网格非常有用。

但是,如果更改了SubItem.Amount列中的值并更新了MainItem.Total值,尽管MainItem.Total值是正确的(它只是一个派生值),主网格不会刷新以显示新值。如果强制使用MainDataGrid.Items.Refresh() (例如,在MainDataGrid_SelectedCellsChanged中),那么主网格就会在MainItem.Total中显示这个值。因此,这是可行的,但它闻起来很难闻,因为它是一种刷新主网格中所有可见行的蛮力方法。[注意:这种情况类似于文章Update single row in a WPF Datagrid,但我已经在使用ObservableCollection了。]

我认为这种行为是因为这种变化发生在ObservableCollection<SubItem>中,而不是在ObservableCollection<MainItem>中,所以它不知道吗?但是,我还没有找到一个可以通知主网格绑定MainItem.Total的内容已经更新的事件。

注意:下面的代码是一个独立的示例,以防有人想要构建并尝试它。这是基于相同的情况,在我的真实项目。

XAML:

代码语言:javascript
运行
复制
<Window x:Class="WpfAppDataGrid.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:WpfAppDataGrid"
        mc:Ignorable="d"
        Title="MainWindow" Height="450" Width="800">
    <Grid>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="*" />
            <ColumnDefinition Width="100" />
        </Grid.ColumnDefinitions>
        <DataGrid x:Name="MainDataGrid" Grid.Column="0"
                  ItemsSource="{Binding MainItems}"
                  RowDetailsVisibilityMode="VisibleWhenSelected"
                  SelectedCellsChanged="MainDataGrid_SelectedCellsChanged"
                  AutoGenerateColumns="False"
                  CanUserSortColumns="True"
                  CanUserAddRows="True"
                  CanUserDeleteRows="True"
                  RowBackground="AliceBlue"
                  HorizontalAlignment="Stretch"
                  VerticalAlignment="Stretch"
                  Margin="10,10,10,10">
            <DataGrid.RowDetailsTemplate>
                <DataTemplate>
                    <DataGrid x:Name="SubDataGrid" ItemsSource="{Binding SubItems}"
                              CellEditEnding="SubDataGrid_CellEditEnding"
                              AutoGenerateColumns="False"
                              CanUserAddRows="True"
                              CanUserDeleteRows="True"
                              RowBackground="Bisque">
                        <DataGrid.Columns>
                            <DataGridTextColumn Header="Candidate" Binding="{Binding Candidate}" />
                            <DataGridTextColumn Header="Primary" Binding="{Binding Primary}" />
                            <DataGridTextColumn Header="Amount" Binding="{Binding Amount, StringFormat=\{0:N2\}}" />
                            <DataGridTextColumn Header="Previous" Binding="{Binding Previous}" />
                            <DataGridTextColumn Header="Party" Binding="{Binding Party}" />
                        </DataGrid.Columns>
                    </DataGrid>
                </DataTemplate>
            </DataGrid.RowDetailsTemplate>
            <DataGrid.Columns>
                <DataGridTextColumn Header="Voter" Binding="{Binding Voter}" />
                <DataGridTemplateColumn Header="Date" Width="100" SortMemberPath="Date" >
                    <DataGridTemplateColumn.CellTemplate>
                        <DataTemplate>
                            <TextBlock Text="{Binding RecordedDate, StringFormat=\{0:MM/dd/yyyy\}}" />
                        </DataTemplate>
                    </DataGridTemplateColumn.CellTemplate>
                    <DataGridTemplateColumn.CellEditingTemplate>
                        <DataTemplate>
                            <DatePicker SelectedDate="{Binding RecordedDate}" />
                        </DataTemplate>
                    </DataGridTemplateColumn.CellEditingTemplate>
                </DataGridTemplateColumn>
                <DataGridTextColumn Header="Status" Binding="{Binding Status}" />
                <DataGridTextColumn Header="Auto" Binding="{Binding Total, StringFormat=\{0:N2\}}" IsReadOnly="True" >
                    <DataGridTextColumn.CellStyle>
                        <Style>
                            <Setter Property="TextBlock.TextAlignment" Value="Right" />
                        </Style>
                    </DataGridTextColumn.CellStyle>
                </DataGridTextColumn>
            </DataGrid.Columns>
        </DataGrid>
        <StackPanel Grid.Column="1" Margin="10,10,10,10">
            <Button Content="Refresh" Click="Button_Click" />
        </StackPanel>
    </Grid>
</Window>

查看代码-隐藏:

代码语言:javascript
运行
复制
using System.Linq;
using System.Windows;
using System.Windows.Controls;

namespace WpfAppDataGrid
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        enum SubDataGridColumn
        {
            Candidate,
            Primary,
            Amount,
            Previous,
            Party
        }

        private static MainViewModel vm = new MainViewModel();
        private static bool subAmountChanged = false;

        public MainWindow()
        {
            DataContext = vm;
            InitializeComponent();
        }

        private void SubVerifyEdit(SubItem sub, int column, string txt)
        {
            switch ((SubDataGridColumn)column)
            {
                case SubDataGridColumn.Candidate:
                    if (sub.Candidate != txt)
                        sub.Candidate = txt;
                    break;
                case SubDataGridColumn.Primary:
                    if (sub.Primary != txt)
                        sub.Primary = txt;
                    break;
                case SubDataGridColumn.Amount:
                    var amount = decimal.Parse(txt);
                    if (sub.Amount != amount)
                    {
                        sub.Amount = amount;
                        subAmountChanged = true;
                    }
                    break;
                case SubDataGridColumn.Party:
                    if (sub.Party != txt)
                        sub.Primary = txt;
                    break;
                case SubDataGridColumn.Previous:
                    if (sub.Previous != txt)
                        sub.Previous = txt;
                    break;
                default:
                    break;
            }
        }

        private void SubDataGrid_CellEditEnding(object sender, DataGridCellEditEndingEventArgs e)
        {
            var sub = (SubItem)e.Row.Item;
            var column = e.Column.DisplayIndex;
            var dep = (DependencyObject)e.EditingElement;
            if (dep is TextBox)
            {
                SubVerifyEdit(sub, column, ((TextBox)dep).Text);
            }
            else if (dep is ComboBox)
            {
                SubVerifyEdit(sub, column, ((ComboBox)dep).SelectedItem.ToString());
            }
        }

        private void Button_Click(object sender, RoutedEventArgs e)
        {
            MainDataGrid.Items.Refresh();
        }

        private void MainDataGrid_SelectedCellsChanged(object sender, SelectedCellsChangedEventArgs e)
        {
            if (subAmountChanged)
            {
                //var main = (MainItem)MainDataGrid.CurrentItem;  
                //var sum = main.SubItems.Sum(s => s.Amount);
                //var manual = main.ManualTotal;
                //var auto = main.AutoTotal;

                //var dep = (DependencyProperty)MainDataGrid.CurrentItem;
                //MainDataGrid.CoerceValue(dep);
                MainDataGrid.Items.Refresh();
                subAmountChanged = false;
            }
        }
    }
}

ViewModel:

代码语言:javascript
运行
复制
using System;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Linq;
using System.Runtime.CompilerServices;

namespace WpfAppDataGrid
{
    public class MainViewModel : INotifyPropertyChanged
    {

        public event PropertyChangedEventHandler PropertyChanged;

        public MainViewModel()
        {
            MainItems = new ObservableCollection<MainItem>();

            MainItems.Add(new MainItem("Thomas", DateTime.Now, "Unsure"));
            MainItems[0].SubItems.Add(new SubItem("Roberts", "NH", 27.46m, "Senator", "Republican"));

            MainItems.Add(new MainItem("Arthur", DateTime.Now, "No worries"));
            MainItems[1].SubItems.Add(new SubItem("Johnson", "IA", 47.5m, "Representative", "Republican"));
            MainItems[1].SubItems.Add(new SubItem("Butegieg", "CA", 76.42m, "Senator", "Democrat"));
            MainItems[1].SubItems.Add(new SubItem("Warren", "SC", 14.5m, "Governor", "Democrat"));

            MainItems.Add(new MainItem("Cathy", DateTime.Now, "What now"));
            MainItems[2].SubItems.Add(new SubItem("Biden", "WI", 1456.98m, "Mayor", "Democrat"));

            MainItems.Add(new MainItem("Jonathan", DateTime.Now, "Got this"));
            MainItems[3].SubItems.Add(new SubItem("Foobar", "MI", 5672.3m, "None", "Republican"));
            MainItems[3].SubItems.Add(new SubItem("Sanders", "ME", 1.45m, "Senator", "Democrat"));

            MainItems.Add(new MainItem("Roger", DateTime.Now, "Still undecided"));
            MainItems[4].SubItems.Add(new SubItem("Wakemeyer", "AK", 56m, "Police Chief", "Democrat"));
            MainItems[4].SubItems.Add(new SubItem("Trump", "FL", 982.34m, "Businessman", "Republican"));
        }

        private void NotifyPropertyChanged([CallerMemberName] string propertyName = "")
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
        }

        private ObservableCollection<MainItem> mainItems;
        public ObservableCollection<MainItem> MainItems
        {
            get { return mainItems; }
            set
            {
                mainItems = value;
                NotifyPropertyChanged();
            }
        }
    }
}

MainItem类:

代码语言:javascript
运行
复制
using System;
using System.Collections.ObjectModel;
using System.Linq;

namespace WpfAppDataGrid
{
    public class MainItem
    {
        public MainItem() { }

        public MainItem(string voter, DateTime date, string status)
        {
            Voter = voter;
            RecordedDate = date;
            Status = status;
        }

        public string Voter { get; set; }
        public DateTime RecordedDate { get; set; }
        public string Status { get; set; }
        public decimal Total { get { return SubItems.Sum(s => s.Amount); } }
        public ObservableCollection<SubItem> SubItems { get; set; } = new ObservableCollection<SubItem>();
    }
}

SubItem类:

代码语言:javascript
运行
复制
namespace WpfAppDataGrid
{
    public class SubItem
    {
        public SubItem() { }

        public SubItem(string candidate, string primary, decimal amount, string previous, string party)
        {
            Candidate = candidate;
            Primary = primary;
            Amount = amount;
            Previous = previous;
            Party = party;
        }

        public string Candidate { get; set; }
        public string Primary { get; set; }
        public decimal Amount { get; set; }
        public string Previous { get; set; }
        public string Party { get; set; }
    }
}
EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2020-02-14 13:36:42

MainItemSubItem都应该实现INotifyPropertyChanged

当更改PropertyChanged属性时,后者应引发Amount事件,而MainItem类则需要为SubItems中的所有SubItem对象订阅PropertyChanged事件,并在更改其中任何一个SubItem对象时为Total属性引发PropertyChanged事件:

代码语言:javascript
运行
复制
public class MainItem : INotifyPropertyChanged
{
    public MainItem()
    {
        SubItems.CollectionChanged += SubItems_CollectionChanged;
    }

    public MainItem(string voter, DateTime date, string status)
    {
        Voter = voter;
        RecordedDate = date;
        Status = status;
        SubItems.CollectionChanged += SubItems_CollectionChanged;
    }

    private void SubItems_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
    {
        if (e.NewItems != null)
        {
            foreach (object subItem in e.NewItems)
            {
                (subItem as INotifyPropertyChanged).PropertyChanged
                    += new PropertyChangedEventHandler(item_PropertyChanged);
                item_PropertyChanged(sub, new PropertyChangedEventArgs(nameof(Total)));
            }
        }

        if (e.OldItems != null)
        {
            foreach (object country in e.OldItems)
            {
                item_PropertyChanged(sub, new PropertyChangedEventArgs(nameof(Total)));
                (subItem as INotifyPropertyChanged).PropertyChanged
                    -= new PropertyChangedEventHandler(item_PropertyChanged);
            }
        }
    }

    private void item_PropertyChanged(object sender, PropertyChangedEventArgs e)
    {
        NotifyPropertyChanged(e.PropertyName);
    }

    public string Voter { get; set; }
    public DateTime RecordedDate { get; set; }
    public string Status { get; set; }
    public decimal Total { get { return SubItems.Sum(s => s.Amount); } }
    public ObservableCollection<SubItem> SubItems { get; set; } = new ObservableCollection<SubItem>();


    public event PropertyChangedEventHandler PropertyChanged;
    private void NotifyPropertyChanged([CallerMemberName] String propertyName = "")
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }
}
票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/60217101

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档