Winforms DataGridView в VirtualMode, когда вызывать AutoResizeColumn?

Я реализовал DataGridView в моей форме и успешно реализована VirtualMode, Это извлекает данные ячейки из локального кэша, и все кажется, что работает правильно при заполнении сетки / подкачки и т. Д. Я обрабатываю DataGridView.CellValueNeeded событие для заполнения клеток.

На DataGridView у меня есть AutoSizeColumnsMode свойство установлено в DataGridViewAutoSizeColumnsMode.DisplayedCells, Я заметил, что при использовании VirtualMode DataGridView не соответствует AutoSizeColumnsMode после заполнения ячеек. Я изучил эту статью, но не нашел решения.

В конечном итоге я хотел бы не полагаться на AutoSizeColumnsMode собственности, а лучше назвать .AutoResizeColumn() Метод где-то, чтобы изменить размер, поэтому я сначала автоматически изменяю размер столбца, но затем разрешаю пользователю изменять размер.

Я пробовал следующее с ограниченным или безуспешно:

  1. Задавать 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,

  2. Пробовал точно так же, кроме как в .CellFormatting обработчик события. Получил то же самое StackOverFlowException,

  3. Пробовал с и без 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;
    }
    

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

  4. Этот на самом деле несколько работает, по причине, которую я не понимаю:

    private void dataGridView_CellValueNeeded(object sender, DataGridViewCellValueEventArgs e)
    {
        // ... Get cell value from cache
        dataGridView.AutoSizeColumnsMode = DataGridViewAutoSizeColumnsMode.DisplayedCells;
    }
    

    Он корректно изменяет размер столбцов, но кажется странным, что его нужно повторно вызывать для каждого необходимого значения ячейки. Кроме того, я не могу сразу установить его на.None сразу после или он будет StackOverFlowException снова. Таким образом, я не могу позволить пользователю изменять размер столбцов.

  5. призвание .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;
        }
    }
}
Другие вопросы по тегам