木木的大象

导航

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

posted on 2011-12-28 18:05  木木的大象  阅读(1297)  评论(1编辑  收藏  举报