Winform DataGridView扩展系列(二)

二、多列表头组件的实现:

  1. 要求:

    同汇总行,不再多说

  2. 实现:

    组件的实现与控件有些差别,特别是在设计时支持上.

    a.组件的层次结构:

    [DesignTimeVisible( true)]
    [Designer( typeof(ZYQ.Design.Designer.MultiDataGridViewColumnHeadersComponentDesigner),  typeof(IDesigner))]
    public  class MultiDataGridViewColumnHeadersComponent : Component{}
    [DefaultProperty( " Text ")]
    public  class MultiDataGridViewColumnHeader{}
    [Editor( typeof(ZYQ.Design.Utilities.Editor.MultiColumnHeaderEditor),  typeof(UITypeEditor))]
    public  class MultiDataGridViewColumnHeaderCollection : List<MultiDataGridViewColumnHeader>{}

    b.设计时支持

    public  class IncludDataGridViewColumnNameConverter : TypeConverter{}
    public  class IncludingDataGridViewColumnCollectionConverter:TypeConverter{}
    public  class IncludingDataGridViewColumnCollectionEditor:CollectionEditor{}
    public  partial  class IncludingDataGridViewColumnEditorFrom : Form{}
    public  class MultiColumnHeaderEditor : UITypeEditor{}
    public  partial  class MultiColumnHeaderEditorFrom : Form{}
    public  class MultiDataGridViewColumnHeadersNode : TreeNode{}
    public  class MultiDataGridViewColumnHeadersComponentDesigner:ComponentDesigner{}

    由于嵌套集合vs默认ComponentDesigner不能完整实现设计时代码,所以必须从ComponentDesigner继承一个设计器来实现设计时代码的自动实现。网上有很多帖子是放在Editor中用 ITypeDescriptorContext对象的PropertyDescriptor属性的SetValue方法实现设计时代码的持久序列化,但在组件中不能用。原因就在于设计器无法知道该如何处理ComponentChanged事件提交的更改,所以正确引发ComponentChanged事件并自己处理提交的更改成为自定义设计器的首要任务。

    public  class MultiDataGridViewColumnHeadersComponentDesigner:ComponentDesigner
    {
        ......
         private  void InitializeServices()
        {
            ......
             this.svr_componentchange =GetService( typeof(IComponentChangeService));
             if ( this.svr_componentchange !=  null)
            {
             this.svr_componentchange.ComponentChanged +=  new ComponentChangedEventHandler(changeService_ComponentChanged);
            }
            .......
        }
         void Items_CollectionChanged( object sender, CollectionChangeEventArgs e)
        {
            base.RaiseComponentChanged((MemberDescriptor)TypeDescriptor.GetProperties(_mycomponent)[ " Items "],  null, _mycomponent.Items);
        }
         private  void DoTransaction( string propertyname)
        {
            designer_transaction = svr_host.CreateTransaction( " Change Items ");
             using (designer_transaction)
            {
                PropertyDescriptor itms = TypeDescriptor.GetProperties(_mycomponent)[propertyname];
                itms.SetValue(Component, _mycomponent.Items);
                    designer_transaction.Commit();
            }
        }

     

    c.细节与实现:

    为配合DataGridView的动作使多列表头跟随改变,必须注册DataGridView的相关事件来重新计算多列表头绘制所需要的数据,他们是:

      #region Hook Events
             void items_CollectionChanged( object sender, CollectionChangeEventArgs e)
            {
                 // if (Depth!=-1)
                    OnDepthChanged();
                   
            }
             void _mygrid_Scroll( object sender, ScrollEventArgs e)
            {
                 if (e.ScrollOrientation == ScrollOrientation.HorizontalScroll)
                {
                    HeaderPrePanit();
                }
            }
             void _mygrid_ColumnStateChanged( object sender, DataGridViewColumnStateChangedEventArgs e)
            {
                HeaderPrePanit();
            }
             void _mygrid_Paint( object sender, PaintEventArgs e)
            {
                PaintHeader();
              
            }

             void _mygrid_CellPainting( object sender, DataGridViewCellPaintingEventArgs e)
            {
                 // 绘制表头
                 if (e.RowIndex == - 1 &&  this.Depth >  0 && e.ColumnIndex != - 1)
                {
                    
                     if (!_IsChangedColumnHeadHeigh)
                        _mygrid.ColumnHeadersHeight += ( this.Depth) *  this.PerHeaderHeight;
                   
                }
            }
             void _mygrid_ColumnHeadersHeightChanged( object sender, EventArgs e)
            {

                 if ( this.PerHeaderHeight *  this.Depth != _mygrid.ColumnHeadersHeight)
                {
                    _mygrid.ColumnHeadersHeight =  this.Depth*  this.PerHeaderHeight;
                    _IsChangedColumnHeadHeigh =  true;
                }
                 else
                    _IsChangedColumnHeadHeigh =  true;
            }
             void _mygrid_CellMouseLeave( object sender, DataGridViewCellEventArgs e)
            {
                 if (e.RowIndex == - 1)
                {
                     // _IsMouseEnter = false;
                    _selectColumnindex = - 1;
                }
                HeaderPrePanit();
            }

             void _mygrid_CellMouseEnter( object sender, DataGridViewCellEventArgs e)
            {
                 if (e.RowIndex == - 1)
                {
                     // _IsMouseEnter = true;
                    _selectColumnindex = e.ColumnIndex;
                }
                HeaderPrePanit();
            }

             void _mygrid_ColumnHeaderCellChanged( object sender, DataGridViewColumnEventArgs e)
            {
                HeaderPrePanit();
            }

             void _mygrid_ColumnDividerWidthChanged( object sender, DataGridViewColumnEventArgs e)
            {
                HeaderPrePanit();
            }
             void _mygrid_ColumnWidthChanged( object sender, DataGridViewColumnEventArgs e)
            {
                ReadyHeader();
                HeaderPrePanit();
            }

             void _mygrid_ColumnDisplayIndexChanged( object sender, DataGridViewColumnEventArgs e)
            {
                HeaderPrePanit();
            }

             #endregion

    多列表头的绘制应该细分一下。影响宽度的情况有列宽的变化(ColumnWidthChanged)、分隔线宽度变化(可以在绘制边框时处理)、列的Visible属性变化;影响位置的变化有水平Scroll的移动;还有鼠标进入表头时背景色的变化,所以绘制被分为ReadyHeader计算宽度、HeaderPrePaint计算位置、HeaderPostPaint绘制多列表头。

    private  void PaintHeader()
    {
        _mygrid.SuspendLayout();
         if (_IsReady !=  4)
        {
            ReadyHeader();
            HeaderPrePanit();
        }
        Graphics g = _mygrid.CreateGraphics();
        PaintEventArgs e =  new PaintEventArgs(g, GetHeaderBandRectangle());
        HeaderPostPaint(g, e);
        _mygrid.ResumeLayout( true);
    }
    private  void PaintContext(Graphics g, Rectangle rect, Rectangle cliprect, string text, bool iscolumn){...}
    private  void PainDividerWidth(Graphics g,  int width, Rectangle rect, Rectangle cliprect){...}
    private  void PaintBackgrond(Graphics g, Rectangle rect, Rectangle clip){...}
    private  void PaintSelectedBackground(Graphics g, Rectangle rect, Rectangle clip){...}
  3. 总结:

     使用组件的好处显而易见。但是使用时的闪烁现象没办法克服!N层多列表头呈现完成。

转载请注明:http://www.cnblogs.com/RZYQ/archive/2011/12/28/2305257.html

转载于:https://www.cnblogs.com/RZYQ/archive/2011/12/28/2305257.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值