Pull to refresh

Событие изменения ячейки DataGrid WPF C#

Проблема

Недавно столкнулся с проблемой отлова события изменения ячейки в DataGrid. Недолго поискав в интернете, нашёл событие CellEditEnding, казалось, всё супер, но нет, возникло несколько проблем.

Первая проблема это то, что событие возникает после завершения редактирования ячейки, НО до изменения самого содержимого DataGrid. То есть, если при вызове этого события мы хотим проводить какие-либо действия с DataGrid, то у нас это не получится. При вызове события, DataGrid занята основным потоком, она ожидает завершения редактирования данных, которое произойдет после вызова события.

Вторая проблема, если при вызове события мы попробуем получить данные ячейки, то разочаруемся, так как будем получать старые, не измененные данные.

Решение

Для примера, данные в DataGrid будут описываться класом Person с полями Id, Name, Age

public class Person
    {
        public int Id { get; set; }
        public string Name { get; set; }
        public int Age { get; set; }
    }

Для решения этой проблемы начнем с разметки XAML
Мы должны каждый столбик определить вручную и у каждого столбца написать свойство UpdateSourceTrigger = "PropertyChanged"

		<Grid>
        <DataGrid x:Name="dataGrid" AutoGenerateColumns="False" CellEditEnding="dataGrid_CellEditEnding" >
            <DataGrid.Columns>
                <DataGridTextColumn Binding="{Binding Id, UpdateSourceTrigger=PropertyChanged}" Header="id"/>
                <DataGridTextColumn Binding="{Binding Name, UpdateSourceTrigger=PropertyChanged}" Header="name"/>
                <DataGridTextColumn Binding="{Binding Age, UpdateSourceTrigger=PropertyChanged}" Header="age"/>
            </DataGrid.Columns>
        </DataGrid>
    </Grid>

После этих действий в событии CellEditEnding можно получить измененные данные после редактирования ячейки

private void dataGrid_CellEditEnding(object sender, DataGridCellEditEndingEventArgs e)
        {
            Person p = e.Row.Item as Person;
            MessageBox.Show($"{p.Id} {p.Name} {p.Age}");
        }

Полный код на данный момент

p
ublic partial class MainWindow : Window
    {
        private List<Person> listPersons;
        public MainWindow()
        {
            InitializeComponent();
            listPersons = new List<Person>()
            {
                new Person() {Id = 0, Name = "Person_1", Age = 32},
                new Person() {Id = 1, Name = "Person_2", Age = 35},
                new Person() {Id = 2, Name = "Person_3", Age = 62}
            };
            dataGrid.ItemsSource = listPersons;
        }

        private bool flagfix = true;
        private void dataGrid_CellEditEnding(object sender, DataGridCellEditEndingEventArgs e)
        {
            Person p = e.Row.Item as Person;
            MessageBox.Show($"{p.Id} {p.Name} {p.Age}");            
        }
    }

    public class Person
    {
        public int Id { get; set; }
        public string Name { get; set; }
        public int Age { get; set; }
    }

Дополнительно

А теперь, я хочу показать, что делать, если мы хотим изменять какие-либо данные в DataGrid, когда меняем какую-либо ячейку. Для примера, мы хотим менять id на любой другой, когда меняем какие-либо данные.

Полный код программы

		<Grid>
        <DataGrid x:Name="dataGrid" AutoGenerateColumns="False" CellEditEnding="dataGrid_CellEditEnding" >
            <DataGrid.Columns>
                <DataGridTextColumn Binding="{Binding Id, UpdateSourceTrigger=PropertyChanged}" Header="id"/>
                <DataGridTextColumn Binding="{Binding Name, UpdateSourceTrigger=PropertyChanged}" Header="name"/>
                <DataGridTextColumn Binding="{Binding Age, UpdateSourceTrigger=PropertyChanged}" Header="age"/>
            </DataGrid.Columns>
        </DataGrid>
    </Grid>
public partial class MainWindow : Window
    {
        private List<Person> listPersons;
        public MainWindow()
        {
            InitializeComponent();
            listPersons = new List<Person>()
            {
                new Person() {Id = 0, Name = "Person_1", Age = 32},
                new Person() {Id = 1, Name = "Person_2", Age = 35},
                new Person() {Id = 2, Name = "Person_3", Age = 62}
            };
            dataGrid.ItemsSource = listPersons;
        }

        private bool flagfix = true;
        private void dataGrid_CellEditEnding(object sender, DataGridCellEditEndingEventArgs e)
        {
            Person p = e.Row.Item as Person;
            //MessageBox.Show($"{p.Id} {p.Name} {p.Age}");
            if (flagfix)
            {
                int numRow = e.Row.GetIndex();
                Random rnd = new Random();
                p.Id = rnd.Next(100, 999);
                listPersons.RemoveAt(numRow);
                listPersons.Insert(numRow, p);
                flagfix = false;
                dataGrid.CancelEdit();
                dataGrid.CancelEdit();
                flagfix = true;
                dataGrid.Items.Refresh();
            }
        }
    }

    public class Person
    {
        public int Id { get; set; }
        public string Name { get; set; }
        public int Age { get; set; }
    }

Объяснение

Возникает вопрос, для чего такие странные манипуляции в обработке события? А вся проблема в том, что хоть событие и вызвано, но изменения то не внесены. Программа ожидает окончания обработки события, чтобы завершить изменение ячейки. Поэтому мы пишем команду "dataGrid.CancelEdit();", чтобы прервать редактирование ячейки. Так как если мы не сделаем это, то мы с не сможем проводить какие-либо манипуляции с DataGrid в обработчике события. А флаг нужен для того, чтобы не уйти в бесконечный цикл, ведь когда выполняется команда "dataGrid.CancelEdit();", то событие вызывается повторно. Команда "dataGrid.CancelEdit();" пишется два раза, если написать один раз, то в некоторых случаях редактирование не будет прервано.

Tags:
Hubs:
You can’t comment this publication because its author is not yet a full member of the community. You will be able to contact the author only after he or she has been invited by someone in the community. Until then, author’s username will be hidden by an alias.