上一篇中实现了 C1FlexGrid的撤销还原功能,这篇是要仿 Excel 做一个行列删除以及单元格的自由合并拆分,楼主怕在原工程里复杂的说不清道不明,所以干脆提取出来做了一个 Demo 来说明实现过程,请指教了。

一  前提概要

C1FlexGrid 中自带的 AllowMerging 属性可以控制单元格的自动合并,条件是相邻单元格的内容相同,就自动合并。

其中 Row 和 Column 还有 C1FlexGrid 本身均可设置 AllowMerging 属性,如果设置某行的 AllowMerging 属性为 true,即 _flex.Rows[i].AllowMerging = true; 则在 i 行内,如果相邻单元格内容相同时是会自动合并的,同理 _flex.Columns[j].AllowMerging = true; 也会自动处理 j 列自动合并。

C1FlexGrid 的 AllowMerging 属性是个枚举值,可选 AllAllHeadersCellsColumnHeadersNone(默认)、RowHeaders,需要注意的是,行列的 AllowMerging 属性是必须结合C1FlexGrid 的 AllowMerging 属性来使用的,比如你要设置行头和列头区域的自动合并,则需要设置 _flex.AllowMerging = AllHeaders; ,其他区域同理。

下面举一个简单的例子,看一下效果:

flex.AllowMerging = AllowMerging.Cell
flex[0, 0] = 1
flex[0, 1] = 1
flex.Rows[0].AllowMerging = true
flex[1, 1] = 3
flex[2, 1] = 3
flex.Columns[1].AllowMerging = true flex.AllowMerging = AllowMerging.ColumnHeader
flex.ColumnHeaders[0, 1] = "A"
flex.ColumnHeaders[0, 2] = "A"
flex.ColumnHeaders.Rows[0].AllowMerging = true flex.AllowMerging = AllowMerging.RowHeader
flex.RowHeaders[0, 0] = ""
flex.RowHeaders[1, 0] = ""
flex.RowHeaders[2, 0] = ""
flex.RowHeaders.Columns[0].AllowMerging = true
 
flex.AllowMerging = AllowMerging.All;

// 为了看到效果

效果如下图所示:

二  正文

现在要做一套可以灵活设置 C1FlexGrid 的合并和拆分机制,需要用到 C1FlexGrid 的 MergeManager 属性,其专门负责管理合并单元格;MergeManger 是实现了接口 IMergeManager,里面有一个方法是

public CellRange GetMergedRange(C1FlexGrid grid, CellType cellType, CellRange range)

该方法会在每次重绘单元格时自动调用,以获取合并单元格区域,从而进行处理;所以我们自己定义一个 MergeManager 来管理合并单元格。

using System.Collections.Generic
using C1.Silverlight.FlexGrid namespace SLFlexGridCellMerge
{
public class MergeManagerExt : IMergeManager
{
#region 私有变量 private List<CellRange> _mergedRanges;// 合并区域集合 #endregio #region 公开属性 /// <summary>
/// 合并单元格集合
/// </summary>
public List<CellRange> MergedRange
{
get
{
return _mergedRange
}
set
{
_mergedRanges = value
}
} #endregio #region 构造函数 /// <summary>
/// 构造函数
/// </summary>
public MergeManagerExt()
{
_mergedRanges = new List<CellRange>()
} #endregio #region 公开方法 /// <summary>
/// <para>IMergeManager接口方法</para>
/// <para>获取range所在合并区域</para>
/// </summary>
public CellRange GetMergedRange(C1FlexGrid grid, CellType cellType, CellRange range)
{
CellRange cellRange = range
if (cellType == CellType.Cell)
{
foreach (CellRange mergedRange in _mergedRanges)
{
if (mergedRange.Contains(range))
{
cellRange = mergedRange
break
}
}
} return cellRange.Normalize()
} /// <summary>
/// 获取某个选定区域所在的合并单元格区域
/// </summary>
/// <param name="selection">已选定区域</param>
/// <returns>选定区域所在的合并单元格区域</returns>
public CellRange GetMergedRange(CellRange selection)
{
CellRange cellRange = selectio
foreach (CellRange range in _mergedRanges)
{
if (range.Intersects(cellRange))
{
cellRange = cellRange.Union(range)
}
} return cellRange.Normalize()
} /// <summary>
/// 判断选区内是否有合并单元格
/// </summary>
/// <param name="selection">选区</param>
/// <returns>选区内是否有合并单元格</returns>
public bool HasMergedRange(CellRange selection)
{
bool flag = false
CellRange cellRange = GetMergedRange(selection)
foreach (CellRange item in _mergedRanges)
{
if (cellRange.Contains(item))
{
flag = true
break
}
} return flag
} /// <summary>
/// 增加合并单元格范围
/// </summary>
/// <param name="cellRange">新增要合并的单元格范围</param>
public void AddMergedRange(CellRange selection)
{
CellRange cellRange = GetMergedRange(selection)
if (!cellRange.IsSingleCell)
{
bool isIn = false;// 是否已经包含在合并单元格中
for (int i = 0; i < _mergedRanges.Count; i++)
{
// 新增的合并区域包含了已经合并的单元格
if (cellRange.Contains(_mergedRanges[i]))
{
_mergedRanges.RemoveAt(i)
i--
}
else if (_mergedRanges[i].Contains(cellRange))
{
isIn = true
}
}
if (!isIn)
{
_mergedRanges.Add(cellRange.Normalize())
}
}
} /// <summary>
/// 拆分单元格
/// </summary>
/// <param name="mergedRange">需要拆分的单元格范围</param>
public void RemoveMergedRange(CellRange selection)
{
CellRange cellRange = GetMergedRange(selection)
for (int i = 0; i < _mergedRanges.Count; i++)
{
if (cellRange.Intersects(_mergedRanges[i]))
{
_mergedRanges.RemoveAt(i)
i--
}
}
}
}
}

在自定义的 MergeManagerExt 中,利用一个 List 来管理合并区域,然后在接口 IMergeManager 的方法 GetMergedRange 中,根据重绘时扫描到的 range (参数),从 List 中查找包含该 range 的合并区域并返回。

然后就可以将 C1FlexGrid 的 Selection 通过方法 AddMergeRange 和 RemoveMergeRange 添加或移除到合并区域集合(List),进行管理,C1FlexGrid 则在每次重绘单元格时通过接口方法 GetMergedRange 获取合并区域集合进行合并处理,这样就可以达到灵活设置单元格的合并和拆分了。

三  扩展

合并区域集合是一个 List<CellRange> 类型,其中 CellRange 简单的记录了 LeftColumn, TopRow, RightColumn, BottomRow 四个整型值,以标记出范围的左上角和右下角坐标。这样会导致一个问题,就是如果 C1FlexGrid 的行列数目已经固定下来了,不再增删,自然可用;但是如果 C1FlexGrid 的行列也是动态增删,此时合并集合中的 CellRange 所标记的范围坐标并没有即时更新,导致在行列增删后,合并范围移位或者超出 C1FlexGrid 范围。

解决方法是在进行行列增删时,同步更新合并区域集合中的 CellRange。

在插入列时:

  • 如果插入的列在合并范围左侧(包括合并范围左列),则将合并范围整体右移1列;
  • 如果插入的列在合并范围之间(不包括合并范围左列,包括合并范围右列),则将合并范围扩张1列,其中左列不动,右列+1;
  • 如果插入的列在合并范围右侧以外,则不影响该合并范围;

在插入行时同上逻辑,楼主就不赘述了。

删除时就比较复杂了,楼主逻辑能力欠差,就画了一张图表说明:

白色、绿色和蓝色均是表示选中要删除的行(按列算,每一列算作一种情况),黄色则表格某个合并范围;

其中红色标注的数据表示该情况会把整个合并范围移除;

上面这是删除行时的情况列举,删除列的逻辑同理就不说了。在自定义的 MergeManagerExt 中增加几个更新合并范围的方法:

/// <summary>
/// 插入列时,与其相关的合并单元格范围更新
/// </summary>
/// <param name="colIndex">插入列的索引位置</param>
public void InsertColumnUpdate(int colIndex)
{
for (int i = 0; i < _mergedRanges.Count; i++)
{
if (_mergedRanges[i].LeftColumn >= colIndex)
{
int top = _mergedRanges[i].TopRow
int left = _mergedRanges[i].LeftColumn + 1
int bottom = _mergedRanges[i].BottomRow
int right = _mergedRanges[i].RightColumn + 1
_mergedRanges[i] = new CellRange(top, left, bottom, right)
}
else if (_mergedRanges[i].LeftColumn < colIndex && colIndex <= _mergedRanges[i].RightColumn)
{
int top = _mergedRanges[i].TopRow
int left = _mergedRanges[i].LeftColum
int bottom = _mergedRanges[i].BottomRow
int right = _mergedRanges[i].RightColumn + 1
_mergedRanges[i] = new CellRange(top, left, bottom, right)
}
}
} /// <summary>
/// 插入行时,与其相关的合并单元格范围更新
/// </summary>
/// <param name="rowIndex">插入行的索引位置</param>
public void InsertRowUpdate(int rowIndex)
{
for (int i = 0; i < _mergedRanges.Count; i++)
{
if (_mergedRanges[i].TopRow >= rowIndex)
{
int top = _mergedRanges[i].TopRow + 1
int left = _mergedRanges[i].LeftColum
int bottom = _mergedRanges[i].BottomRow + 1
int right = _mergedRanges[i].RightColum
_mergedRanges[i] = new CellRange(top, left, bottom, right)
}
else if (_mergedRanges[i].TopRow < rowIndex && rowIndex <= _mergedRanges[i].BottomRow)
{
int top = _mergedRanges[i].TopRow
int left = _mergedRanges[i].LeftColum
int bottom = _mergedRanges[i].BottomRow + 1
int right = _mergedRanges[i].RightColum
_mergedRanges[i] = new CellRange(top, left, bottom, right)
}
}
} /// <summary>
/// 删除选定区域的行时,更新MergeManager内相对应的合并单元区域
/// </summary>
/// <param name="selection">当前选定区域所在的行区域</param>
public void DeleteRowsUpdate(CellRange selectedRows)
{
for (int i = 0; i < _mergedRanges.Count; i++)
{
if (_mergedRanges[i].BottomRow >= selectedRows.TopRow)
{
CellRange intersection = _mergedRanges[i].Intersection(selectedRows)
int topRow = _mergedRanges[i].TopRow
int bottomRow = _mergedRanges[i].BottomRow
if (_mergedRanges[i].TopRow <= selectedRows.TopRow)
{
topRow = _mergedRanges[i].TopRow
bottomRow = _mergedRanges[i].BottomRow - intersection.RowSpa
}
else
{
if (intersection.IsValid)
{
topRow = selectedRows.TopRow
}
else
{
topRow = _mergedRanges[i].TopRow - selectedRows.RowSpa
}
bottomRow = _mergedRanges[i].BottomRow - selectedRows.RowSpa
}
if (topRow > bottomRow ||
((topRow == bottomRow) && _mergedRanges[i].ColumnSpan == 1))
{
_mergedRanges.RemoveAt(i)
i--
continue
}
_mergedRanges[i] = new CellRange(topRow, _mergedRanges[i].LeftColumn, bottomRow, _mergedRanges[i].RightColumn)
}
}
} /// <summary>
/// 删除选定区域的列时,更新MergeManager内相对应的合并单元区域
/// </summary>
/// <param name="selection">当前选中的区域</param>
public void DeleteColumnsUpdate(CellRange selectedColumns)
{
for (int i = 0; i < _mergedRanges.Count; i++)
{
if (_mergedRanges[i].RightColumn >= selectedColumns.LeftColumn)
{
CellRange intersection = _mergedRanges[i].Intersection(selectedColumns)
int leftColumn = _mergedRanges[i].LeftColum
int rightColumn = _mergedRanges[i].RightColum
if (_mergedRanges[i].LeftColumn <= selectedColumns.LeftColumn)
{
leftColumn = _mergedRanges[i].LeftColum
rightColumn = _mergedRanges[i].RightColumn - intersection.ColumnSpa
}
else
{
if (intersection.IsValid)
{
leftColumn = selectedColumns.LeftColum
}
else
{
leftColumn = _mergedRanges[i].LeftColumn - selectedColumns.ColumnSpa
}
rightColumn = _mergedRanges[i].RightColumn - selectedColumns.ColumnSpa
} if (leftColumn > rightColumn ||
((leftColumn == rightColumn) && _mergedRanges[i].RowSpan == 1))
{
_mergedRanges.RemoveAt(i)
i--
continue
}
_mergedRanges[i] = new CellRange(_mergedRanges[i].TopRow, leftColumn, _mergedRanges[i].BottomRow, rightColumn)
}
}
}

四  展示

楼主自然以此做了个 Demo,看看效果吧

[C1] C1FlexGrid 行列增删&单元格合并拆分的更多相关文章

  1. excel技巧--单元格合并与拆分

    如果要将上图的地区列做成下图的合并单一列: 有如下做法: (以下图表格为例) 1.选择要排序的表格,点击“开始”-->排序和筛选-->自定义排序.在对话框选择“业务项目”进行排序: 2.选 ...

  2. Excel单元格内容拆分、合并

    例:如何将EXCEL单元格A1中的“1-2-1”,在B1.C1.D1单元格中分别显示”1“.”2“.”1“.方法一: 在B1中输入“=mid(A1,1,1)”在C1中输入“=mid(AI,3,1)”在 ...

  3. ExtJS 4.2 Grid组件的单元格合并

    ExtJS 4.2 Grid组件本身并没有提供单元格合并功能,需要自己实现这个功能. 目录 1. 原理 2. 多列合并 3. 代码与在线演示 1. 原理 1.1 HTML代码分析 首先创建一个Grid ...

  4. NPOI 教程 - 2.1单元格合并

    来源:http://liyingchun343333.blog.163.com/blog/static/3579731620091018212990/ 合并单元格在制作表格时很有用,比如说表格的标题就 ...

  5. asp.net使用控件datagrid实现表头单元格合并

    合并的要点: 1.datagid的单元格合并原理是table中tr,td的布局实现; 2.合并的时机实在其datagridcreate事件中实现; 3.认识一个对象TableCellCollectio ...

  6. DataGridView单元格合并

    本文章转载:http://www.cnblogs.com/xiaofengfeng/p/3382094.html 图: 代码就是如此简单 文件下载:DataGridView单元格合并源码 也可以参考: ...

  7. devexpress实现单元格合并以及依据条件合并单元格

    1.devexpress实现单元格合并非常的简单,只要设置属性[AllowCellMerge=True]就可以了,实现效果如下图: 2.但是在具体要求中并非需要所有的相同单元格都合并,可能需要其他的条 ...

  8. SNF快速开发平台MVC-表格单元格合并组件

    1.   表格单元格合并组件 1.1.      效果展示 1.1.1.    页面展现表格合并单元格 图 4.1 1.1.2.    导出excel合并单元格 图 4.2 1.2.      调用说 ...

  9. 关于table动态添加数据 单元格合并 数组合并

    var newArr = [ {"BranchID":1,"BranchName":"城二","BranchFullName&qu ...

随机推荐

  1. OpenCV模板匹配算法详解

    1 理论介绍 模板匹配是在一幅图像中寻找一个特定目标的方法之一,这种方法的原理非常简单,遍历图像中的每一个可能的位置,比较各处与模板是否“相似”,当相似度足够高时,就认为找到了我们的目标.OpenCV ...

  2. c#语言规范

    0x00 分类 C#语言规范主要有两个来源,即我们熟知的ECMA规范和微软的规范.尽管C#的ECMA规范已经前后修订4次,但其内容仅仅到C# 2.0为止.所以慕容为了方便自己和各位方便查询,在此将常见 ...

  3. BI分析受阻?FineBI推出SPA螺旋式分析新功能!

    过去,企业级的数据分析通常会有这么几种场景,业务部门托信息部门分析数据,结果报表一出,唇枪舌剑争论你我高低,数据不准,指标不对.信息部门欠缺业务概念,业务部门不懂技术逻辑,数据分析之路,暂时搁浅. 后 ...

  4. Android游戏开发实践(1)之NDK与JNI开发03

    Android游戏开发实践(1)之NDK与JNI开发03 前面已经分享了两篇有关Android平台NDK与JNI开发相关的内容.以下列举前面两篇的链接地址,感兴趣的可以再回顾下.那么,这篇继续这个小专 ...

  5. [转]ThinkPHP中实例化对象M()和D()的区别,select和find的区别

    1.ThinkPHP中实例化对象M()和D()的区别 在实例化的过程中,经常使用D方法和M方法,这两个方法的区别在于M方法实例化模型无需用户为每个数据表定义模型类,如果D方法没有找到定义的模型类,则会 ...

  6. java根据html生成摘要

    转自:http://java.freesion.com/article/48772295755/ 开发一个系统,需要用到这个,根据html生成你指定多少位的摘要 package com.chendao ...

  7. 一步步开发自己的博客 .NET版(3、注册登录功能)

    前言 这次开发的博客主要功能或特点:    第一:可以兼容各终端,特别是手机端.    第二:到时会用到大量html5,炫啊.    第三:导入博客园的精华文章,并做分类.(不要封我)    第四:做 ...

  8. 【腾讯Bugly干货分享】Android性能优化典范——第6季

    本文来自于腾讯bugly开发者社区,非经作者同意,请勿转载,原文地址:http://dev.qq.com/topic/580d91208d80e49771f0a07c 导语 这里是Android性能优 ...

  9. 死去活来,而不变质:Domain Model(领域模型) 和 EntityFramework 如何正确进行对象关系映射?

    写在前面 阅读目录: 设计误区 数据库已死 枚举映射 关联映射 后记 在上一篇<一缕阳光:DDD(领域驱动设计)应对具体业务场景,如何聚焦 Domain Model(领域模型)?>博文中, ...

  10. CSharpGL(38)带初始数据创建Vertex Buffer Object的情形汇总

    CSharpGL(38)带初始数据创建Vertex Buffer Object的情形汇总 开始 总的来说,OpenGL应用开发者会遇到为如下三种数据创建Vertex Buffer Object的情形: ...