[C1] C1FlexGrid 行列增刪&單元格合並拆分


上一篇中實現了 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] = "1"
flex.RowHeaders[1, 0] = "1"
flex.RowHeaders[2, 0] = "1"
flex.RowHeaders.Columns[0].AllowMerging = true
 
flex.AllowMerging = AllowMerging.All;
// 為了看到效果
View Code

效果如下圖所示:

image

二  正文

現在要做一套可以靈活設置 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--
                }
            }
        }
    }
}
View Code

在自定義的 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;
  • 如果插入的列在合並范圍右側以外,則不影響該合並范圍;

在插入行時同上邏輯,樓主就不贅述了。

刪除時就比較復雜了,樓主邏輯能力欠差,就畫了一張圖表說明:

image

白色、綠色藍色均是表示選中要刪除的行(按列算,每一列算作一種情況),黃色則表格某個合並范圍;

其中紅色標注的數據表示該情況會把整個合並范圍移除;

上面這是刪除行時的情況列舉,刪除列的邏輯同理就不說了。在自定義的 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)
        }
    }
}

View Code

四  展示

樓主自然以此做了個 Demo,看看效果吧熱烈的笑臉

image


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM