Вычисляемые столбцы сетки данных

#c# #wpf #datagridview

#c# #wpf #datagridview

Вопрос:

Я пытаюсь перенести свое приложение Excel в WPF datagrid. Я собираюсь ввести данные в столбец A, а в столбце B я хотел бы произвести вычисление, взяв предыдущую ячейку и текущую ячейку столбца, и добавить предыдущую ячейку столбца B. пример вычисления: B2 = B1 (A2-A1). Каков наилучший подход к этому?

Комментарии:

1. Подождите. Только что поймал это. Под «предыдущей ячейкой» вы имеете в виду предыдущую строку ?

2. Да, ячейка предыдущей строки того же столбца. Текущая строка равна 2, поэтому в ячейке (B2) я хотел бы произвести вычисление = B1 (A2-A1)

Ответ №1:

Лично я бы начал с создания класса, который представляет записи, и реализовал INotifyPropertyChanged в этом классе.

 public class recordObject : INotifyPropertyChanged
{
    private int a;
    public int A 
    { 
        get
        {
            return a;
        }
        set
        {
            a = value;
            OnPropertyChanged("A");
        }
    }

    private int b;
    public int B
    { 
        get
        {
            return b;
        }
        set
        {
            b = value;
            OnPropertyChanged("B");
        }
    }

    public event PropertyChangedEventHandler PropertyChanged;
    protected void OnPropertyChanged(string propertyName)
    {
        if (PropertyChanged != null)
        {
            PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
        }
    }
}
  

Затем в вашем коде позади окна, в котором вы показываете datagrid, вы захотите подписаться на PropertyChanged для каждого объекта в списке. Тогда вам пришлось бы вручную вычислять значения столбцов всякий раз, когда эти свойства изменялись. Крик, я знаю, но это сработало бы.

Событие изменения свойства будет выглядеть следующим образом:

 void recordObject_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
    var objectList = DataGrid.ItemsSource as List<recordObject>;
    var myRecord = sender as recordObject;
    if (objectList != null amp;amp; myRecord != null)
    {
        int idx = objectList.IndexOf(myRecord);
        // Perform your calculations here using idx to access records before and after the current record
        // making sure to check for list boundaries for top and bottom.
        // Also note that this will likely kick off cascading event calls so make sure you're only changing
        // the previous or following record object.
    }
}
  

Если вы подключаете это событие ко всем записям в вашем связанном списке, то оно будет срабатывать при каждом изменении любого свойства. В приведенном выше классе это применимо как к A, так и B. Вы можете отфильтровать, какие свойства вам интересны для мониторинга, с помощью e.propertyName (простое сравнение строк) и соответствующим образом настроить бизнес-логику. Если вы хотите сохранить инкапсуляцию или, по крайней мере, поместить бизнес-логику для объекта в сам объект, этот метод может быть статическим для класса recordObject . Однако вам пришлось бы предусмотреть получение datagrid из этого статического метода (вероятно, через статическое свойство в вашем окне). Итак:

 public static void recordObject_PropertyChanged(object sender, PropertyChangedEventArgs e)
  

и подключенный таким образом:

 record.PropertyChanged  = new PropertyChangedEventHandler(recordObject.recordObject_PropertyChanged);
  

Комментарии:

1. Вау, это действительно подробный ответ, спасибо за это. Насколько я понял, ваш ответ сосредоточен на автоматическом обновлении столбца B, что приветствуется, но не на данном этапе. То, что я ищу прямо сейчас, — это бизнес-логика за сценой.

2. Не мог бы ты быть более конкретным, Джим? Все, что вы получили с помощью бизнес-логики в вашем вопросе, это B2 = B1 (A2-A1). Я думал, что ваш вопрос был более общим, поэтому я не уверен, к чему вы клоните.

3. я отмечу это как ответ, однако я должен изучить то, что вы предложили, и сделать много попыток и ошибок 🙂

4. В ответ на ваш комментарий меня действительно интересуют примечания, которые вы делаете в своем коде (например … // Выполните свои вычисления здесь, используя idx … скорее всего, запустится каскадное событие ….. и т.д.). В идеале я бы поместил эту логику в recordObject, но это кажется невозможным.

5. Не исключено. Просто создайте статическое свойство DataGrid в вашем окне и назначьте ему свою datagrid в Window_Loaded. Таким образом, вы можете ссылаться на свою коллекцию объектов как MyWindow. MyDataGrid. Источник элементов.

Ответ №2:

Лучше всего реализовать эту логику в классе и привязать сетку к соответствующим свойствам. Например:

 class SomeData 
{
  int A { get; set; }
  int B { get; set; }
  int AminusB { get { return A - B; } }
}
  

Комментарии:

1. Это точное решение не сработало бы для того, чего пытаются достичь. Каждый экземпляр SomeData должен был бы иметь ссылку на предыдущие SomeData, чтобы вычислять B каждый раз в зависимости от значений A и B в предыдущих SomeData.

2. Хороший улов, Адам. Я также пропустил это в своем ответе.

3. Спасибо, я внедрил ваше решение (Ник и Джейкоб), чтобы выполнить вычисление в той же строке без каких-либо проблем. Однако я все еще не могу понять, как получить ячейку предыдущей строки и добавить ее в ячейку текущей строки B2 = B1 (A2-A1)