.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. thinkphp 3.2 linux二级目录安装

    详解:http://document.thinkphp.cn/manual_3_2.html#url_rewrite 注意:linux系统对大小写敏感 服务器系统:linux (阿里云服务器) thi ...

  2. RabbitMQ学习系列(一): 介绍

    1. 介绍 RabbitMQ是一个由erlang开发的基于AMQP(Advanced Message Queue )协议的开源实现.用于在分布式系统中存储转发消息,在易用性.扩展性.高可用性等方面都非 ...

  3. 【CentOS7之防火墙命令】

    1.CentOS7的发现带了很多行的指令和新的技术.并且在帮助的中文解释也增多了很多这意味着Linux在中国的发展越来越呈现普及.今天来介绍下CentOS7的新防火墙firewall. 更多的可查看: ...

  4. linux文件及文件夹权限

    以前上学的时候简单了解了下linux,现在对它的认识也只停留在一些小白命令上.以为我只要不是做服务器端开发的应该不会去碰吧,工作了才知道做开发的多少还是要熟悉一些linux的,至少你的项目会部署在li ...

  5. 移动Web初级入门

    最好的阅读是输出. –玉伯 即将开始涉入移动Web了,有点小兴奋也有点小紧张,希望能在未来的团队里带来一些价值.记录一下我现在所认识的移动Web. 原文摘自我的前端博客,欢迎大家来访问 原文地址:ht ...

  6. tomcat server.xml中文版

    原文:http://www.blogjava.net/baoyaer/articles/107278.html Tomcat Server的结构图 该文件描述了如何启动Tomcat Server &l ...

  7. -webkit-box-flex被内容撑开了

    <!doctype html> <html> <head> <meta charset="utf-8"> <title> ...

  8. 优先队列实现Huffman编码

    首先把所有的字符加入到优先队列,然后每次弹出两个结点,用这两个结点作为左右孩子,构造一个子树,子树的跟结点的权值为左右孩子的权值的和,然后将子树插入到优先队列,重复这个步骤,直到优先队列中只有一个结点 ...

  9. 掌握 Linux PC 性能之基准测试

    导读 基准测试是一项测试或一系列测试,用来确定某个计算机硬件运行起来的状况有多好:在许多情况下,“基准测试”实际上等同于“压力测试”,通过测试硬件的极限,然后可以将测得的结果与其他硬件测得的结果作一番 ...

  10. svg + d3

    为了实现元素的添加,删除,拖拽,左键点击,右键单击,悬浮等功能,使用了d3 + svg 的技术来实现界面. 最开始是采用canvas,但是由于功能原因放弃了该技术,可以看下 canvas简介 另附:c ...