Silverlight 自定义Grid实线

(一)前言

由于项目的需求,必须用silverlight实现表格形式的预警图。在Silverlight中表格形式的最佳方式为Grid,虽然Grid提供了ShowGridLine属性,但是该线条为虚线,外观看起来很不协调。开始的时候,本人打算在每个Cell中采用Border来设置线条。这样虽然可以实现实线,但是每行每列都会涉及到单元格与单元格之间的重叠,以及第一列与最后一列、第一行与最后一行的线条控制需要复杂的算法来控制,因此这种方式肯定是行不通的(不是实现不了,而是将简单的问题复杂化了)。下面讲解的是另外一种方式,采用添加直线的方法来绘制实线。

 

(二)类图设计

下面所涉及的都是针对于特定情况下的Grid实线绘制,仅仅是本人项目中抽象出来的简单版本而已(仅仅是思路,具体实现涉及到信息安全,哥可扛不起,哈哈,现在的绝对与项目无关),类图如下:

 

实现的效果图如下:

 

(三)具体实现

这里主要可以控制第一行的高度,第一列的宽度,Grid在容器Canvas的间隔(LeftMargin,TopMargin),其他单元格的宽度、高度,Grid的行数,Grid的列数。至于实线的具体绘制封装在基类GridFormatter中,具体单元格的内容由子类来实现(可能用于很多场景),第一行的标题,第一列的内容都有子类AlertTable去实现。这里涉及到一些简单的数学运算,应该是很容易理解的。实现多态的话,只要实现抽象类GridFormatter的抽象方法SetTableContent即可。

 

基类GridFormatter的具体实现

(1)调用的方法主要是如下2个,主要参数都标识了,应该比较清晰易懂(至于判断参数是否小于0,这里就不打算写上去了,大家只要知道这些参数具体是什么就可以了):

 1 /// <summary>
 2         /// 实例化GridFormatter类。
 3         /// </summary>
 4         /// <param name="leftMargin">左边距。</param>
 5         /// <param name="topMargin">右边距。</param>
 6         /// <param name="rowCount">行数。</param>
 7         /// <param name="columnCount">列数。</param>
 8         public GridFormatter(int leftMargin, int topMargin,
 9             int rowCount,int columnCount)
10         {
11             this.LeftMargin = leftMargin;
12             this.TopMargin = topMargin;
13             this.RowCount = rowCount;
14             this.ColumnCount = columnCount;
15         }
16 
17         /// <summary>
18         /// 创建Grid的内容。
19         /// </summary>
20         /// <param name="canvas">Canvas容器。</param>
21         /// <param name="cellWidth">单元格宽度。(第一行第一列的除外)</param>
22         /// <param name="cellHeight">单元格高度。(第一行第一列的除外)</param>
23         /// <param name="firstColumnWidth">第一列的宽度。</param>
24         /// <param name="firstRowHeight">第一行的高度。</param>
25         public void Create(Canvas canvas, int cellWidth, int cellHeight,
26             int firstColumnWidth, int firstRowHeight) 
27         {
28             if (canvas == null)
29             {
30                 throw new ArgumentNullException("The container's parameter is null!");
31             }
32             
33             this.CellWidth = cellWidth;
34             this.CellHeight = cellHeight;
35             this.FirstColumnWidth = firstColumnWidth;
36             this.FirstRowHeight = firstRowHeight;
37 
38             AddGrid(canvas);
39             CreateTable();
40         }

 

(2)接下来我们需要设置表格的架构,也就是说设置表格的行数和列数,以及这些单元格的宽度和高度,代码如下:

 1         private void SetTableSchema()
 2         {
 3             for (int rowIndex = 0; rowIndex < this.RowCount; rowIndex++)
 4             {
 5                 int height = rowIndex == 0 ? this.FirstRowHeight : this.CellHeight;
 6                 RowDefinition rd = new RowDefinition();
 7                 rd.Height = new GridLength(height);
 8                 this.Grid.RowDefinitions.Add(rd);
 9             }
10            
11             for (int columnIndex = 0; columnIndex < this.ColumnCount; columnIndex++)
12             {
13                 int width = columnIndex == 0 ? this.FirstColumnWidth : this.CellWidth;
14                 ColumnDefinition cd = new ColumnDefinition();
15                 cd.Width = new GridLength(width);
16                 this.Grid.ColumnDefinitions.Add(cd);
17             }
18         }

如果是第一行,设置高度为this.FirstRowHeight。如果是第一列,设置宽度为this.FirstColumnWidth。具体由如下2行代码设置高度和宽度,

5                 int height = rowIndex == 0 ? this.FirstRowHeight : this.CellHeight;

13               int width = columnIndex == 0 ? this.FirstColumnWidth : this.CellWidth;

(3)设置Grid的大小以及容器Canvas的大小(宽度和高度),因为Canvas必须设置大小才能正常显示,而且可以通过Canvas才能灵活的设置控件的绝对位置。代码如下:

1         private void SetSize()
2         {
3             this.Width = this.CellWidth * (this.ColumnCount - 1+ this.FirstColumnWidth;
4             this.Height = this.CellHeight * (this.RowCount - 1+ this.FirstRowHeight;
5             this.Container.Width = this.Width + this.LeftMargin * 2;
6             this.Container.Height = this.Height + this.TopMargin * 2;
7         }

下面的2行代码将使Grid在Canvas中居中显示:

5             this.Container.Width = this.Width + this.LeftMargin * 2;
6             this.Container.Height = this.Height + this.TopMargin * 2;

 

(4)设置水平线,代码如下:

 1         private void SetHorizontalLines()
 2         {
 3             AddLine(this.LeftMargin, this.TopMargin, this.Width + this.LeftMargin, this.TopMargin);
 4 
 5             for (int index = 0; index < this.RowCount; index++)
 6             {
 7                 int y1 = index * this.CellHeight + this.TopMargin + this.FirstRowHeight;
 8                 int y2 = y1;
 9                 AddLine(this.LeftMargin, y1, this.Width + this.LeftMargin, y2);
10             }
11         }

 

第三行   3   AddLine(this.LeftMargin, this.TopMargin, this.Width + this.LeftMargin, this.TopMargin);主要是绘制最上面的水平线。

for循环将设置其他的水平线。int y1 = index * this.CellHeight + this.TopMargin + this.FirstRowHeight;设置y坐标。因为从该行开始,y坐标将加上this.TopMargin(顶部间隔)以及 this.FirstRowHeight(第一行的高度)。

 

(5)设置竖线,代码如下:

 1         private void SetVerticalLines()
 2         {
 3             AddLine(this.LeftMargin, this.TopMargin, this.LeftMargin, this.TopMargin + this.Height);
 4 
 5             for (int index = 0; index < this.ColumnCount; index++)
 6             {
 7                 int x1 = index * this.CellWidth + this.LeftMargin + this.FirstColumnWidth;
 8                 int x2 = x1;
 9                 AddLine(x1, this.TopMargin, x2, this.TopMargin + this.Height);
10             }
11         }

第三行   3   AddLine(this.LeftMargin, this.TopMargin, this.LeftMargin, this.TopMargin + this.Height);主要是绘制最左边的竖线。

for循环将设置其他的竖线。int x1 = index * this.CellWidth + this.LeftMargin + this.FirstColumnWidth;设置x坐标。因为从该列开始,x坐标将加上this.LeftMargin (左边间隔)以及 this.FirstColumnWidth(第一列的宽度)。

 

(6)实现单元格的内容

对于单元格具体内容的设置,将由子类实现public abstract void SetTableContent();

 1     public class AlertTable : GridFormatter
 2     {
 3         public AlertTable(int leftPadding, int topPadding,
 4             int rowCount, int columnCount)
 5             : base(leftPadding, topPadding, rowCount, columnCount)
 6         {
 7         }
 8 
 9         public override void SetTableContent()
10         {
11             SetFirstRowContent();
12             SetFirstColumnContent();
13             SetContent();
14         }
15 
16         private void SetContent()
17         {          
18             for (int rowIndex = 1; rowIndex < base.RowCount; rowIndex++)
19             {
20                 for (int columnIndex = 1; columnIndex < base.ColumnCount; columnIndex++)
21                 {
22                     //SetCellUserControl(rowIndex, columnIndex);
23                 }           
24             }
25         }
26 
27         private void SetFirstColumnContent()
28         {
29             for (int index = 1; index < base.RowCount; index++)
30             {
31                 base.SetCellTextContent(index, 0"Row " + index.ToString());
32             }
33         }
34 
35         private void SetFirstRowContent()
36         {
37             for (int index = 1; index < base.ColumnCount; index++)
38             {
39                 base.SetCellTextContent(0, index, "Column Head " + index.ToString());
40             }
41         }
42     }


子类将重写父类的方法,如第9行:public override void SetTableContent();具体由子类来填充单元格的内容。

 

具体实现只有100行左右的代码,



(四)总结

针对于容器中控件的绝对位置的控制采用Canvas(通常需要涉及到具体坐标点的控制),灵活运用各种控件来进行操作。对于Grid需要设置实线的话必须自定义绘制相关实线(目前Grid是虚线的,很不协调)。这种方式是由局限性的,只是针对于特定场景最有效。最近几个月跟着款哥混,水平直线飙升,多谢款哥。一个人有多优秀,要看他有谁指点,跟对人,做对事,至理名言啊,哈哈....

其实项目中哥还是没采用这种多态的方式,没必要,这样的场景只是偶尔出现,不需要多态。上面涉及的只是很简单的抽象,很多时候,代码需要不断的设计、重写来完善。有时候,方法也没必要设计得非常灵活,因为更加灵活的话意味着复杂性的提高。

 

源代码下载:/Files/jasenkin/Jasen.Silverlight.GridSample.rar

 

posted @ 2011-05-01 18:07  jasen.kin  阅读(4909)  评论(17编辑  收藏  举报