SourceGrid 阅读笔记(2)

    技术2022-05-11  40

    转载请注明出处: 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

    SourceGrid 阅读笔记(1)


    最新回复(0)