Winform DataGridView扩展系列(二)
二、多列表头组件的实现:
- 要求:
    同汇总行,不再多说 
- 实现:
    组件的实现与控件有些差别,特别是在设计时支持上. 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){...}
- 总结:
    使用组件的好处显而易见。但是使用时的闪烁现象没办法克服!N层多列表头呈现完成。  
转载请注明:http://www.cnblogs.com/RZYQ/archive/2011/12/28/2305257.html
 
                    
                 
 
                
            
         浙公网安备 33010602011771号
浙公网安备 33010602011771号