WPF下的Richtextbox中实现表格合并,添加删除行列等功能

  .Net中已有现在的方法实现这些功能,不过可能是由于未完善,未把方法公开出来。只能用反射的方法去调用它。

详细信息可以查看.Net Framework 的源代码

实现了以下功能:

  • 合并选中的单元格
  • 拆分已合并的单元格(这功能有点坑,有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 }
View Code

 

posted @ 2014-09-02 11:30  我只是搬运工  阅读(1078)  评论(0编辑  收藏  举报