Winforms DataGridView в VirtualMode, когда вызывать AutoResizeColumn?
Я реализовал DataGridView
в моей форме и успешно реализована VirtualMode
, Это извлекает данные ячейки из локального кэша, и все кажется, что работает правильно при заполнении сетки / подкачки и т. Д. Я обрабатываю DataGridView.CellValueNeeded
событие для заполнения клеток.
На DataGridView у меня есть AutoSizeColumnsMode
свойство установлено в DataGridViewAutoSizeColumnsMode.DisplayedCells
, Я заметил, что при использовании VirtualMode DataGridView не соответствует AutoSizeColumnsMode после заполнения ячеек. Я изучил эту статью, но не нашел решения.
В конечном итоге я хотел бы не полагаться на AutoSizeColumnsMode
собственности, а лучше назвать .AutoResizeColumn()
Метод где-то, чтобы изменить размер, поэтому я сначала автоматически изменяю размер столбца, но затем разрешаю пользователю изменять размер.
Я пробовал следующее с ограниченным или безуспешно:
Задавать
DataGridView.AutoSizeColumnsMode
в.None
, Тогда по моему.CellValueNeeded
обработчикprivate void dataGridView_CellValueNeeded(object sender, DataGridViewCellValueEventArgs e) { // ... Get cell value from cache dataGridView.AutoResizeColumn(e.ColumnIndex, DataGridViewAutoSizeColumnMode.DisplayedCells); }
Это кидает
StackOverFlowException
предположительно потому, что он многократно повышает.CellValueNeeded
,Пробовал точно так же, кроме как в
.CellFormatting
обработчик события. Получил то же самоеStackOverFlowException
,Пробовал с и без
DataGridView.SuspendLayout/ResumeLayout
:private void dataGridView_CellValueNeeded(object sender, DataGridViewCellValueEventArgs e) { // ... Get cell value from cache dataGridView.CellValueNeeded -= dataGridView_CellValueNeeded; dataGridView.AutoResizeColumn(e.ColumnIndex, DataGridViewAutoSizeColumnMode.DisplayedCells); dataGridView.CellValueNeeded += dataGridView_CellValueNeeded; }
Это дает все пустые ячейки, поэтому бесполезно.
Этот на самом деле несколько работает, по причине, которую я не понимаю:
private void dataGridView_CellValueNeeded(object sender, DataGridViewCellValueEventArgs e) { // ... Get cell value from cache dataGridView.AutoSizeColumnsMode = DataGridViewAutoSizeColumnsMode.DisplayedCells; }
Он корректно изменяет размер столбцов, но кажется странным, что его нужно повторно вызывать для каждого необходимого значения ячейки. Кроме того, я не могу сразу установить его на.None сразу после или он будет
StackOverFlowException
снова. Таким образом, я не могу позволить пользователю изменять размер столбцов.призвание
.UpdateCellValue()
как упоминалось в статье из моего.CellValueNeeded
обработчик бросковStackOverFlowException
также.
Так можно ли позвонить .AutoResizeColumn()
где-то, где это не будет расти .CellValueNeeded
пока не переполнится? Поскольку #4, кажется, обладает способностью выполнять функцию автоматического изменения размера, кажется, что я мог бы также вызвать ее вручную откуда-то.
2 ответа
Я думаю, что это может быть решением, хотя мне все еще интересно услышать, что говорят другие.
Я продолжал смотреть на некоторые другие события, поднятые DataGridView
и нашел .RowPostPaint
событие. Я создал следующий обработчик:
private void dataGridView_RowPostPaint(object sender, DataGridViewRowPostPaintEventArgs e)
{
if (dataGridView.AllowUserToResizeColumns) //So not to run unnecessarily
{
return;
}
var lastIndex = dataGridView.Rows.GetLastRow(DataGridViewElementStates.Displayed);
if (e.RowIndex == lastIndex) //Only do on the last displayed row
{
dataGridView.AutoResizeColumns(DataGridViewAutoSizeColumnsMode.DisplayedCells);
dataGridView.AllowUserToResizeColumns = true; // User has control from here on
}
}
Это выполняет автоматическое изменение размера столбцов при начальной загрузке данных, а затем позволяет пользователю изменять размер оттуда. Он делает это только один раз, так что лучше, чем для каждого необходимого значения ячейки. Я должен установить dataGridView.AllowUserToResizeColumns = false
до начальной загрузки данных.
Это, кажется, отвечает всем требованиям. Пользователь увидит хорошо подходящие столбцы при первоначальной загрузке и может настроить оттуда, и большая часть моих данных имеет сопоставимую длину от строки к строке, поэтому в большинстве случаев не следует усекать или тратить на это место.
Поскольку вы заявляете, что вам интересно то, что говорят другие, я собрал несколько иной подход к использованию автоматического изменения размера столбца с виртуальной сеткой данных.
Первоначальный вызов AutoResizeColumns размещается после события Shown, поэтому форма и дочерние компоненты инициализируются и отображаются. Кроме того, путем передачи события DataGridView Scroll для изменения размера, а не RowPostPaint, это должно быть несколько более эффективным, поскольку это событие запускается реже, и я думаю, что оно хорошо согласуется с ссылкой на MSDN, которую вы цитируете:
using System.Collections.Generic;
using System.Windows.Forms;
namespace DataGridViewTest
{
public partial class DataGridViewForm : Form
{
private List<string> dataSource;
public DataGridViewForm()
{
InitializeComponent();
// Enable VirtualMode for dataGridView1
dataGridView1.VirtualMode = true;
// Wire CellValueNeeded event handler
dataGridView1.CellValueNeeded += DataGridView1_CellValueNeeded;
// Wire Scroll event handler
dataGridView1.Scroll += DataGridView1_Scroll;
// Wire form Shown event handler
this.Shown += DataGridViewForm_Shown;
}
private void DataGridViewForm_Shown(object sender, System.EventArgs e)
{
// Populate dataGridView1 here to avoid perception of a long form startup time
populateDataGridView();
// Resize columns after the form is initialized and displayed on screen,
// otherwise calling this method won't actually have an effect on column sizes
dataGridView1.AutoResizeColumns(DataGridViewAutoSizeColumnsMode.DisplayedCells);
}
private void DataGridView1_CellValueNeeded(object sender, DataGridViewCellValueEventArgs e)
{
// Set the triggering cell's value to the corresponding value from dataSource
e.Value = dataSource[e.RowIndex];
}
private void DataGridView1_Scroll(object sender, ScrollEventArgs e)
{
// Resize columns again, but only if a vertical scroll just happened
if (e.ScrollOrientation == ScrollOrientation.VerticalScroll)
{
dataGridView1.AutoResizeColumns(DataGridViewAutoSizeColumnsMode.DisplayedCells);
}
}
private void populateDataGridView()
{
fetchIntoDataSource();
associateDataSourceToDataGridView();
}
private void fetchIntoDataSource()
{
dataSource = new List<string>();
// Insert a test string into dataSource many times
for (int i = 0; i < 1000; i++)
{
dataSource.Add("test string");
}
}
private void associateDataSourceToDataGridView()
{
// Synchronize dataGridView1.RowCount to dataSource.Count
// This is necessary for the CellValueNeeded event to fire
dataGridView1.RowCount = dataSource.Count;
}
}
}