C#自定义网格组件(DataGridView)实现数据分组、排序

2012-05-01 23:59:39|?次阅读|上传:wustguangh【已有?条评论】发表评论

关键词:C#, 界面设计|来源:唯设编程网

这是OutlookGrid类的核心方法,实现了数据行的填充,与设置数据列一样,首先调用数据行Rows属性的Clear方法将数据行数据清空,然后根据groupingStyle是否为null判断是否启用数据分组功能。如果不使用数据分组功能,则根据数据模型中的数据行元素逐行创建DataGridViewCell对象的实例,并调用Rows属性的Add方法,增加到数据行即可。如果groupingStyle不等于null,则对数据模型中的数据行进行分组,遇到新的分组数据就增加一行分组概述行。

此外,OutlookGrid类还覆盖了响应鼠标单击、双击以及编辑事件的函数和排序函数,以便响应排序、分组的激励事件,这些都比较简单,不在此赘述。

3.2. OutlookGridRow类的设计

OutlookGridRow类继承自DataGridViewRow,实现了数据行显示的相关功能,包括对分组行的特殊渲染方式,下面对其重要的成员方法进行介绍。

a. 判断鼠标事件是否在"+"、"-"图标区域

internal bool IsIconHit(DataGridViewCellMouseEventArgs e)
{
    if (e.ColumnIndex < 0) return false;

    OutlookGrid grid = (OutlookGrid)this.DataGridView;
    Rectangle rowBounds = grid.GetRowDisplayRectangle(this.Index, false);
    int x = e.X;

    DataGridViewColumn c = grid.Columns[e.ColumnIndex];
    if (this.isGroupRow &&
        (c.DisplayIndex == 0) &&
        (x > rowBounds.Left + 4) &&
        (x < rowBounds.Left + 16) &&
        (e.Y > rowBounds.Height - 18) &&
        (e.Y < rowBounds.Height - 7))
        return true;

    return false;


    //System.Diagnostics.Debug.WriteLine(e.ColumnIndex);
}

这个方法很简单,根据鼠标事件的DataGridViewCellMouseEventArgs类型参数判断鼠标事件对应的坐标是否在"+"、"-"图标的区域内部,并且返回对应的bool值。

b. 数据行的绘制函数

protected override void Paint(Graphics graphics,Rectangle clipBounds,
    Rectangle rowBounds, int rowIndex, DataGridViewElementStates rowState,
    bool isFirstDisplayedRow, bool isLastVisibleRow)
{
    if (this.isGroupRow)
    {

        OutlookGrid grid = (OutlookGrid)this.DataGridView;
        int rowHeadersWidth = grid.RowHeadersVisible ? grid.RowHeadersWidth : 0;

        // this can be optimized
        Brush brush = new SolidBrush(grid.DefaultCellStyle.BackColor);
        Brush brush2 = new SolidBrush(Color.FromKnownColor(KnownColor.GradientActiveCaption));

        int gridwidth = grid.Columns.GetColumnsWidth(DataGridViewElementStates.Displayed);
        Rectangle rowBounds2 = grid.GetRowDisplayRectangle(this.Index, true);

        // draw the background
        graphics.FillRectangle(brush,
            rowBounds.Left + rowHeadersWidth - grid.HorizontalScrollingOffset,
            rowBounds.Top, gridwidth, rowBounds.Height - 1);
        
        // draw text, using the current grid font
        graphics.DrawString(group.Text, grid.Font, Brushes.Black,
             rowHeadersWidth - grid.HorizontalScrollingOffset + 23, rowBounds.Bottom - 18);
        
        //draw bottom line
        graphics.FillRectangle(brush2,
             rowBounds.Left + rowHeadersWidth - grid.HorizontalScrollingOffset,
             rowBounds.Bottom - 2, gridwidth - 1, 2);
        
        // draw right vertical bar
        if (grid.CellBorderStyle == DataGridViewCellBorderStyle.SingleVertical 
            || grid.CellBorderStyle == DataGridViewCellBorderStyle.Single)
            graphics.FillRectangle(brush2, 
                rowBounds.Left + rowHeadersWidth - grid.HorizontalScrollingOffset + gridwidth - 1,
                 rowBounds.Top, 1, rowBounds.Height);

        if (group.Collapsed)
        {
            if (grid.ExpandIcon != null)
                graphics.DrawImage(grid.ExpandIcon,
                    rowBounds.Left + rowHeadersWidth - grid.HorizontalScrollingOffset + 4,
                    rowBounds.Bottom - 18, 11, 11);
        }
        else
        {
            if (grid.CollapseIcon != null)
                graphics.DrawImage(grid.CollapseIcon,
                     rowBounds.Left + rowHeadersWidth - grid.HorizontalScrollingOffset + 4, 
                     rowBounds.Bottom - 18, 11, 11);
        }
        brush.Dispose();
        brush2.Dispose();
    }
    base.Paint(graphics, clipBounds, rowBounds, rowIndex, rowState, isFirstDisplayedRow, isLastVisibleRow);

}

对OutlookGridRow类的行绘制函数进行重写,如果是分组行,则调用自定义的绘制逻辑,包括对"+"、"-"图标的绘制,否则直接调用父类的绘制函数即可。

c. 单元格的绘制

protected override void PaintCells(Graphics graphics, Rectangle clipBounds,
    Rectangle rowBounds, int rowIndex, DataGridViewElementStates rowState,
    bool isFirstDisplayedRow, bool isLastVisibleRow, DataGridViewPaintParts paintParts)
{
    if (!this.isGroupRow)
        base.PaintCells(graphics, clipBounds, rowBounds, rowIndex, rowState,
            isFirstDisplayedRow, isLastVisibleRow, paintParts);
}

如果不是分组行,则调用父类的PaintCells对单元格进行绘制,否则不需要绘制单元格。
3.3 DataSourceManager类的设计
DataSourceManager类主要用来对网格控件的数据模型进行管理,该类的一个核心方法是InitManager,该方法的完整定义如下:

private void InitManager()
{
    if (dataSource is IListSource)
        InitDataSet();
    if (dataSource is IList)
        InitList();
    if (dataSource is OutlookGrid)
        InitGrid();
}

该方法根据传入的dataSource类型调用不同的数据初始化方法初始化数据,下面分别对各方法进行介绍。

a. InitDataSet

发表评论0条 】
网友评论(共?条评论)..
C#自定义网格组件(DataGridView)实现数据分组、排序