Silverlight多重表头实现
内容导读
互联网集市收集整理的这篇技术教程文章主要介绍了Silverlight多重表头实现,小编现在分享给大家,供广大互联网技能从业者学习和参考。文章包含19212字,纯文字阅读大概需要28分钟。
内容图文
效果:
实现主要逻辑:通过动态拼接XML生成表头样式,绑定到列上。
主要是动态拼接XML时要仔细核对对应的占位行,具体可以看代码,注释很详细
两个类一个接口
NTree<T>:定义表头树形结构
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Collections.ObjectModel; 5 6 namespace SLDGHeader 7 { 8 /// <summary> 9 /// 树结构 10 /// </summary> 11 /// <typeparam name="T"> 节点中的数据 </typeparam> 12 public class NTree<T> 13 { 14///<summary> 15/// 节点数据 16///</summary> 17privatereadonly T data; 18///<summary> 19/// 节点数据 20///</summary> 21public T Data 22 { 23get { return data; } 24 } 25///<summary> 26/// 是否根节点 27///</summary> 28publicbool IsRoot { get { return Parent == null; } } 29///<summary> 30/// 当前节点深度 31/// 根节点为1 32///</summary> 33publicint Depth { get; privateset; } 34///<summary> 35/// 父节点 36///</summary> 37public NTree<T> Parent 38 { 39get; 40privateset; 41 } 42///<summary> 43/// 子节点 44///</summary> 45public ReadOnlyCollection<NTree<T>> Children 46 { 47get { return children.AsReadOnly(); } 48 } 49private List<NTree<T>> children; 50///<summary> 51/// 实例化一个节点 52///</summary> 53///<param name="data">节点数据</param> 54public NTree(T data) 55 { 56this.Depth = 1; 57this.data = data; 58 children = new List<NTree<T>>(); 59 } 60///<summary> 61/// 在当前节点添加子节点 62///</summary> 63///<param name="data">节点数据</param> 64///<returns>当前节点</returns> 65public NTree<T> AddChild(T data) 66 { 67var node = new NTree<T>(data) { Parent = this, Depth = this.Depth + 1 }; 68 children.Add(node); 69returnthis; 70 } 71///<summary> 72/// 在当前节点子节点中插入子节点 73///</summary> 74///<param name="index">插入位置</param> 75///<param name="data">节点数据</param> 76///<returns>当前节点</returns> 77public NTree<T> InsertChild(int index, T data) 78 { 79var node = new NTree<T>(data) { Parent = this, Depth = this.Depth + 1 }; 80 children.Insert(index, node); 81returnthis; 82 } 83///<summary> 84/// 在当前节点添加子节点 85///</summary> 86///<param name="data">节点数据</param> 87///<returns>当前节点</returns> 88public NTree<T> AddChilren(params T[] datas) 89 { 90foreach (var data in datas) 91 { 92 AddChild(data); 93 } 94returnthis; 95 } 96///<summary> 97/// 移除当前节点下指定的子节点 98///</summary> 99///<param name="node">要移除的子节点</param>100///<returns>当前节点</returns>101public NTree<T> RemoveChild(NTree<T> node) 102 { 103 children.Remove(node); 104returnthis; 105 } 106///<summary>107/// 在当前节点添加兄弟节点 108///</summary>109///<param name="data">节点数据</param>110///<returns>当前节点</returns>111///<exception cref="NullParentNodeException:当前节点没有父节点">当前节点没有父节点</exception>112public NTree<T> AddBrother(T data) 113 { 114if (this.Parent == null) 115 { 116thrownew NullParentNodeException("有父节点的节点才能添加兄弟节点。"); 117 } 118119this.Parent.AddChild(data); 120returnthis; 121 } 122///<summary>123/// 获取指定索引处的子节点 124///</summary>125///<param name="i">子节点索引</param>126///<returns>子节点</returns>127///<exception cref="ArgumentOutOfRangeException:子节点索引超出范围">子节点索引超出范围</exception>128public NTree<T> GetChild(int i) 129 { 130if (i >= children.Count || i < 0) 131 { 132thrownew ArgumentOutOfRangeException("子节点索引超出范围"); 133 } 134return children[i]; 135 } 136///<summary>137/// 获取指定的子节点 138///</summary>139///<param name="data">节点数据</param>140///<returns>查找到的第一个子节点</returns>141public NTree<T> GetChild(T data) 142 { 143return children.Where(i => i.data.Equals(data)).FirstOrDefault(); 144 } 145///<summary>146/// 获取指定子节点的索引 147///</summary>148///<param name="data">节点数据</param>149///<returns>查找到的第一个子节点的索引,没有找到返回-1</returns>150publicint GetChildIndex(NTree<T> data) 151 { 152var index = -1; 153for (int i = 0; i < children.Count; i++) 154 { 155if (children[i].Equals(data)) 156 { 157 index = i; 158break; 159 } 160 } 161return index; 162 } 163///<summary>164/// 遍历树节点 165///</summary>166///<param name="node">起始节点</param>167///<param name="action">遍历到每个节点的操作</param>168///<exception cref="ArgumentNullException:参数action不可为空">参数action不可为空</exception>169publicstaticvoid Traverse(NTree<T> node, Action<T> action) 170 { 171if (action == null) 172 { 173thrownew ArgumentNullException("参数action不可为空"); 174 } 175 action(node.data); 176foreach (var kid in node.children) 177 { 178 Traverse(kid, action); 179 } 180 } 181///<summary>182/// 从当前节点开始遍历树节点 183///</summary>184///<param name="action">遍历到每个节点的操作</param>185///<exception cref="ArgumentNullException:参数action不可为空">参数action不可为空</exception>186publicvoid Traverse(Action<T> action) 187 { 188 Traverse(this, action); 189 } 190///<summary>191/// 遍历树节点 192///</summary>193///<param name="node">起始节点</param>194///<param name="action">遍历到每个节点的操作</param>195///<exception cref="ArgumentNullException:参数action不可为空">参数action不可为空</exception>196publicstaticvoid Traverse(NTree<T> node, Action<NTree<T>> action) 197 { 198if (action == null) 199 { 200thrownew ArgumentNullException("参数action不可为空"); 201 } 202 action(node); 203foreach (var kid in node.children) 204 { 205 Traverse(kid, action); 206 } 207 } 208///<summary>209/// 从当前节点开始遍历树节点 210///</summary>211///<param name="action">遍历到每个节点的操作</param>212///<exception cref="ArgumentNullException:参数action不可为空">参数action不可为空</exception>213publicvoid Traverse(Action<NTree<T>> action) 214 { 215if (action == null) 216 { 217thrownew ArgumentNullException("参数action不可为空"); 218 } 219 action(this); 220foreach (var kid inthis.children) 221 { 222 Traverse(kid, action); 223 } 224 } 225///<summary>226/// 获取当前节点开始的所有节点中数据 227///</summary>228///<returns>节点数据列表</returns>229public IEnumerable<T> GetDatas() 230 { 231returnnew[] { data }.Union(children.SelectMany(x => x.GetDatas())); 232 } 233///<summary>234/// 当前节点下叶节点的数量 235///</summary>236///<returns></returns>237publicint GetCount() 238 { 239var count = 0; 240 Traverse((NTree<T> n) => 241 { 242if (n.Children.Count == 0) 243 { 244 count++; 245 } 246 }); 247return count; 248 } 249///<summary>250/// 获取当前节点所在树的深度 251///</summary>252///<returns>当前节点所在树的深度</returns>253publicint GetTreeDepth() 254 { 255int Depth = 1; 256var parent = this; 257while (parent.Parent != null) 258 { 259 parent = parent.Parent; 260 } 261 Traverse((NTree<T> n) => 262 { 263if (Depth < n.Depth) 264 { 265 Depth = n.Depth; 266 } 267 }); 268269return Depth; 270 } 271 } 272///<summary>273/// 父节点为空引用异常 274///</summary>275publicclass NullParentNodeException : Exception 276 { 277public NullParentNodeException() 278 : base("父节点为空引用") 279 { 280281 } 282public NullParentNodeException(string message) 283 : base(message) 284 { 285286 } 287public NullParentNodeException(string message, Exception inner) 288 : base(message) 289 { 290291 } 292//public NullParentNodeException(SerializationInfo info, StreamingContext context) 293//{ 294295//}296 } 297 }
MultiHeadersColumn:多重表头和绑定的列
IDataGridHeader:定义生成表头和绑定列的数据接口
1 using System; 2 using System.Windows; 3 using System.Windows.Controls; 4 using System.Collections.Generic; 5 using System.Windows.Markup; 6 using System.Text; 7 8 namespace SLDGHeader 9 { 10 public class MultiHeadersColumn 11 { 12 #region 字段 13///<summary> 14/// 单元格边框颜色 15///</summary> 16string splitLineColor = "#ccc"; 17///<summary> 18/// 数据行宽度 19///</summary> 20string dataWidth = "30"; 21///<summary> 22/// 表头行高度 23///</summary> 24string dataHeight = "auto"; 25///<summary> 26/// 分隔线线宽度 27///</summary> 28string lineWidth = "1"; 29///<summary> 30/// 分隔符线高度 31///</summary> 32string lineHeight = "1"; 33#endregion 34#region 属性 35///<summary> 36/// 单元格边框颜色 37///</summary> 38publicstring SplitLineColor 39 { 40get { return splitLineColor; } 41set { splitLineColor = value; } 42 } 43///<summary> 44/// 数据行宽度 45///</summary> 46publicstring DataWidth 47 { 48get { return dataWidth; } 49set { dataWidth = value; } 50 } 51///<summary> 52/// 表头行高度 53///</summary> 54publicstring DataHeight 55 { 56get { return dataHeight; } 57set { dataHeight = value; } 58 } 59///<summary> 60/// 分隔线线宽度 61///</summary> 62publicstring LineWidth 63 { 64get { return lineWidth; } 65set { lineWidth = value; } 66 } 67///<summary> 68/// 分隔符线高度 69///</summary> 70publicstring LineHeight 71 { 72get { return lineHeight; } 73set { lineHeight = value; } 74 } 75#endregion 76public DataGridTemplateColumn GetMultiHeadersColumn(NTree<IDataGridHeader> node) 77 { 78 DataGridTemplateColumn col = GetTemplateColumn(node); 79 col.HeaderStyle = GetStyle("NameHeaderStyle", node); 80return col; 81 } 82 DataGridTemplateColumn GetTemplateColumn(NTree<IDataGridHeader> node) 83 { 84 List<NTree<IDataGridHeader>> nodes = new List<NTree<IDataGridHeader>>(); 85 node.Traverse((NTree<IDataGridHeader> n) => 86 { 87if (n.Children.Count == 0) 88 { 89 nodes.Add(n); 90 } 91 }); 92string strtemplate = GetDataTemplate(nodes); 93 DataTemplate template = (DataTemplate)XamlReader.Load(strtemplate); 94 95 DataGridTemplateColumn colunm = new DataGridTemplateColumn(); 96 colunm.CellTemplate = template; 97 98return colunm; 99 } 100///<summary>101/// 获取绑定列模板XML字符串 102///</summary>103///<param name="nodes">列对应节点</param>104///<returns>返回列模板XML字符串</returns>105string GetDataTemplate(List<NTree<IDataGridHeader>> nodes) 106 { 107if (nodes == null) 108 { 109thrownew ArgumentNullException("参数nodes不能为空"); 110 } 111 StringBuilder sb = new StringBuilder(200); 112113 sb.Append(@"<DataTemplate "); 114 sb.AppendLine("xmlns=‘http://schemas.microsoft.com/winfx/2006/xaml/presentation‘ "); 115 sb.AppendLine("xmlns:x=‘http://schemas.microsoft.com/winfx/2006/xaml‘ "); 116 sb.AppendLine("xmlns:sdk=‘http://schemas.microsoft.com/winfx/2006/xaml/presentation/sdk‘"); 117 sb.AppendLine(" >"); 118119 sb.AppendLine("<StackPanel Orientation=‘Horizontal‘> "); 120for (int i = 0; i < nodes.Count * 2 - 1; i++) 121 { 122if (i % 2 == 0) 123 { 124 sb.Append("<TextBlock VerticalAlignment=‘Center‘ "); 125 sb.Append(" Width=‘").Append(dataWidth).Append("‘ "); 126 sb.Append(" HorizontalAlignment=‘Center‘ TextWrapping=‘Wrap‘ Text=‘{Binding ").Append(nodes[(i + 1) / 2].Data.ColName).AppendLine("}‘ /> "); 127 } 128else129 { 130 sb.Append(" <Rectangle Fill=‘").Append(splitLineColor).Append("‘ VerticalAlignment=‘Stretch‘ "); 131 sb.Append(" Width=‘").Append(lineWidth).Append("‘ "); 132 sb.AppendLine(" /> "); 133 } 134 } 135136 sb.AppendLine("</StackPanel> "); 137 sb.AppendLine("</DataTemplate> "); 138return sb.ToString(); 139 } 140141 Style GetStyle(string headerstyle, NTree<IDataGridHeader> node) 142 { 143var depth = node.GetTreeDepth(); 144string stylestr = GetStyleStr("NameHeaderStyle", depth, node); 145 Style ste = (Style)XamlReader.Load(stylestr); 146147return ste; 148 } 149///<summary>150/// 获取表头样式XML字符串 151///</summary>152///<param name="headerstyle">样式名称</param>153///<param name="depth">树的深度</param>154///<param name="node">树的根节点</param>155///<returns>返回表头样式XML字符串</returns>156string GetStyleStr(string headerstyle, int depth, NTree<IDataGridHeader> node) 157 { 158159 StringBuilder sb = new StringBuilder(2000); 160//计算数据列数量161int colCount = node.GetCount(); 162163 AppendHeader(headerstyle, sb); 164//构建表头行列165 sb.AppendLine("<Grid HorizontalAlignment=‘{TemplateBinding HorizontalContentAlignment}‘ VerticalAlignment=‘{TemplateBinding VerticalContentAlignment}‘>"); 166//多少行167 sb.AppendLine("<Grid.RowDefinitions>"); 168//int rowCount = 3;169for (int i = 0; i < depth * 2; i++) 170 { 171if (i % 2 == 0) 172 { 173 sb.AppendLine("<RowDefinition "); 174 sb.Append(" Height=‘").Append(dataHeight).Append("‘ "); 175 sb.AppendLine(" /> "); 176 } 177else178 { 179 sb.AppendLine("<RowDefinition "); 180 sb.Append(" Height=‘").Append(lineHeight).Append("‘ "); 181 sb.AppendLine("/>"); 182 } 183 } 184 sb.AppendLine("</Grid.RowDefinitions>"); 185186//多少列187 sb.AppendLine("<Grid.ColumnDefinitions>"); 188189for (int i = 0; i < colCount * 2; i++) 190 { 191if (i % 2 == 0) 192 { 193 sb.AppendLine("<ColumnDefinition "); 194 sb.Append("Width=‘").Append(dataWidth).Append("‘ "); 195 sb.AppendLine(" />"); 196 } 197else198 { 199 sb.AppendLine("<ColumnDefinition "); 200 sb.Append("Width=‘").Append(lineWidth).Append("‘ "); 201 sb.AppendLine(" />"); 202 } 203204 } 205 sb.AppendLine("</Grid.ColumnDefinitions>"); 206207//开始构单元格208 AppendCell(node, sb, depth, colCount); 209210211 AppendEnd(sb); 212213return sb.ToString(); 214 } 215///<summary>216/// 绘制头部和表头背景 217///</summary>218///<param name="headerstyle">样式名称</param>219///<param name="sb"></param>220privatevoid AppendHeader(string headerstyle, StringBuilder sb) 221 { 222 sb.Append(@"<Style "); 223 sb.AppendLine("xmlns=‘http://schemas.microsoft.com/winfx/2006/xaml/presentation‘ "); 224 sb.AppendLine("xmlns:x=‘http://schemas.microsoft.com/winfx/2006/xaml‘ "); 225 sb.AppendLine("xmlns:dataprimitives=‘clr-namespace:System.Windows.Controls.Primitives;assembly=System.Windows.Controls.Data‘ "); 226227//列样式名称228 sb.Append(@" x:Key=‘").Append(headerstyle).Append("‘ "); 229230 sb.AppendLine(@"TargetType=‘dataprimitives:DataGridColumnHeader‘ >"); 231 sb.AppendLine("<Setter Property=‘Template‘>"); 232 sb.AppendLine("<Setter.Value>"); 233 sb.AppendLine("<ControlTemplate>"); 234 sb.AppendLine("<Grid x:Name=‘Root‘>"); 235 sb.AppendLine("<Grid.ColumnDefinitions>"); 236 sb.AppendLine("<ColumnDefinition/>"); 237 sb.AppendLine("<ColumnDefinition Width=‘Auto‘/>"); 238 sb.AppendLine("</Grid.ColumnDefinitions>"); 239 sb.AppendLine("<Rectangle x:Name=‘BackgroundRectangle‘ Fill=‘#FF1F3B53‘ Stretch=‘Fill‘ Grid.ColumnSpan=‘2‘/>"); 240 sb.AppendLine("<Rectangle x:Name=‘BackgroundGradient‘ Stretch=‘Fill‘ Grid.ColumnSpan=‘2‘>"); 241 sb.AppendLine("<Rectangle.Fill>"); 242//表头背景色243 sb.AppendLine("<LinearGradientBrush EndPoint=‘.7,1‘ StartPoint=‘.7,0‘>"); 244 sb.AppendLine(@"<GradientStop Color=‘#FCFFFFFF‘ Offset=‘0.015‘/> 245 <GradientStop Color=‘#F7FFFFFF‘ Offset=‘0.375‘/> 246 <GradientStop Color=‘#E5FFFFFF‘ Offset=‘0.6‘/> 247 <GradientStop Color=‘#D1FFFFFF‘ Offset=‘1‘/>"); 248 sb.AppendLine("</LinearGradientBrush>"); 249 sb.AppendLine("</Rectangle.Fill>"); 250 sb.AppendLine("</Rectangle>"); 251 } 252///<summary>253/// 添加结束XML部分 254///</summary>255///<param name="sb"></param>256privatevoid AppendEnd(StringBuilder sb) 257 { 258 sb.AppendLine("</Grid>"); 259//绘制最后一列分割线260 sb.AppendLine(@"<Rectangle x:Name=‘VerticalSeparator‘ Fill=‘") 261 .Append(splitLineColor) 262 .Append(@"‘ VerticalAlignment=‘Stretch‘") 263 .Append(" Width=‘").Append(lineWidth).Append("‘ ") 264 .AppendLine(" Visibility=‘Visible‘ Grid.Row=‘1‘ Grid.Column=‘1‘ />"); 265 sb.AppendLine("</Grid>"); 266 sb.AppendLine("</ControlTemplate>"); 267 sb.AppendLine("</Setter.Value>"); 268 sb.AppendLine("</Setter>"); 269 sb.AppendLine("</Style>"); 270 } 271///<summary>272/// 构建行 273/// 偶数行、偶数列为数据,奇数行、奇数列为分割线,以此计算单元格占位、合并行列 274///</summary>275///<param name="node">构建行的树结构</param>276///<param name="sb">字符串</param>277///<param name="depth">树的深度</param>278private StringBuilder AppendCell(NTree<IDataGridHeader> node, StringBuilder sb, int depth, int totalCol) 279 { 280//当前节点左兄弟节点的页节点数量,用于计算当前单元格列位置281var precolCount = 0; 282if (!node.IsRoot) 283 { 284 precolCount = GetLeftCount(node); 285 } 286287//当前节点的页节点数量288var colCount = node.GetCount(); 289290//1、数据单元格291 sb.Append(@"<ContentPresenter "); 292 sb.Append(@"Content=‘").Append(node.Data.DisplayColName).Append("‘ "); 293 sb.Append(@"VerticalAlignment=‘Center‘ HorizontalAlignment=‘Center‘ "); 294 sb.Append(" Grid.Row=‘").Append((node.Depth - 1) * 2).Append("‘ "); 295//考虑表头行可能不一致(层级有深有浅),层级较少的需要合并列显示,所有合并在最后一行表头296var rowSpan = 1; 297if (node.Children.Count == 0 && node.Depth != depth) 298 { 299 rowSpan = depth * 2 - 1 - (node.Depth - 1) * 2; 300 sb.Append(" Grid.RowSpan=‘").Append(rowSpan).Append("‘ "); 301 } 302303 sb.Append(" Grid.Column=‘").Append(precolCount * 2).Append("‘ "); 304 sb.Append("Grid.ColumnSpan=‘").Append(colCount * 2 - 1).AppendLine("‘ />"); 305306//2、行分隔线单元格307 sb.Append(@"<Rectangle Fill=‘").Append(splitLineColor).Append("‘ VerticalAlignment=‘Stretch‘ ") 308 .Append("Height=‘").Append(lineHeight).Append("‘ "); 309310//表头行合并后要调整分割线,否则会在合并单元格中间显示分割线311if (node.Children.Count == 0 && node.Depth != depth) 312 { 313//如果有合并单元,则分割线要跳过合并单元格,合并数量为(rowSpan - 1)314 sb.Append("Grid.Row=‘").Append(node.Depth * 2 - 1 + rowSpan - 1).Append("‘ "); 315 } 316else317 { 318 sb.Append("Grid.Row=‘").Append(node.Depth * 2 - 1).Append("‘ "); 319 } 320321 sb.Append(" Grid.Column=‘").Append(precolCount * 2).Append("‘ "); 322 sb.Append("Grid.ColumnSpan=‘").Append(colCount * 2).AppendLine("‘ />"); 323324//4、列分隔线单元格 325//最后一列分割线不绘制,否则在调整了后面列的列宽会出现类似空列的单元格326if ((precolCount + colCount) != totalCol) 327 { 328 sb.Append(@"<Rectangle Fill=‘").Append(splitLineColor).Append("‘ VerticalAlignment=‘Stretch‘ ") 329 .Append(" Width=‘").Append(lineWidth).Append("‘ "); 330331 sb.Append("Grid.Row=‘").Append((node.Depth - 1) * 2).Append("‘ "); 332 sb.Append("Grid.RowSpan=‘").Append(rowSpan).Append("‘ "); 333 sb.Append("Grid.Column=‘").Append((precolCount + colCount) * 2 - 1).AppendLine("‘ />"); 334 } 335336337//5、递归生成子节点单元格338if (node.Children.Count != 0) 339 { 340foreach (var item1 in node.Children) 341 { 342 AppendCell(item1, sb, depth, totalCol); 343 } 344 } 345346347return sb; 348 } 349///<summary>350/// 获取当前节点左边的叶节点数量 351///</summary>352///<param name="node"></param>353///<returns></returns>354privateint GetLeftCount(NTree<IDataGridHeader> node) 355 { 356var precolCount = 0; 357var index = node.Parent.GetChildIndex(node); 358for (int i = 0; i < index; i++) 359 { 360 precolCount += node.Parent.GetChild(i).GetCount(); 361 } 362if (!node.Parent.IsRoot) 363 { 364 precolCount += GetLeftCount(node.Parent); 365 } 366return precolCount; 367 } 368 } 369publicinterface IDataGridHeader 370 { 371///<summary>372/// 绑定列名 373///</summary>374string ColName { get; set; } 375///<summary>376/// 显示名称 377///</summary>378string DisplayColName { get; set; } 379 } 380 }
原文:http://www.cnblogs.com/missile/p/7133503.html
内容总结
以上是互联网集市为您收集整理的Silverlight多重表头实现全部内容,希望文章能够帮你解决Silverlight多重表头实现所遇到的程序开发问题。 如果觉得互联网集市技术教程内容还不错,欢迎将互联网集市网站推荐给程序员好友。
内容备注
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 gblab@vip.qq.com 举报,一经查实,本站将立刻删除。
内容手机端
扫描二维码推送至手机访问。