二、多列表头组件的实现:
- 要求:
同汇总行,不再多说
- 实现:
组件的实现与控件有些差别,特别是在设计时支持上.
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