Winforms DataGridView в VirtualMode, когда вызывать AutoResizeColumn? - PullRequest
2 голосов
/ 04 марта 2012

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

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

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

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

  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 ]

2 голосов
/ 05 марта 2012

Я думаю, что это может быть решением, хотя мне все еще интересно услышать, что говорят другие.

Я продолжал смотреть на некоторые другие события, вызванные 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 до начальной загрузки данных.

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

0 голосов
/ 26 марта 2017

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

Первоначальный вызов 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;
        }
    }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...