Как предотвратить мерцание строк в сетке данных во время работы приложения

В приложении, которое я сейчас разрабатываю, я использую datagridview для отображения данных. Чтобы заполнить его, я должен нажать кнопку, и фоновый рабочий начнет работать, он заполнит таблицу данных, а когда закончит работу, он будет использовать таблицу данных в качестве источника данных для сетки данных. Это отлично работает, пользовательский интерфейс остается отзывчивым и так далее. Но теперь я применил окраску строк в зависимости от их значений (я все еще играю с этим, поэтому любые предложения приветствуются):

        private void ApplyColoring()
    {
        if (dataGridView1.DataSource != null)
        {
            foreach (DataGridViewRow dataGridRow in dataGridView1.Rows)
            {
                // hardmap a color to a column
                IDictionary<Int32, Color> colorDictionary = new Dictionary<Int32, Color>();
                colorDictionary.Add( 7, Color.FromArgb(194, 235, 211));
                colorDictionary.Add( 8, Color.Salmon);
                colorDictionary.Add( 9, Color.LightBlue);
                colorDictionary.Add(10, Color.LightYellow);
                colorDictionary.Add(11, Color.LightGreen);
                colorDictionary.Add(12, Color.LightCoral);
                colorDictionary.Add(13, Color.Blue);
                colorDictionary.Add(14, Color.Yellow);
                colorDictionary.Add(15, Color.Green);
                colorDictionary.Add(16, Color.White);

                foreach (DataGridViewRow gridRow in dataGridView1.Rows)
                {
                    foreach (DataGridViewCell cell in gridRow.Cells)
                    {
                        if (colorDictionary.Keys.Contains(cell.ColumnIndex))
                        {
                            // standard background 
                            cell.Style.BackColor = Color.FromArgb(194, 235, 211);
                        }
                    }
                }

                IList<String> checkedValues = new List<String>();


                // first we loop through all the rows
                foreach (DataGridViewRow gridRow in dataGridView1.Rows)
                {
                    IDictionary<String, Int32> checkedVal = new Dictionary<String, Int32>();

                    // then we loop through all the data columns
                    int maxCol = dnsList.Count + 7;
                    for (int columnLoop = 7; columnLoop < maxCol; columnLoop++)
                    {
                        string current = gridRow.Cells[columnLoop].Value.ToString();

                        for (int checkLoop = 7; checkLoop < maxCol; checkLoop++)
                        {
                            string check = gridRow.Cells[checkLoop].Value.ToString();

                            if (!current.Equals(check))
                            {
                                if (checkedVal.Keys.Contains(current))
                                {
                                    gridRow.Cells[columnLoop].Style.BackColor = colorDictionary[checkedVal[current]];
                                }
                                else
                                {
                                    gridRow.Cells[columnLoop].Style.BackColor = colorDictionary[columnLoop];
                                    checkedVal.Add(current, columnLoop);
                                }
                            }
                        }
                    }
                }
            }
        }
    }

Это доставляет мне проблемы. Не потому, что окраска не работает, она работает. Но потому что это делает его чертовски медленным. В первый раз он работает нормально, но когда я снова нажимаю кнопку, он работает чертовски медленно, и сетка данных мерцает. Я хочу, чтобы это выполнялось как постпроцесс, поэтому его (или, скорее, следует) запускать после завершения фоновой работы. Но когда я вызываю applycoloring из события RunWorkerCompleted, это просто медленно. Что я должен сделать, чтобы предотвратить это? Как я могу убедиться, что пользовательский интерфейс не мерцает при выполнении нового запроса (при этом НЕ ПОТЕРЯЯ текущие данные в сетке).


person Oxymoron    schedule 11.01.2010    source источник
comment
в дополнение к suspendlayout() также избегайте всех этих вложенных циклов (я не знаю, что это на самом деле делает, но кажется слишком запутанным, чтобы быть честным). Кроме того, все эти .ToString(), вероятно, снижают производительность.   -  person Pasi Savolainen    schedule 11.01.2010
comment
Вероятно, вы можете удалить первый цикл foreach, так как он, похоже, ничего не делает, а только значительно увеличивает количество циклов!!! foreach (DataGridViewRow dataGridRow в dataGridView1.Rows)   -  person Kurru    schedule 11.01.2010
comment
Вы совершенно правы, я удалил 2 цикла foreach. Стандартная раскраска не обязательно должна быть в отдельном цикле + самый верхний foreach совершенно бесполезен :) Я посмотрю замечание ToString() :)   -  person Oxymoron    schedule 14.01.2010
comment
Моя проблема похожа, но мой сценарий отличается. DataGridView не перестает мерцать, когда размер отображаемых данных точно такой же, как вертикальный размер DataGridview. Я решил это, установив DataGridView..AutoSizeRowsMode = DataGridViewAutoSizeRowsMode.None;   -  person Ronald Ramos    schedule 28.07.2019


Ответы (6)


Два предложения:

  1. Попробуйте вызвать SuspendLayout() до цикла и ResumeLayout() после цикла. Большинство других элементов управления вызывают это BeginUpdate() и EndUpdate() (Listviews, Combobox и т. д.).
  2. Используйте VirtualMode в DataGridView, если вы имеете дело с большим объемом данных.
person Martijn Laarman    schedule 11.01.2010

можно включить двойную буферизацию.

VB:

Imports System.Reflection

поместите следующее, например. в Form_Load

Dim systemType As Type = DataGridView1.GetType()
Dim propertyInfo As PropertyInfo = systemType.GetProperty("DoubleBuffered", BindingFlags.Instance Or BindingFlags.NonPublic)
propertyInfo.SetValue(DataGridView1, True, Nothing)

Для C#: перейдите по ссылке:Исправление медленной прокрутки DataGridView

Ваше здоровье

person Evolved    schedule 22.07.2011
comment
Я обнаружил, что если я использовал событие RowPostPaint, оно вызывало мигание каждые пару секунд, это решение исправило это. - person Wize; 15.05.2014

Я нашел другой способ сделать двойную буферизацию отражения для медленных сетей данных:

Создайте метод расширения с некоторым отражением, чтобы установить двойную буферизацию в сетке данных:

public static class DataGridViewExtensioncs
{

    public static void DoubleBuffered(this DataGridView dgv, bool setting)
    {
        var dgvType = dgv.GetType();
        var pi = dgvType.GetProperty("DoubleBuffered",
              BindingFlags.Instance | BindingFlags.NonPublic);
        pi.SetValue(dgv, setting, null);
    }
}

Затем в инициализаторе формы выполните:

this.dataGrid.DoubleBuffered(true);

Это очень похоже на ответ Evolved, и кредиты принадлежат Швете Лодхе: http://www.codeproject.com/Tips/390496/Reduction-flicker-blinking-in-DataGridView

person Wouter Simons    schedule 11.08.2014

Включите двойную буферизацию

Список пространств имен, необходимых для компиляции функции:

using System;
using System.Reflection;
using System.Windows.Forms;

public static class ExtensionMethods
{
   public static void DoubleBuffered(this DataGridView dgv, bool setting)
   {
      Type dgvType = dgv.GetType();
      PropertyInfo pi = dgvType.GetProperty("DoubleBuffered", BindingFlags.Instance | BindingFlags.NonPublic);
      pi.SetValue(dgv, setting, null);
   }
}

затем инициализируйте datagridview

dataGridView1.DoubleBuffered(true);
person Ramgy Borja    schedule 26.06.2017

Попробуйте вызвать SuspendLayout перед вашими обновлениями. Не забудьте вызвать ResumeLayout.

person Arthur    schedule 11.01.2010

Я бы настоятельно рекомендовал не зацикливаться на сетке (может помочь двойная буферизация, но она просто «скрывает» настоящую проблему), поскольку она производит много рендеринга.

Для таких целей я использую обработчик событий:

dataGridView1.CellValueChanged += new DataGridViewCellEventHandler(dataGridView1_CellValueChanged);

private void dataGridView1_CellPainting(object sender, DataGridViewCellPaintingEventArgs e)
    {
            if (dataGridView1.Columns[5].Index == e.ColumnIndex && e.RowIndex >= 0 && dataGridView1[5, e.RowIndex].Value.ToString() != "0")
            {
                e.CellStyle.BackColor = Color.PaleGreen;
            }
    }

Каждый раз, когда значения вашей ячейки меняются, ваша сетка автоматически обновляется и меняет цвет фона.

person Lennart    schedule 05.08.2016