Отображение суммарной Total строки в DataGridView

четверг, 16 июля 2009, Victor Usov

Анализ проблемы

Разрабатывая WinForms приложение, столкнулся с проблемой отображения суммарной строки (Totals) для цифровых колонок в стандартном DataGridView .NET 2.0. Как показывает анализ разных источников и собственноручный поиск, такой возможности в DataGridView нет. В такой ситуации существуют два выхода:

   1. Покупать компоненты, где уже реализована такая функциональность (DevExpress, ComponentOne, Xceed и т.д.);
   2. Расширять существующий DataGridView.

Первый вариант требует дополнительных финансовых затрат (более $800), на что заказчик скорее всего не согласится. Более того, если приложение уже написано, а необходимость в Totals появилась уже в процессе, возникает существенная проблема интеграции новых компонентов с минимальными изменениями по коду, что тоже не так просто. Поэтому приходится дописывать стандартный DataGridView.

Задача, на первый взгляд, кажется не очень сложной, тем более что кто-то мог ее уже успешно решить... Но, как не странно, поиск необходимой информации не дал ожидаемых результатов. Единственно, что предлагают народные умельцы, так это добавлять строку в конце и отображать там необходимую суммарную информацию. Но такое решение также вносит существенный недостаток, например, при условии большого количества строк, которые не поместятся в DataGridView, и придется отображать полосу прокрутки. Тогда Totals строка не будет видна, поскольку будет последней. Кроме того, положение Totals будет меняться при добавлении новых строк, что не совсем корректно.

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

И так, это должен быть компонент, унаследованный от System.Windows.Forms. DataGridView с необходимыми внешними настройками, и который, при обновлении источника данных (DataSource) или значений в ячейках, корректно отображал бы суммарную информацию независимо от количества строк в нужном формате.

Решение проблемы

Для расширения возможностей стандартного DataGridView предлагается следующий подход. Необходимо разработать элемент управления, который будет фактически находится за пределами DataGridView. Иначе говоря, добавляться в «childrens» родителя самого DataGridView. При этом он должен быть в виде строки, колонки которой будут связаны с соответственными колонками DataGridView.

Первое простое решение - это для каждой колонки, где необходимо отображать Total, добавлять в «parent» обычный System.Windows.Forms.Label, что первоначально и было реализовано. Но результаты тестирования показали, что такая реализация имеет существенные недостатки (строго говоря «баги»), в условиях необходимости изменять положение и ширину колонок, а также использовать горизонтальную и вертикальную полосы прокрутки. Исправить вновь появившиеся трудности полностью не удалось, при этом существенно усложнился код.
   По этому, предлагается программно добавить еще один DataGridView в событии ParentChanged, колонки которого будут полностью дублировать родительский, с учетом ширины, положения, формата отображения данных и т.д.

private void TotalDataGridView_ParentChanged(object sender, EventArgs e)
{
   if (Parent != null)
  {
       if (_showTotals && !Parent.Controls.Contains(_totalPanel))
       {
           Parent.Controls.Add(_totalPanel);

                  ......................................

           if (!_totalPanel.Controls.Contains(totalTitle))
                 _totalPanel.Controls.Add(totalTitle);

           if (!_totalPanel.Controls.Contains(_countLable))
                _totalPanel.Controls.Add(_countLable);

           if (!_totalPanel.Controls.Contains(_countValueLable))
               _totalPanel.Controls.Add(_countValueLable);

           if (!_totalPanel.Controls.Contains(_totalGrid))
             _totalPanel.Controls.Add(_totalGrid);
      }
       else if (!_showTotals)
      {
          Parent.Controls.Remove(_totalPanel);
      }
   }
}

Таким способом решается проблема горизонтальной прокрутки Total строки, поскольку в данном случае для TotalDataGridView необходимо задать свойство HorizontalScrollingOffset. Также все корректно работает при измении ширины и положения колонок.

 

   Далее необходимо отметить те колонки, для которых нужно отображать Total. Для этого создаем enum: 

   public enum ShowTotal
  {
      No = 0,
      Yes = 1
   }

   и задаем свойство Tag для нужных колонок, например 

        col2.Tag = ExtendedDataGridView.ShowTotal.Yes;

   В процессе добавление TotalDataGridView создается коллекция List _totals, которая содержит колоноки отмеченые для расчета Total, который осуществляется следующим образом:

   public void CalculateTotals()
  {
       double total = 0;
       foreach (DataGridViewColumn column in _totals)
      {
           foreach (DataGridViewRow row in this.Rows)
           {
                 if (!row.IsNewRow && !(row.Cells[column.Index].Value is DBNull))
                      total += Convert.ToDouble(row.Cells[column.Index].Value);
           }
            _totalGrid.Rows[0].Cells[column.Index].Value = total;
            total = 0;
      }
      _countValueLable.Text = Rows.Count.ToString();
  }

  Метод CalculateTotals() следует вызывать в ряде событий DataGridView: OnRowsAdded, OnCellValueChanged, OnDataSourceChanged и его «родителя»: OnLoad. 
  Для изменения параметров отображения в свойствах дизайнера создана секция «Extended», в корую входят ряд настроек: ShowTotals, ShowCount, TotalText, CountText.
 

 И так, разработаный TotalDataGridView имеет такой вид:


 

Выводы

   Решая проблему отображения суммарной строки, выбран наиболее оптимальный вариант, который заключается в расширении возможностей стандартного DataGridView .NET 2.0. Задача реализуется путем добавления дублируещего TotalDataGridView с одной строкой в Parent «родительского» DataGridView. Этим, в первую очередь, достигается корректрое отображение Total строки при измении scroll bars, положении и ширины колонок.


Ищите нас в интернетах!

Комментарии

Свежие вакансии