博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

小菜之智能客户端(6)演化TaskVision,自定义DataGrid控件

Posted on 2008-09-24 15:18  a-peng  阅读(2527)  评论(3编辑  收藏  举报
(一)、使用默认DataGridView
在上一篇中,小菜使用DataGridView控件来显示任务数据Tasks。用到的代码如下:
dgTasks.DataSource = _dataLayer.Tasks.DefaultView;
dgTasks为DataGridView控件。
效果如下图:
 image
右边的dgTasks控件将强类型DataSet:DataSetTasks中的所有列都显示出来。因为这是默认情况。

(二)、使用默认DataGrid
vs2005的工具箱中只有DataGridView没有DataGrid,是不是就说DataGridView替代了DataGrid了。
那小菜在工具箱中找到DataGrid,那是不是说DataGrid替代DataGridView。

DataGridView可以替代DataGrid,一定是在哪些方面增强了。那是哪些地方呢?
如果你连DataGrid都没用过,也在相掺和说DataGridView替代DataGrid,那真该反思了。

上面的四句话,就是小菜从DataGrid说起的原因。

在vs2005中可以右击工具箱,选择项:
image
将这一项勾选,确定后在工具箱就会看到DataGrid控件了,和自定义控件的添加方式类似。

那我们就用DataGrid替换DataGridView来看看效果,如下图:
image
代码:
dgTasks.DataSource = _dataLayer.Tasks.DefaultView;
dgTasks为DataGrid控件。

效果和使用DataGridView差不多,不过DataGrid多了篮色的头部。
同样,右边的dgTasks控件也是将强类型DataSet:DataSetTasks中的所有列都显示出来。因为这是默认情况。

(三)、使用自定义DataGrid
上面的视觉效果一定让你很不满意吧。

头顶篮色鸡冠小菜可不想要,把所有列都显示出来小菜可不想要,列名直接是数据库中的字段小菜也不想要,添加删除修改也不是小菜想要的。

这些需求很常见,那怎么办呢?

(1)修改列标题
效果如下:
image
哇!看起来不错噢。

小菜先给出代码,然后再分析:
创建一个类MainFormDgStyle:
using System;
using System.Windows.Forms;

namespace TaskVision
{
    
public class MainFormDgStyle
    
{
        
public const string TableName = "Tasks";

        
public static DataGridTableStyle GetStyle()
        
{
            
// creates the table style that's used for the datagrid
            DataGridTableStyle dgTableStyle = new DataGridTableStyle();

            DataGridTextBoxColumn taskID 
= new DataGridTextBoxColumn();
            taskID.HeaderText 
= "ID";
            taskID.MappingName 
= "TaskID";
            taskID.Width 
= 30;

            DataGridTextBoxColumn priorityText 
= new DataGridTextBoxColumn();
            priorityText.HeaderText 
= "Priority";
            priorityText.MappingName 
= "PriorityText";
            priorityText.Width 
= 75;

            DataGridTextBoxColumn assignedToText 
= new DataGridTextBoxColumn();
            assignedToText.HeaderText 
= "Assigned To";
            assignedToText.MappingName 
= "AssignedToText";
            assignedToText.Width 
= 75;

            DataGridTextBoxColumn taskSummary 
= new DataGridTextBoxColumn();
            taskSummary.HeaderText 
= "Summary";
            taskSummary.MappingName 
= "TaskSummary";
            taskSummary.Width 
= 128;

            DataGridTextBoxColumn statusText 
= new DataGridTextBoxColumn();
            statusText.HeaderText 
= "Status";
            statusText.MappingName 
= "StatusText";
            statusText.Width 
= 55;

            DataGridTextBoxColumn progressText 
= new DataGridTextBoxColumn();
            progressText.HeaderText 
= "Progress";
            progressText.MappingName 
= "Progress";
            progressText.Width 
= 55;

            DataGridTextBoxColumn dateDue 
= new DataGridTextBoxColumn();
            dateDue.HeaderText 
= "Due Date";
            dateDue.MappingName 
= "DateDue";
            dateDue.Width 
= 65;

            DataGridTextBoxColumn dateCreated 
= new DataGridTextBoxColumn();
            dateCreated.HeaderText 
= "Date Created";
            dateCreated.MappingName 
= "DateCreated";
            dateCreated.Width 
= 80;

            DataGridTextBoxColumn dateModified 
= new DataGridTextBoxColumn();
            dateModified.HeaderText 
= "Last Changed";
            dateModified.MappingName 
= "DateModified";
            dateModified.Width 
= 80
            
            dgTableStyle.GridColumnStyles.AddRange(
                
new DataGridColumnStyle[]{
                    taskID,priorityText,assignedToText,taskSummary,statusText,progressText,dateDue,dateCreated,dateModified
                }
);

            dgTableStyle.MappingName 
= TableName;
            
            
return dgTableStyle;
        }

    }

}

主窗体中使用:
dgTasks.TableStyles.Add(MainFormDgStyle.GetStyle()); 
dgTasks.DataSource 
= _dataLayer.Tasks.DefaultView;
我们来分析下代码:
DataGrid->DataGridTableStyle->DataGridColumnStyle

DataGridTableStyle为DataGrid表格样式。
DataGridColumnStyle为DataGrid列样式,为抽象类。
DataGridTextBoxColumn(为DataGrid文本框列样式) 继承 DataGridColumnStyle
DataGridBoolColumn(为DataGrid单选框列样式) 继承 DataGridColumnStyle

所以下面的代码就很好理解了。
DataGridTextBoxColumn taskID = new DataGridTextBoxColumn();
taskID.HeaderText = "ID";  //列标头的文本
taskID.MappingName = "TaskID";  //该列样式所映射到的数据成员名称
taskID.Width = 30;  //列宽度

其它的都一样了。

dgTableStyle.GridColumnStyles.AddRange( new DataGridColumnStyle[]{ taskID,priorityText,等等});
这是简单的把所定义的列样式添加到DataGrid表格样式中。

dgTableStyle.MappingName = TableName; //指定映射到数据源的名称

好了,继续我们的改进DataGrid控件。
我们希望默认情况下DataGrid中Date Created和Last Changed这两列不显示,不过经过定制可以显示出来。
这样我们可以修改代码将这两列的宽度暂时定为0,这样就可以暂时不显示出来,以后还有显示出来的机会。
DataGridTextBoxColumn dateCreated = new DataGridTextBoxColumn();
dateCreated.HeaderText 
= "Date Created";
dateCreated.MappingName 
= "DateCreated";
dateCreated.Width 
= 0;

DataGridTextBoxColumn dateModified 
= new DataGridTextBoxColumn();
dateModified.HeaderText 
= "Last Changed";
dateModified.MappingName 
= "DateModified";
dateModified.Width 
= 0
(2)美化DataGrid
让DataGrid隔行显示背景色:
dgTableStyle.AlternatingBackColor = System.Drawing.Color.LemonChiffon;
运行效果如下图:
image 
 
ID列左边的行标头,小菜并不想要,能去掉吗,当然。
dgTableStyle.RowHeadersVisible = false;
运行效果如下图:
image 
去掉DataGrid的篮色鸡冠:
可以在dgTasks的属性窗口将CaptionVisible设为false
对应代码dgTasks.CaptionVisible = false;
运行效果如下:
image 

从上图你可以看出,DataGrid默认使用DataGridTextBoxColumn做为列样式,所以默认是可编辑数据,最后一行白色可以允许你添加数据,
怎样才能禁止DataGrid添加删除修改呢?
在dgTasks的属性窗口将ReadOnly设为true
对应代码dgTasks.ReadOnly = true;
image
从图中你也可以看出,现在不能编辑,添加数据了,为灰色底框。

(3)自定义DataGrid列
新的需求出来了:Progress列是显示任务进度的,使用数字表示不够直观,让我们使用进度条来显示它。
如下图:
image
我们之前是使用:DataGridTextBoxColumn
DataGridTextBoxColumn progressText = new DataGridTextBoxColumn();
progressText.HeaderText 
= "Progress";
progressText.MappingName 
= "Progress";
progressText.Width 
= 55;
我们要自定义出进度条的效果,可以自定义类DataGridProgressBarColumn 继承 DataGridTextBoxColumn
为何不继承至DataGridColumnStyle呢?因为继承DataGridTextBoxColumn我们的工作将简化很多。这像写控件,你不会都想从Component继承吧,同理。
using System;
using System.Drawing;
using System.Windows.Forms;

namespace TaskVision
{
    
public class DataGridProgressBarColumn : System.Windows.Forms.DataGridTextBoxColumn
    
{
        
public DataGridProgressBarColumn(string headerText, string mappingName, int width)
        
{
            
base.HeaderText = headerText;
            
base.MappingName = mappingName;
            
base.Width = width;
        }


        
protected override void Edit(CurrencyManager source, int rowNum, Rectangle bounds, 
            
bool readOnly, string displayText, bool cellIsVisible)
        
{
            
//使单元格无法编辑                
        }


        
protected override void Paint(Graphics g, Rectangle bounds, CurrencyManager source, 
            
int rowNum, Brush backBrush, Brush foreBrush, bool alignToRight)
        
{               
            
            
int progressVal = (int)GetColumnValueAtRow(source, rowNum); //取得progress的值                
            float percentage = ((float)progressVal / 100.0f); //将progress除于100转为百份比 
            
            
// if the current row is this row, draw the selection back color
            if (this.DataGridTableStyle.DataGrid.CurrentRowIndex == rowNum)
                g.FillRectangle(
new SolidBrush(this.DataGridTableStyle.SelectionBackColor), bounds);
            
else
                g.FillRectangle(backBrush, bounds);

            
if (percentage > 0.0)
            
{
                
// Draw the progress bar and the text
                g.FillRectangle(new SolidBrush(Color.FromArgb(163189242)),bounds.X + 2
                    bounds.Y 
+ 2, Convert.ToInt32((percentage * bounds.Width - 4)),bounds.Height - 4);
                g.DrawString(progressVal.ToString() 
+ "%"this.DataGridTableStyle.DataGrid.Font, 
                    foreBrush, bounds.X 
+ 6, bounds.Y + 2);
            }

            
else
            
{
                
// draw the text
                if (this.DataGridTableStyle.DataGrid.CurrentRowIndex == rowNum)
                    g.DrawString(progressVal.ToString() 
+ "%"this.DataGridTableStyle.DataGrid.Font, 
                        
new SolidBrush(this.DataGridTableStyle.SelectionForeColor), 
                        bounds.X 
+ 6, bounds.Y + 2);
                
else
                    g.DrawString(progressVal.ToString() 
+ "%"this.DataGridTableStyle.DataGrid.Font, 
                        foreBrush, bounds.X 
+ 6, bounds.Y + 2);
            }


        }

    }

}
这样我们在MainFormDgStyle.cs中就可以将
DataGridTextBoxColumn progressText = new DataGridTextBoxColumn();
progressText.HeaderText 
= "Progress";
progressText.MappingName 
= "Progress";
progressText.Width 
= 55;
修改为:
DataGridProgressBarColumn progressText = new DataGridProgressBarColumn("Progress""Progress"55);
代码并不复杂,我们可查询.net sdk2.0关于Paint的参数,注意是查询DataGridColumnStyle.Paint,为什么不是DataGridTextBoxColumn.Paint呢?因为我们想要得到更多的支持,像背景刷Brush backBrush, 前景刷Brush foreBrush。
还有就是Edit,也是使用DataGridColumnStyle.Edit,这样才能保证无法编辑。

DataGridColumnStyle.Paint参数如下:
g:要绘制到的 Graphics。
bounds:要在其中绘画的外围 Rectangle。
source:该列所属的 System.Windows.Forms.DataGrid 控件的 CurrencyManager
rowNum:所引用的基础数据表中的行号。
backBrush:用于绘制背景色的 Brush。
foreBrush:用于绘制前景色的 Color。
alignToRight:一个值,它指示内容是否为右对齐。如果内容为右对齐,则为 true;否则为 false

同样我们也不希望其它列可以编辑,因为灰色的底框太难看了,我们希望的效果如下图:
image 
那我们就新建一个DataGridTextBoxColumn继承至DataGridTextBoxColumn
using System;
using System.Drawing;
using System.Windows.Forms;
using System.Threading;

namespace TaskVision
{
    
public class DataGridTextBoxColumn : System.Windows.Forms.DataGridTextBoxColumn
    
{
        
public DataGridTextBoxColumn(string format, string headerText, string mappingName, int width)
        
{
            
base.Format = format;
            
base.HeaderText = headerText;
            
base.MappingName = mappingName;
            
base.Width = width;
        }


        
protected override void Edit(System.Windows.Forms.CurrencyManager source, int rowNum,
                System.Drawing.Rectangle bounds, 
bool isReadOnly, string instantText, bool cellIsVisible)
        
{
            
//使单元格无法编辑
        }


        
protected override void Paint(System.Drawing.Graphics g, System.Drawing.Rectangle bounds,
            System.Windows.Forms.CurrencyManager source, 
int rowNum, System.Drawing.Brush backBrush,
            System.Drawing.Brush foreBrush, 
bool alignToRight)
        
{
            
object bVal = GetColumnValueAtRow(source, rowNum);

            
if (this.Format == "d"//日期格式
            {
                
try
                
{
                    bVal 
= String.Format("{0:d}", Convert.ToDateTime(bVal));
                }

                
catch
                
{
                }

            }


            
// if the current row is this row, draw the selection back color
            if (this.DataGridTableStyle.DataGrid.CurrentRowIndex == rowNum)
            
{
                g.FillRectangle(
new SolidBrush(this.DataGridTableStyle.SelectionBackColor), bounds);
                g.DrawString(Convert.ToString(bVal), 
this.DataGridTableStyle.DataGrid.Font,
                        
new SolidBrush(this.DataGridTableStyle.SelectionForeColor), bounds.X + 2, bounds.Y + 2);
            }

            
else
            
{
                g.FillRectangle(backBrush, bounds);
                g.DrawString(Convert.ToString(bVal), 
this.DataGridTableStyle.DataGrid.Font, foreBrush,
                                bounds.X 
+ 2, bounds.Y + 2);
            }

        }

    }

}

接下来就修改下MainFormDgStyle中的GetStyle()
将非日期的列按如下方式修改
DataGridTextBoxColumn taskID = new DataGridTextBoxColumn();
taskID.HeaderText 
= "ID";
taskID.MappingName 
= "TaskID";
taskID.Width 
= 30;
修改为
DataGridTextBoxColumn taskID = new DataGridTextBoxColumn(string.Empty, "ID""TaskID"30);
将日期的列按如下方式修改
DataGridTextBoxColumn dateDue = new DataGridTextBoxColumn();
dateDue.HeaderText 
= "Due Date";
dateDue.MappingName 
= "DateDue";
dateDue.Width 
= 65;
修改为
DataGridTextBoxColumn dateDue = new DataGridTextBoxColumn("d""Due Date""DateDue"65);
DataDue为任务预计完成日期,如果超期,应该给于强调,比如让日期显示为红色。
我们可以修改DataGridTextBoxColumn来完成这个功能:
using System;
using System.Drawing;
using System.Windows.Forms;
using System.Threading;

namespace TaskVision
{
    
public class DataGridTextBoxColumn : System.Windows.Forms.DataGridTextBoxColumn
    
{
        
private bool _isRedIfOverDue = false;

        
public DataGridTextBoxColumn(string format, string headerText, string mappingName, int width)
        
{
            
base.Format = format;
            
base.HeaderText = headerText;
            
base.MappingName = mappingName;
            
base.Width = width;
        }


        
public DataGridTextBoxColumn(string format, string headerText, string mappingName, 
            
int width, bool isRedIfOverDue)
        
{
            
base.Format = format;
            
base.HeaderText = headerText;
            
base.MappingName = mappingName;
            
base.Width = width;
            
this._isRedIfOverDue = isRedIfOverDue;
        }


        
protected override void Edit(CurrencyManager source, int rowNum, Rectangle bounds, 
            
bool readOnly, string displayText, bool cellIsVisible)
        
{
            
//使单元格无法编辑
        }


        
protected override void Paint(Graphics g, Rectangle bounds, CurrencyManager source, 
            
int rowNum, Brush backBrush, Brush foreBrush, bool alignToRight)
        
{
            
object bVal = GetColumnValueAtRow(source, rowNum);

            
if (this.Format == "d"//日期格式   
            {
                
try
                
{
                    bVal 
= String.Format("{0:d}", Convert.ToDateTime(bVal));
                }

                
catch
                
{
                }

            }


            
// if the current row is this row, draw the selection back color
            if (this.DataGridTableStyle.DataGrid.CurrentRowIndex == rowNum)
            
{
                g.FillRectangle(
new SolidBrush(this.DataGridTableStyle.SelectionBackColor), bounds);
                g.DrawString(Convert.ToString(bVal), 
this.DataGridTableStyle.DataGrid.Font, 
                    
new SolidBrush(this.DataGridTableStyle.SelectionForeColor), 
                    bounds.X 
+ 2, bounds.Y + 2);
            }

            
else
            
{
                g.FillRectangle(backBrush, bounds);

                
//如果超期显示为红色    
                if (_isRedIfOverDue && this.Format == "d" && 
                    Convert.ToDateTime(bVal).Date 
< DateTime.Now.Date)
                
{
                    g.DrawString(Convert.ToString(bVal), 
this.DataGridTableStyle.DataGrid.Font, 
                        
new SolidBrush(Color.Red), bounds.X + 2, bounds.Y + 2);
                }

                
else
                
{
                    g.DrawString(Convert.ToString(bVal), 
this.DataGridTableStyle.DataGrid.Font, 
                        foreBrush, bounds.X 
+ 2, bounds.Y + 2);
                }

            }

        }

    }

}

上面的优先级Priority使用文本显示:Major(高),Medium(中),Minor(低)不太直观,让我们使用图片来显示,这样可以引起注意。Major使用:Major.gifimage,Medium使用:Medium.gif空白图片,Minor使用:Minor.gif image
最终效果如下图:
image 
我们为优先级定义一个列DataGridPriorityColumn代码如下:
using System;
using System.Drawing;
using System.Collections;
using System.Windows.Forms;

namespace TaskVision
{
    
public class DataGridPriorityColumn : System.Windows.Forms.DataGridTextBoxColumn
    
{
        
private Hashtable _images = new Hashtable(); //用来缓存图片路径
        private readonly static string _priorityImagesPath = Application.StartupPath + @"\Images\";

        
public DataGridPriorityColumn(string headerText, string mappingName, int width)
        
{
            
base.HeaderText = headerText;
            
base.MappingName = mappingName;
            
base.Width = width;
        }


        
protected override void Edit(CurrencyManager source, int rowNum, Rectangle bounds, 
            
bool readOnly, string displayText, bool cellIsVisible)
        
{
            
//使之无法编辑
        }


        
protected override void Paint(Graphics g, Rectangle bounds, CurrencyManager source, 
            
int rowNum, Brush backBrush, Brush foreBrush, bool alignToRight)
        
{
            
object bVal = GetColumnValueAtRow(source, rowNum);

            Image imageToDraw;

            
// we're caching the image in a hashtable
            if (_images.ContainsKey(bVal))
                imageToDraw 
= (Image)_images[bVal];
            
else
            
{
                
// get the image from disk and cache it
                try
                
{
                    imageToDraw 
= Image.FromFile(_priorityImagesPath + (string)bVal + ".gif");
                    _images.Add(bVal, imageToDraw);
                }

                
catch
                
{
                    
return;
                }

            }


            
// if the current row is this row, draw the selection back color
            if (this.DataGridTableStyle.DataGrid.CurrentRowIndex == rowNum)
                g.FillRectangle(
new SolidBrush(this.DataGridTableStyle.SelectionBackColor), bounds);
            
else
                g.FillRectangle(backBrush, bounds);

            g.DrawImage(imageToDraw, 
new Point(bounds.X, bounds.Y));
        }

    }

}

修改MainFormDgStyle的代码:
DataGridTextBoxColumn priorityText = new DataGridTextBoxColumn("Priority","PriorityText",75);
修改为:
DataGridPriorityColumn priorityText = new DataGridPriorityColumn("Priority""PriorityText"15);
好了,到现在自定义DataGrid就演化完了。

提供到该篇为止的完整源码,包括数据库:https://files.cnblogs.com/a-peng/taskvision_6.rar