Недавно встала передо мной задача - сделать автоподбор по ширине для столбцов в таблице System.Windows.Forms.DataGridView. Оказалось, что в том случае, когда столбцов в таблице около двадцати, а рядов - около двадцати тысяч, решение задачи неочевидно.
Тупо попробовав установить свойство AutoSizeColumnsMode = true, я обнаружил, что после прокрутки таблицы повторного автоподбора не происходит. Пришлось реализовать это программно, реагируя на события VerticalScrollBar.ValueChanged и VerticalScrollBar.MouseCaptureChanged. Вскоре выяснилось, что при нажатии, скажем, кнопки "вниз" прокрутка идёт медленно, а полоса прокрутки, да и всё таблица сильно дёргается. Пробовал и так и эдак, пока не понял очевидную вещь - если события прокрутки происходят слишком часто - реагировать на каждое из них необязательно. Реализовать это довольно просто.
using System; using System.Windows.Forms; using System.ComponentModel; namespace CoolProject { public class AutoSizeDataGridView : DataGridView { // Максимальная частота автоподбора ширины, мс. const int AutoSizeInterval = 200; bool _autoSizeDisplayedColumnOnScroll, scrollInterlock; int cacheVerticalScrollValue; Timer timer; // Запуск отложенной процедуры автоподбора ширины. void OnScroll() { if (AutoSizeDisplayedColumnOnScroll && !VerticalScrollBar.Capture) if (cacheVerticalScrollValue != VerticalScrollBar.Value) { cacheVerticalScrollValue = VerticalScrollBar.Value; if (timer.Enabled) timer.Stop(); timer.Start(); } } // Делать ли автоподбор ширины после прокрутки. [DefaultValue(false)] public bool AutoSizeDisplayedColumnOnScroll { get { return _autoSizeDisplayedColumnOnScroll; } set { _autoSizeDisplayedColumnOnScroll = value; if (value && timer == null) CreateScrollTimer(); } } // Настроить таблицу на автоподбор ширины после прокрутки - // создать таймер и подписаться на события. void CreateScrollTimer() { VerticalScrollBar.MouseCaptureChanged += (sender, e) => OnScroll(); VerticalScrollBar.ValueChanged += (sender, e) => OnScroll(); cacheVerticalScrollValue = VerticalScrollBar.Value; timer = new Timer() { Interval = AutoSizeInterval, Enabled = false }; timer.Tick += (sender, e) => { AutoSizeColumns(); timer.Stop(); }; } // Собственно автоподбор ширины. // Код тривиален, привожу для полноты ощущений. private void AutoSizeColumns() { if (!scrollInterlock) using (var g = CreateGraphics()) try { int firstCol, lastRow; scrollInterlock = true; firstCol = FirstDisplayedScrollingColumnIndex; lastRow = Math.Min(FirstDisplayedScrollingRowIndex + DisplayedRowCount(true) + 1, Rows.Count); for (int j = 0; j < Columns.Count; j++) if (Columns[j].ValueType != typeof(bool)) { int width = DataGridViewCell.MeasureTextWidth(g, Columns[j].HeaderText, Font, ColumnHeadersHeight, TextFormatFlags.Default); for (int i = FirstDisplayedScrollingRowIndex; i < lastRow; i++) width = Math.Max(DataGridViewCell. MeasureTextWidth(g, this[j, i].FormattedValue.ToString(), Font, Rows[i].Height, TextFormatFlags.Default), width); Columns[j].Width = width + 5; } FirstDisplayedScrollingColumnIndex = firstCol; } catch { } finally { scrollInterlock = false; } } } }
Вообще, применение метода гораздо шире. Он годится для любых часто повторяющихся событий, на которые нужно реагировать с ограниченной частотой. Как я понимаю, метод не нов. Кому не лень, может найти тому много примеров. Просто решил написать о том, что лично мне показалось неочевидным.
спасибо за инфу
ОтветитьУдалитьПолезно. Но я юзаю xtraGrid, там уже есть такая фича.
ОтветитьУдалитьDevExpress - вообще крутые контролы. Там даже есть почти полный аналог Excel для генерации отчётов. Хотя я всё же предпочитаю Excel.
ОтветитьУдалитьСпасиб инфа нужняковая!
ОтветитьУдалитьПотдерживаю! Нужный пост!
ОтветитьУдалитьблин, сделай пошире блок с текстом. код скукоживается ппц.
ОтветитьУдалитьПрочитал,с моими малыми знаниями мой мозг отправило на орбиту луны
ОтветитьУдалить