转载请注明出处: http://blog.csdn.net/jh_zzz/
==================================================================
2.
相关的数据是如何处理的:
GridVirtual本身并不维护具体的Cell数据,Rows和Columns对象的构建实际上都是由派生类(如SourceGrid.Grid)通过重写抽象函数CreateRowsObject及CreateColumnsObject函数实现。
CellVirtual有一个Model(
Models.ModelContainer)对象,用来加载不同的Cell派生对象。由CellVirtual派生的类在构造函数中会调用Model. AddModel向Model注册,如Cells.Virtual.CheckBox:
public
CheckBox()
{
…
Model.AddModel(new Models.CheckBox());
}
Models.CheckBox负责维护具体的数据,比如CheckBox的状态,标题等内容,Models.Image则维护相应的图片信息。
注意到分别从
Cell
和
CellVirtual
派生了一个
CheckBox
类,这两个具体什么时候用哪个我还没搞清。
如何向一个
Grid
添加一个
Cell
对象呢?
下面是一个最简单的例子:
private
void frmSample14_Load(object sender, System.EventArgs e)
{
grid1.BorderStyle = BorderStyle.FixedSingle;
grid1.ColumnsCount = 3;
grid1.FixedRows = 1;
grid1.Rows.Insert(0);
grid1[0,0] = new SourceGrid.Cells.ColumnHeader("String");
grid1[0,1] = new SourceGrid.Cells.ColumnHeader("DateTime");
grid1[0,2] = new SourceGrid.Cells.ColumnHeader("CheckBox");
for (int r = 1; r < 10; r++)
{
grid1.Rows.Insert(r);
grid1[r,0] = new SourceGrid.Cells.Cell("Hello " + r.ToString(), typeof(string));
grid1[r,1] = new SourceGrid.Cells.Cell(DateTime.Today, typeof(DateTime));
grid1[r,2] = new SourceGrid.Cells.CheckBox(null, true);
}
grid1.AutoSizeCells();
}
还以CheckBox为例,
public
Cells.ICell this[int row, int col]方法最终将调用InsertCell,InsertCell会调用Cell对象的BindToGrid方法将Cell绑定到指定的位置,以下是SourceGrid.Cells.Cell中的BindToGrid方法代码:
public
void BindToGrid(Grid p_grid, Position p_Position)
{
m_Range.MoveTo(p_Position);
if (Model == null)
throw new SourceGridException("Models not valid, Model property is null. You must assign the models before binding the cell to the grid.");
if (Model.ValueModel == null)
throw new SourceGridException("Models not valid, Model.ValueModel property is null. You must assign the value model before binding the cell to the grid.");
m_Grid = p_grid;
OnAddToGrid(EventArgs.Empty);
RefreshSpanSearch();
}
这里m_Range是一个Range对象,Range对象定义了由一个起始点和一个结束点划定的范围,同时还包含一个Cell跨行,跨列等相应的信息。
添加完了一个Cell,具体的数据由Cell中的Model自己维护,接下来便是如何显示了。
==================================================================
3.
自绘的过程是如何完成的
每个
Grid
由几块板构成。
GridSubPanel
m_PanelDockTop
///
This panel contains the TopLeft and the Top panel. Is used for focking purpose.
GridSubPanel
LeftPanel
///
Gets the not scrollable left panel (For RowHeader)
GridSubPanel
TopPanel
///
Gets the not scrollable top panel (For ColHeader)
GridSubPanel
TopLeftPanel
///
Gets the not scrollable top+left panel (For Row or Col Header)
GridSubPanel
ScrollablePanel
///
Gets the scrollable panel for normal scrollable cells
GridSubPanelHidden
HiddenFocusPanel
///
Gets the hidden panel for internal use only. I use this panel to catch mouse and keyboard events.
他们的布置如下:
当存在固定行数,并且当前行数大于固定行数,
PanelDockTop.Height
大于零。否则为零。
当存在固定列数,并且当前列数大于固定列数
m_LeftPanel.Width = m_TopLeftPanel.Width
大于零。否则为零,具体图形如下:
TopPanel
存放列标题(高度由固定行数决定)。
LeftPanel
存放行标题(宽度由固定列数决定)
SourceGrid. CustomScrollControl 在初始化时会自动调用CreateDockControls,此函数是一个虚函数,GridVirtual派生自CustomScrollControl,并覆盖了CreateDockControls函数,创建了相应的Dock Panel。
GridVirtual提供OnTopLeftPanelPaint,OnTopPanelPaint,OnScrollablePanelPaint函数,GridSubPanel在OnPaint时会根据自己的属性调用相应的重绘函数,Grid以及Cell在需要的时候会调用Invalidate(InvalidateCell最终也是调用的Invalidate函数)促使OnPaint函数被调用,从而导致Cell被重绘。
前面提得OnTopLeftPanelPaint几个函数都是调用的PanelPaint,PanelPaint会依次调用PaintCell促使每个Cell重绘,PaintCell最终是调用的Cell对象自身的View对象的
DrawCell方法来进行重绘:
protected
virtual void PaintCell(CellContext cellContext,
GridSubPanel
p_Panel,
DevAge.Drawing.GraphicsCache graphics,
Rectangle
p_PanelDrawRectangle)
{
if ( p_PanelDrawRectangle.Width > 0 && p_PanelDrawRectangle.Height > 0 &&
(cellContext.Cell.Editor == null || cellContext.Cell.Editor.EnableCellDrawOnEdit || cellContext.IsEditing() == false) )
cellContext.Cell.View.DrawCell(cellContext, graphics, p_PanelDrawRectangle);
}
再来看看DrawCell函数,他位于Cells.Views中:
public
void DrawCell(CellContext cellContext,
DevAge.Drawing.GraphicsCache graphics,
Rectangle p_ClientRectangle)
{
PrepareView(cellContext);
Draw(graphics, p_ClientRectangle);
}
我原本以为每个View对象自己会完成控件绘制过程,或者直接创建一个.Net标准的控件,事实却并非如此,这里Draw方法已经深入到了DevAgeSourcePack项目中,通过源代码发现,他其实就是调用了
OnDraw函数。ViewBase对象是从
DevAge.Drawing.VisualElements.ContainerBase派生的,并实现了IView接口,所有的Cells.Views中定义的对象都是从ViewBase派生的,看看ContainerBase中的
OnDraw函数:
protected override void OnDraw(GraphicsCache graphics, RectangleF area)
{
OnDrawBackground(graphics, area);
using (MeasureHelper measure = new MeasureHelper(graphics))
{
RectangleF contentArea = GetContentRectangle(measure, area);
OnDrawContent(graphics, contentArea);
}
}
OnDraw会调用OnDrawBackground,GetContentRectangle,OnDrawContent,继续看:
protected virtual void OnDrawBackground(GraphicsCache graphics, RectangleF area)
{
if (Border != null)
{
Border.Draw(graphics, area);
area = Border.GetContentRectangle(area);
}
if (Background != null)
{
Background.Draw(graphics, area);
}
}
protected virtual void OnDrawContent(GraphicsCache graphics, RectangleF area)
{
//In this case the elements are drawed one over the another
if (ElementsDrawMode == ElementsDrawMode.Covering)
{
foreach (IVisualElement element in GetElements())
element.Draw(graphics, area);
}
//In this case the elements are drawed considering an alignment
else if (ElementsDrawMode == ElementsDrawMode.Align)
{
using (MeasureHelper measure = new MeasureHelper(graphics))
{
foreach (IVisualElement element in GetElements())
{
RectangleF elementArea;
element.Draw(graphics, area, out elementArea);
area = CalculateRemainingArea(area, element.AnchorArea, elementArea);
}
}
}
else
throw new ApplicationException("DrawMode not supported");
}
可以发现,这几个函数都依赖于GetElements方法,Border和Background属性,DrawCell在Draw之前调用了PrepareView,他会准备相关的绘制信息,回头再看看Views中的对象,他们都会重写GetElements方法,在PrepareView中也会设置Border,Background的信息,从而使得绘制可成功完成。
==================================================================
其他单元格编辑之类的暂时也就没时间看了,SourceGrid对MVC设计模式应用的非常好,看完了SourceGrid代码,自己写自定义的控件应该没问题了,受益匪浅。
邮件交流:
mailto:jhzzzz@gmail.com