SourceGrid 阅读笔记(2)

转载请注明出处: 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 大于零。否则为零,具体图形如下:
 
  Panel 

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

SourceGrid 阅读笔记(1)

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值