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

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

实现了以下功能:

  • 合并选中的单元格
  • 拆分已合并的单元格(这功能有点坑,有bug)
  • 插入指定行列的表格
  • 添加删除选中行
  • 添加删除选中列

把调用方法封装到一个类用

 using System;
using System.Linq;
using System.Reflection;
using System.Windows.Documents; namespace WPFMergeTable
{
/// <summary>
/// 表格相关操作
/// </summary>
public class TextRangeEditTables
{
//-------------------------------------------------------------------------------------------------\\
//
// 通过反射获取到表格操作方法,并调用之
//
// 详细请查看.NET Framwork WPF RichTextBox 相关源代码
// http://referencesource.microsoft.com/#PresentationFramework/src/Framework/System/Windows/Documents/TextRangeEditTables.cs
// http://referencesource.microsoft.com/#PresentationFramework/src/Framework/System/Windows/Documents/TextRange.cs
//
//-------------------------------------------------------------------------------------------------// #region 表格相关操作 /// <summary>
/// 获取选中单元格的第一个(左上角)和最后一个(右下角)单元格
/// </summary>
/// <param name="selection">RichTextBox.Section</param>
/// <param name="startCell"></param>
/// <param name="endCell"></param>
/// <returns></returns>
public static bool GetSelectedCells(TextSelection selection, out TableCell startCell, out TableCell endCell)
{
startCell = null;
endCell = null; #region 函数原型
/********************************************************************************************\
/// <summary>
/// From two text positions finds out table elements involved
/// into building potential table range.
/// </summary>
/// <param name="anchorPosition">
/// Position where selection starts. The cell at this position (if any)
/// must be included into a range unconditionally.
/// </param>
/// <param name="movingPosition">
/// A position opposite to an anchorPosition.
/// </param>
/// <param name="includeCellAtMovingPosition">
/// <see ref="TextRangeEditTables.BuildTableRange"/>
/// </param>
/// <param name="anchorCell">
/// The cell at anchor position. Returns not null only if a range is not crossing table
/// boundary. Returns null if the range does not cross any TableCell boundary at all
/// or if cells crossed belong to a table whose boundary is crossed by a range.
/// In other words, anchorCell and movingCell are either both nulls or both non-nulls.
/// </param>
/// <param name="movingCell">
/// The cell at the movingPosition. Returns not null only if a range is not crossing table
/// boundary. Returns null if the range does not cross any TableCell boundary at all
/// or if cells crossed belong to a table whose boundary is crossed by a range.
/// In other words, anchorCell and movingCell are either both nulls or both non-nulls.
/// </param>
/// <param name="anchorRow"></param>
/// <param name="movingRow"></param>
/// <param name="anchorRowGroup"></param>
/// <param name="movingRowGroup"></param>
/// <param name="anchorTable"></param>
/// <param name="movingTable"></param>
/// <returns>
/// True if at least one structural unit was found.
/// False if no structural units were crossed by either startPosition or endPosition
/// (up to their commin ancestor element).
/// </returns>
private static bool IdentifyTableElements(
TextPointer anchorPosition, TextPointer movingPosition,
bool includeCellAtMovingPosition,
out TableCell anchorCell, out TableCell movingCell,
out TableRow anchorRow, out TableRow movingRow,
out TableRowGroup anchorRowGroup, out TableRowGroup movingRowGroup,
out Table anchorTable, out Table movingTable)
\********************************************************************************************/
#endregion //System.Windows.Documents.TextRangeEditTables
Type objectType = (from asm in AppDomain.CurrentDomain.GetAssemblies()
from type in asm.GetTypes()
where type.IsClass
&& asm.ManifestModule.Name == "PresentationFramework.dll"
&& type.Name == "TextRangeEditTables"
select type).Single();
//MethodInfo info = objectType.GetMethod("IdentifyTableElements", BindingFlags.NonPublic | BindingFlags.Static);
MethodInfo info = getNonPublicMethodInfo(objectType, "IdentifyTableElements");
if (info != null)
{
object[] param = new object[];
param[] = selection.Start;
param[] = selection.End;
param[] = false; object result = info.Invoke(null, param);
startCell = param[] as TableCell;
endCell = param[] as TableCell;
return (bool)result;
}
return false;
} /// <summary>
/// 选中单元格是否能合并
/// </summary>
/// <param name="selection">RichTextBox.Section</param>
/// <returns></returns>
public static bool CanMergeCellRange(TextSelection selection)
{
TableCell startCell = null;
TableCell endCell = null;
Type objectType = (from asm in AppDomain.CurrentDomain.GetAssemblies()
from type in asm.GetTypes()
where type.IsClass
&& asm.ManifestModule.Name == "PresentationFramework.dll"
&& type.Name == "TextRangeEditTables"
select type).Single();
//MethodInfo info = objectType.GetMethod("CanMergeCellRange", BindingFlags.NonPublic | BindingFlags.Static);
MethodInfo info = getNonPublicMethodInfo(objectType, "CanMergeCellRange");
if (info != null)
{
GetSelectedCells(selection, out startCell, out endCell);
if (startCell != null && endCell != null)
{
int startColumnIndex = (int)getPrivateProperty<TableCell>(startCell, "ColumnIndex");
int endColumnIndex = (int)getPrivateProperty<TableCell>(endCell, "ColumnIndex");
int startRowIndex = (int)getPrivateProperty<TableCell>(startCell, "RowIndex");
int endRowIndex = (int)getPrivateProperty<TableCell>(endCell, "RowIndex");
TableRowGroup rowGroup = getPrivateProperty<TableRow>(startCell.Parent, "RowGroup") as TableRowGroup;
return (bool)info.Invoke(null, new object[] {
rowGroup, // RowGroup
startRowIndex, // topRow
endRowIndex + endCell.RowSpan - , // bottomRow
startColumnIndex, // leftColumn
endColumnIndex + endCell.ColumnSpan - // rightColumn
});
}
}
return false;
} /// <summary>
/// 合并选中表格
/// </summary>
/// <param name="selection"></param>
/// <returns></returns>
public static TextRange MergeCells(TextRange selection)
{
MethodInfo mInfo = getNonPublicMethodInfo<TextRange>("MergeCells");
if (mInfo != null)
{
return mInfo.Invoke(selection, null) as TextRange;
}
return null;
} /// <summary>
/// 拆分表格(好像还有问题。。。)
/// </summary>
/// <param name="selection"></param>
/// <param name="splitCountHorizontal"></param>
/// <param name="splitCountVertical"></param>
/// <returns></returns>
public static TextRange SplitCell(TextRange selection, int splitCountHorizontal, int splitCountVertical)
{
MethodInfo mInfo = getNonPublicMethodInfo<TextRange>("SplitCell");
if (mInfo != null)
{
return mInfo.Invoke(selection, new object[] { splitCountHorizontal, splitCountVertical }) as TextRange;
}
return null;
} /// <summary>
/// 插入表格
/// </summary>
/// <param name="selection"></param>
/// <param name="rowCount">行数</param>
/// <param name="columnCount">列数</param>
/// <returns></returns>
public static TextRange InsertTable(TextRange selection, int rowCount, int columnCount)
{
MethodInfo mInfo = getNonPublicMethodInfo<TextRange>("InsertTable");
if (mInfo != null)
{
return mInfo.Invoke(selection, new object[] { rowCount, columnCount }) as TextRange;
}
return null;
} /// <summary>
/// 在光标下插入行
/// </summary>
/// <param name="selection"></param>
/// <param name="rowCount">行数</param>
/// <returns></returns>
public static TextRange InsertRows(TextRange selection, int rowCount)
{
MethodInfo mInfo = getNonPublicMethodInfo<TextRange>("InsertRows");
if (mInfo != null)
{
return mInfo.Invoke(selection, new object[] { rowCount }) as TextRange;
}
return null;
} /// <summary>
/// 删除选中行
/// </summary>
/// <param name="selection"></param>
/// <returns></returns>
public static bool DeleteRows(TextRange selection)
{
MethodInfo mInfo = getNonPublicMethodInfo<TextRange>("DeleteRows");
if (mInfo != null)
{
return (bool)mInfo.Invoke(selection, null);
}
return false;
} /// <summary>
/// 在光标右边插入列
/// </summary>
/// <param name="selection"></param>
/// <param name="columnCount">列数</param>
/// <returns></returns>
public static TextRange InsertColumns(TextRange selection, int columnCount)
{
MethodInfo mInfo = getNonPublicMethodInfo<TextRange>("InsertColumns");
if (mInfo != null)
{
return mInfo.Invoke(selection, new object[] { columnCount }) as TextRange;
}
return null;
} /// <summary>
/// 删除选中列
/// </summary>
/// <param name="selection"></param>
/// <returns></returns>
public static bool DeleteColumns(TextRange selection)
{
MethodInfo mInfo = getNonPublicMethodInfo<TextRange>("DeleteColumns");
if (mInfo != null)
{
return (bool)mInfo.Invoke(selection, null);
}
return false;
} /// <summary>
/// 获取类中私有方法
/// </summary>
/// <param name="type"></param>
/// <param name="methodName"></param>
/// <returns></returns>
private static MethodInfo getNonPublicMethodInfo(Type type, string methodName)
{
MethodInfo mInfo = type
.GetMethod(methodName,
BindingFlags.NonPublic
| BindingFlags.Static
| BindingFlags.Instance);
return mInfo;
} /// <summary>
/// 获取类中私有方法
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="methodName"></param>
/// <returns></returns>
private static MethodInfo getNonPublicMethodInfo<T>(string methodName)
where T : class
{
return getNonPublicMethodInfo(typeof(T), methodName);
} /// <summary>
/// 获取私有属性
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="instance"></param>
/// <param name="propertyName"></param>
/// <returns></returns>
private static object getPrivateProperty<T>(object instance, string propertyName)
where T : class
{
object result = null;
PropertyInfo pInfo = typeof(T).GetProperty(propertyName, BindingFlags.NonPublic | BindingFlags.Instance);
if (pInfo != null)
{
result = pInfo.GetValue(instance, null);
}
return result;
} #endregion
}
}

WPF下的Richtextbox中实现表格合并,添加删除行列等功能的更多相关文章

  1. js 表格操作----添加删除

    js 表格操作----添加删除 书名:<input type="text" id="name"> 价格:<input type="t ...

  2. [转载]EasyUI中数据表格DataGrid添加排序功能

    我们这里演示的是EasyUI数据表格DataGrid从服务器端排序功能,因为觉的本地数据排序没有多大的作用,一般我们DataGrid不会读取全部数据,只会读取当前页的数据,所以本地数据排序也只是对当前 ...

  3. 编辑 Ext 表格(一)——— 动态添加删除行列

    一.动态增删行 在 ext 表格中,动态添加行主要和表格绑定的 store 有关, 通过对 store 数据集进行添加或删除,就能实现表格行的动态添加删除.   (1) 动态添加表格的行  gridS ...

  4. vue+element项目中动态表格合并

    需求:elementui里的table虽然有合并函数(:span-method),单基本都是设置固定值合并.现在有一个树型结构的数据,要求我们将里面的某个list和其他属性一起展开展示,并且list中 ...

  5. ant design 中实现表格头部可删除和添加

    我是用antd pro做一个项目.有一个小需求是表格头部栏可操作.具体是表头的每一项都带一个"x"按钮,当不想展示这一栏的时候,直接点"x",这一栏就不展示了. ...

  6. Javascript中DataGrid表格纵线添加数据

    接之前写的一篇博客http://www.cnblogs.com/Liu30/p/7229641.html,生成一个6*24的表格之后,添加数据 表格数据一般都是按行添加,我所做的这个表格是想添加一天2 ...

  7. ios中tableview的移动添加删除

    // // MJViewController.m // UITableView-编辑模式 // // Created by mj on 13-4-11. // Copyright (c) 2013年 ...

  8. File类中的一些属性 添加删除文件夹

    import java.io.File; import java.io.IOException; public class FileD { public static void main(String ...

  9. Mac OS X中Launchpad的图标添加删除方法(添加方法别试了,和Linux很大区别)

    说明:在Mac下的Launchpad图标添加和删除都与应用程序的app文件有关,如果单纯的只想在Launchpad添加自定义的图标,然后指定要某条命令运行时,建议不要这么干,Launchpad的图标管 ...

随机推荐

  1. Java直接内存与堆内存

    NIO的Buffer提供了一个可以不经过JVM内存直接访问系统物理内存的类——DirectBuffer. DirectBuffer类继承自ByteBuffer,但和普通的ByteBuffer不同,普通 ...

  2. 认识Android Service

    一.使用Service 1.什么是Service以及Service的配置项:  Service是Android组件的一种,派生自android.app.service类.它不提供交互界面,只是在后台运 ...

  3. socket (一)

    socket通常也称作"套接字",用于描述IP地址和端口,是一个通信链的句柄,应用程序通常通过"套接字"向网络发出请求或者应答网络请求. socket起源于Un ...

  4. vim的编译安装及其插件YouCompleteMe安装

    相关的环境: win 7 x64 vs2013 community python 2.7.10 AMD64 python 3.5 AMD64 LLVM 3.5 cmake 3.5   YouCompl ...

  5. [IOS]JSPatch

    用途 修复线上出现的紧急crash,热更新 例子 demo 原理解读 在程序didFinishLaunch时候执行,[JPEngine startEngine], startEngine做了对解析js ...

  6. SQLServer日期函数用法

    --1.显示本月第一天 ,) ),)) --2.显示本月最后一天 ,),,))) ,,,)) --3.上个月的最后一天 ,,)) --4.本月的第一个星期一 , ) --5.本年的第一天 ,) --6 ...

  7. Python-pandas

    Python-pandas Python 中处理时间序列的主要工具是 pandas 库. 1.pannas 基础 1.1使用 DataFrame 类的第一步 #!/etc/bin/python #co ...

  8. Netty里的设计模式

    最近在撸 Netty 源码,发现了一些模式,顺手做个笔记. 分析版本是4.0 1. 构造器模式 ServerBootstrap 和 Bootstrap 的构建 2. 责任链设计模式 pipeline ...

  9. Sicily 1151: 简单的马周游问题(DFS)

    这道题嘛,直接使用DFS搜索,然后莫名其妙地AC了.后来看了题解,说是move的顺序不同的话可能会导致超时,这时便需要剪枝,真是有趣.原来自己是误打误撞AC了,hhh.题解还有另一种解法是先把一条完整 ...

  10. TBitmapSurface.StretchFrom

    procedure TBitmapSurface.StretchFrom(const Source: TBitmapSurface; const NewWidth, NewHeight: Intege ...