Вопрос геймерам. Подбираете ли вы IRL монетки, валяющиеся под ногами?

Вопрос геймерам. Подбираете ли вы IRL монетки, валяющиеся под ногами?

среда, 30 марта 2011 г.

Про автоподбор ширины и не только

Недавно встала передо мной задача - сделать автоподбор по ширине для столбцов в таблице 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;
    }
}
}
}

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

7 комментариев:

  1. Полезно. Но я юзаю xtraGrid, там уже есть такая фича.

    ОтветитьУдалить
  2. DevExpress - вообще крутые контролы. Там даже есть почти полный аналог Excel для генерации отчётов. Хотя я всё же предпочитаю Excel.

    ОтветитьУдалить
  3. блин, сделай пошире блок с текстом. код скукоживается ппц.

    ОтветитьУдалить
  4. Прочитал,с моими малыми знаниями мой мозг отправило на орбиту луны

    ОтветитьУдалить