Как предотвратить мерцание строк в сетке данных во время работы приложения
В приложении, которое я сейчас разрабатываю, я использую сетку данных для отображения данных. Чтобы заполнить его, я должен нажать кнопку, и фоновый рабочий начнет работать, он заполнит таблицу данных, и когда он закончится, он будет использовать эту таблицу в качестве источника данных для сетки данных. Это отлично работает, пользовательский интерфейс остается отзывчивым и так далее. Но теперь я применил раскраску к строкам в зависимости от их значений (я все еще играю с этим, поэтому любые предложения приветствуются):
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, это просто медленно. Что я должен сделать, чтобы предотвратить это? Как я могу убедиться, что пользовательский интерфейс не мерцает при выполнении нового запроса (не теряя текущие данные в сетке).
6 ответов
Два предложения:
- Попробуйте вызвать SuspendLayout() до цикла и ResumeLayout() после цикла. Большинство других элементов управления вызывают это BeginUpdate() и EndUpdate() (Listviews, Combobox и т. Д.).
- Используйте VirtualMode для DataGridView, если вы имеете дело с большим объемом данных.
Вы можете включить двойную буферизацию.
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
ура
Я нашел другой способ сделать двойную буферизацию отражения для медленных данных:
Создайте метод расширения с некоторым отражением для установки двойной буферизации на сетке данных:
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/Reducing-flicker-blinking-in-DataGridView
Включить двойную буферизацию
Список пространств имен, необходимых для компиляции функции:
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);
Попробуйте позвонить в SuspendLayout перед вашими обновлениями. Не забудьте позвонить в ResumeLayout.
Я настоятельно рекомендую не зацикливаться на сетке (двойная буферизация может помочь, но она просто "скрывает" реальную проблему), так как она производит много рендеринга.
Для подобных целей я использую обработчик событий:
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;
}
}
Каждый раз, когда значения ваших ячеек меняются, ваша сетка автоматически обновляется и меняет задний цвет