.Net中已有现在的方法实现这些功能,不过可能是由于未完善,未把方法公开出来。只能用反射的方法去调用它。
详细信息可以查看.Net Framework 的源代码
http://referencesource.microsoft.com/#PresentationFramework/src/Framework/System/Windows/Documents/TextRangeEditTables.cs
http://referencesource.microsoft.com/#PresentationFramework/src/Framework/System/Windows/Documents/TextRange.cs
实现了以下功能:
- 合并选中的单元格
- 拆分已合并的单元格(这功能有点坑,有bug)
- 插入指定行列的表格
- 添加删除选中行
- 添加删除选中列
把调用方法封装到一个类用
1 using System; 2 using System.Linq; 3 using System.Reflection; 4 using System.Windows.Documents; 5 6 namespace WPFMergeTable 7 { 8 /// <summary> 9 /// 表格相关操作 10 /// </summary> 11 public class TextRangeEditTables 12 { 13 //-------------------------------------------------------------------------------------------------\ 14 // 15 // 通过反射获取到表格操作方法,并调用之 16 // 17 // 详细请查看.NET Framwork WPF RichTextBox 相关源代码 18 // http://referencesource.microsoft.com/#PresentationFramework/src/Framework/System/Windows/Documents/TextRangeEditTables.cs 19 // http://referencesource.microsoft.com/#PresentationFramework/src/Framework/System/Windows/Documents/TextRange.cs 20 // 21 //-------------------------------------------------------------------------------------------------// 22 23 #region 表格相关操作 24 25 /// <summary> 26 /// 获取选中单元格的第一个(左上角)和最后一个(右下角)单元格 27 /// </summary> 28 /// <param name="selection">RichTextBox.Section</param> 29 /// <param name="startCell"></param> 30 /// <param name="endCell"></param> 31 /// <returns></returns> 32 public static bool GetSelectedCells(TextSelection selection, out TableCell startCell, out TableCell endCell) 33 { 34 startCell = null; 35 endCell = null; 36 37 #region 函数原型 38 /******************************************************************************************** 39 /// <summary> 40 /// From two text positions finds out table elements involved 41 /// into building potential table range. 42 /// </summary> 43 /// <param name="anchorPosition"> 44 /// Position where selection starts. The cell at this position (if any) 45 /// must be included into a range unconditionally. 46 /// </param> 47 /// <param name="movingPosition"> 48 /// A position opposite to an anchorPosition. 49 /// </param> 50 /// <param name="includeCellAtMovingPosition"> 51 /// <see ref="TextRangeEditTables.BuildTableRange"/> 52 /// </param> 53 /// <param name="anchorCell"> 54 /// The cell at anchor position. Returns not null only if a range is not crossing table 55 /// boundary. Returns null if the range does not cross any TableCell boundary at all 56 /// or if cells crossed belong to a table whose boundary is crossed by a range. 57 /// In other words, anchorCell and movingCell are either both nulls or both non-nulls. 58 /// </param> 59 /// <param name="movingCell"> 60 /// The cell at the movingPosition. Returns not null only if a range is not crossing table 61 /// boundary. Returns null if the range does not cross any TableCell boundary at all 62 /// or if cells crossed belong to a table whose boundary is crossed by a range. 63 /// In other words, anchorCell and movingCell are either both nulls or both non-nulls. 64 /// </param> 65 /// <param name="anchorRow"></param> 66 /// <param name="movingRow"></param> 67 /// <param name="anchorRowGroup"></param> 68 /// <param name="movingRowGroup"></param> 69 /// <param name="anchorTable"></param> 70 /// <param name="movingTable"></param> 71 /// <returns> 72 /// True if at least one structural unit was found. 73 /// False if no structural units were crossed by either startPosition or endPosition 74 /// (up to their commin ancestor element). 75 /// </returns> 76 private static bool IdentifyTableElements( 77 TextPointer anchorPosition, TextPointer movingPosition, 78 bool includeCellAtMovingPosition, 79 out TableCell anchorCell, out TableCell movingCell, 80 out TableRow anchorRow, out TableRow movingRow, 81 out TableRowGroup anchorRowGroup, out TableRowGroup movingRowGroup, 82 out Table anchorTable, out Table movingTable) 83 ********************************************************************************************/ 84 #endregion 85 86 //System.Windows.Documents.TextRangeEditTables 87 Type objectType = (from asm in AppDomain.CurrentDomain.GetAssemblies() 88 from type in asm.GetTypes() 89 where type.IsClass 90 && asm.ManifestModule.Name == "PresentationFramework.dll" 91 && type.Name == "TextRangeEditTables" 92 select type).Single(); 93 //MethodInfo info = objectType.GetMethod("IdentifyTableElements", BindingFlags.NonPublic | BindingFlags.Static); 94 MethodInfo info = getNonPublicMethodInfo(objectType, "IdentifyTableElements"); 95 if (info != null) 96 { 97 object[] param = new object[11]; 98 param[0] = selection.Start; 99 param[1] = selection.End; 100 param[2] = false; 101 102 object result = info.Invoke(null, param); 103 startCell = param[3] as TableCell; 104 endCell = param[4] as TableCell; 105 return (bool)result; 106 } 107 return false; 108 } 109 110 /// <summary> 111 /// 选中单元格是否能合并 112 /// </summary> 113 /// <param name="selection">RichTextBox.Section</param> 114 /// <returns></returns> 115 public static bool CanMergeCellRange(TextSelection selection) 116 { 117 TableCell startCell = null; 118 TableCell endCell = null; 119 Type objectType = (from asm in AppDomain.CurrentDomain.GetAssemblies() 120 from type in asm.GetTypes() 121 where type.IsClass 122 && asm.ManifestModule.Name == "PresentationFramework.dll" 123 && type.Name == "TextRangeEditTables" 124 select type).Single(); 125 //MethodInfo info = objectType.GetMethod("CanMergeCellRange", BindingFlags.NonPublic | BindingFlags.Static); 126 MethodInfo info = getNonPublicMethodInfo(objectType, "CanMergeCellRange"); 127 if (info != null) 128 { 129 GetSelectedCells(selection, out startCell, out endCell); 130 if (startCell != null && endCell != null) 131 { 132 int startColumnIndex = (int)getPrivateProperty<TableCell>(startCell, "ColumnIndex"); 133 int endColumnIndex = (int)getPrivateProperty<TableCell>(endCell, "ColumnIndex"); 134 int startRowIndex = (int)getPrivateProperty<TableCell>(startCell, "RowIndex"); 135 int endRowIndex = (int)getPrivateProperty<TableCell>(endCell, "RowIndex"); 136 TableRowGroup rowGroup = getPrivateProperty<TableRow>(startCell.Parent, "RowGroup") as TableRowGroup; 137 return (bool)info.Invoke(null, new object[] { 138 rowGroup, // RowGroup 139 startRowIndex, // topRow 140 endRowIndex + endCell.RowSpan - 1, // bottomRow 141 startColumnIndex, // leftColumn 142 endColumnIndex + endCell.ColumnSpan - 1 // rightColumn 143 }); 144 } 145 } 146 return false; 147 } 148 149 /// <summary> 150 /// 合并选中表格 151 /// </summary> 152 /// <param name="selection"></param> 153 /// <returns></returns> 154 public static TextRange MergeCells(TextRange selection) 155 { 156 MethodInfo mInfo = getNonPublicMethodInfo<TextRange>("MergeCells"); 157 if (mInfo != null) 158 { 159 return mInfo.Invoke(selection, null) as TextRange; 160 } 161 return null; 162 } 163 164 /// <summary> 165 /// 拆分表格(好像还有问题。。。) 166 /// </summary> 167 /// <param name="selection"></param> 168 /// <param name="splitCountHorizontal"></param> 169 /// <param name="splitCountVertical"></param> 170 /// <returns></returns> 171 public static TextRange SplitCell(TextRange selection, int splitCountHorizontal, int splitCountVertical) 172 { 173 MethodInfo mInfo = getNonPublicMethodInfo<TextRange>("SplitCell"); 174 if (mInfo != null) 175 { 176 return mInfo.Invoke(selection, new object[] { splitCountHorizontal, splitCountVertical }) as TextRange; 177 } 178 return null; 179 } 180 181 /// <summary> 182 /// 插入表格 183 /// </summary> 184 /// <param name="selection"></param> 185 /// <param name="rowCount">行数</param> 186 /// <param name="columnCount">列数</param> 187 /// <returns></returns> 188 public static TextRange InsertTable(TextRange selection, int rowCount, int columnCount) 189 { 190 MethodInfo mInfo = getNonPublicMethodInfo<TextRange>("InsertTable"); 191 if (mInfo != null) 192 { 193 return mInfo.Invoke(selection, new object[] { rowCount, columnCount }) as TextRange; 194 } 195 return null; 196 } 197 198 /// <summary> 199 /// 在光标下插入行 200 /// </summary> 201 /// <param name="selection"></param> 202 /// <param name="rowCount">行数</param> 203 /// <returns></returns> 204 public static TextRange InsertRows(TextRange selection, int rowCount) 205 { 206 MethodInfo mInfo = getNonPublicMethodInfo<TextRange>("InsertRows"); 207 if (mInfo != null) 208 { 209 return mInfo.Invoke(selection, new object[] { rowCount }) as TextRange; 210 } 211 return null; 212 } 213 214 /// <summary> 215 /// 删除选中行 216 /// </summary> 217 /// <param name="selection"></param> 218 /// <returns></returns> 219 public static bool DeleteRows(TextRange selection) 220 { 221 MethodInfo mInfo = getNonPublicMethodInfo<TextRange>("DeleteRows"); 222 if (mInfo != null) 223 { 224 return (bool)mInfo.Invoke(selection, null); 225 } 226 return false; 227 } 228 229 /// <summary> 230 /// 在光标右边插入列 231 /// </summary> 232 /// <param name="selection"></param> 233 /// <param name="columnCount">列数</param> 234 /// <returns></returns> 235 public static TextRange InsertColumns(TextRange selection, int columnCount) 236 { 237 MethodInfo mInfo = getNonPublicMethodInfo<TextRange>("InsertColumns"); 238 if (mInfo != null) 239 { 240 return mInfo.Invoke(selection, new object[] { columnCount }) as TextRange; 241 } 242 return null; 243 } 244 245 /// <summary> 246 /// 删除选中列 247 /// </summary> 248 /// <param name="selection"></param> 249 /// <returns></returns> 250 public static bool DeleteColumns(TextRange selection) 251 { 252 MethodInfo mInfo = getNonPublicMethodInfo<TextRange>("DeleteColumns"); 253 if (mInfo != null) 254 { 255 return (bool)mInfo.Invoke(selection, null); 256 } 257 return false; 258 } 259 260 /// <summary> 261 /// 获取类中私有方法 262 /// </summary> 263 /// <param name="type"></param> 264 /// <param name="methodName"></param> 265 /// <returns></returns> 266 private static MethodInfo getNonPublicMethodInfo(Type type, string methodName) 267 { 268 MethodInfo mInfo = type 269 .GetMethod(methodName, 270 BindingFlags.NonPublic 271 | BindingFlags.Static 272 | BindingFlags.Instance); 273 return mInfo; 274 } 275 276 /// <summary> 277 /// 获取类中私有方法 278 /// </summary> 279 /// <typeparam name="T"></typeparam> 280 /// <param name="methodName"></param> 281 /// <returns></returns> 282 private static MethodInfo getNonPublicMethodInfo<T>(string methodName) 283 where T : class 284 { 285 return getNonPublicMethodInfo(typeof(T), methodName); 286 } 287 288 /// <summary> 289 /// 获取私有属性 290 /// </summary> 291 /// <typeparam name="T"></typeparam> 292 /// <param name="instance"></param> 293 /// <param name="propertyName"></param> 294 /// <returns></returns> 295 private static object getPrivateProperty<T>(object instance, string propertyName) 296 where T : class 297 { 298 object result = null; 299 PropertyInfo pInfo = typeof(T).GetProperty(propertyName, BindingFlags.NonPublic | BindingFlags.Instance); 300 if (pInfo != null) 301 { 302 result = pInfo.GetValue(instance, null); 303 } 304 return result; 305 } 306 307 #endregion 308 } 309 }