2012-08-29 41 views
5

我想從動態生成的單元格引用列表創建Excel.Range對象。簡化excel單元格列表以創建範圍

Excel.Range outputRange = sheet.get_Range(strCellRange, Type.Missing); 

由於strCellRange可能會變得很大,它會給出com例外。 因此,我想簡化它與範圍符號聯合。

例如

string strCellRange = "F2,G2,H2,I2,J2,K2,L2,F7,G7,H7,I7,J7,K7,L7,F12,G12,H12,I12,J12,K12,L12,F17,G17,H17,I17,J17,K17,L17,F22,G22,H22,I22,J22,K22,L22,F27,G27,H27,I27,J27,K27,L27"; 

string strCellRange = "F2:L2,F7:L7,F12:L12,F17:L17,F22:L22,F27:L27"; 
  1. 有任何Excel的方法來創建大量的單元格引用的Range對象?
  2. 是否有已知的算法來實現上述簡化(矩陣算法)?
+0

在Excel VBA中,您可以使用'Union'來完成1和2。我對如何 - 或者如果 - 我可以通過'C#執行此操作感到很難過。「 – brettdj

+0

@brettdj:是的,我可以使用application.Union(, )。但AFAIK在我的原始字符串中,逗號「,」也是聯合運算符。 –

+0

在下面看到我的更新。 rgds – brettdj

回答

3

加揚,

VBA你可以強迫你直接串到一系列與

Sub Test() 
Dim rng1 As Range 
Dim strCellRange As String 
strCellRange = "F2,G2,H2,I2,J2,K2,L2,F7,G7,H7,I7,J7,K7,L7,F12,G12,H12,I12,J12,K12,L12,F17,G17,H17,I17,J17,K17,L17,F22,G22,H22,I22,J22,K22,L22,F27,G27,H27,I27,J27,K27,L27" 
Set rng1 = Range(strCellRange) 
Set rng1 = Union(rng1, rng1) 
Debug.Print rng1.Address 
End Sub 
+0

+1。很好很簡單。 – shahkalpesh

+0

好的,但OP的問題是,給定字符串的範圍的生成由於字符串中的單元格數量而產生異常... –

+0

@brettdj:你回答了我的簡化問題,我現在可以採取自我工會每當接近細胞限制。最終,我可能會用簡化版本達到另一個限制,但我可以打破並有不同的範圍。 –

0

我不認爲有任何方法來檢索長單元格引用。我寧願採取完整的表格,然後通過編碼進行瀏覽。

1

VBA

Function Unionize(src As Range) As Range 
Dim cell As Range 
Dim unionizedRange As Range 

For Each cell In src 
    If unionizedRange Is Nothing Then 
     Set unionizedRange = cell 
    Else 
     Set unionizedRange = Union(unionizedRange, cell) 
    End If 
Next 

Set Unionize = unionizedRange 
End Function 

C#

Excel.Range Unionize(Excel.Range src) 
{ 
    Excel.Range unionizedRange; 

    foreach (Excel.Range cell in src) 
    { 
     if (unionizedRange == null) 
     { 
      unionizedRange = cell; 
     } 
     Else 
     { 
      unionizedRange = Application.Union(unionizedRange, cell); 
     } 
    } 
    return unionizedRange; 
} 

編輯(粗剪,沒通過編譯器的語法運行它):基於@ brettdj的解決方案

Excel.Range outputRange = sheet.get_Range(strCellRange, Type.Missing); 
strCellRange = Application.Union(outputRange, outputRange).Address(false, false); 
+0

根據我的理解,縮短的strCellRange版本不是OP的目標。這只是一種繞過這個事實的方法,即試圖從原始字符串中獲取範圍將引發異常 –

2

這可能是一個起點(不超出Z列的值工作,而不能識別矩形):

private string CompactRangeStringByRows(string strCellRange) 
    { 
     SortedDictionary<int, SortedList<char, char>> rows = new SortedDictionary<int, SortedList<char, char>>(); 
     foreach (string aCell in strCellRange.Split(new Char[] { ',' })) 
     { 
      char col = aCell[0]; 
      int row = int.Parse(aCell.Substring(1, aCell.Length - 1)); 
      SortedList<char, char> cols; 
      if (!rows.TryGetValue(row, out cols)) 
      { 
       cols = new SortedList<char, char>(); 
       rows[row] = cols; 
      } 
      cols.Add(col, col); 
     } 
     StringBuilder sb = new StringBuilder(); 
     bool first = true; 
     foreach (KeyValuePair<int, SortedList<char, char>> rowCols in rows) 
     { 
      char minCol = '0'; 
      char maxCol = '0'; 
      foreach (char col in rowCols.Value.Keys) 
      { 
       if (minCol == '0') 
       { 
        minCol = col; 
        maxCol = col; 
       } 
       else 
       { 
        if (col == maxCol + 1) 
         maxCol = col; 
        else 
        { 
         AddRangeString(sb, first, rowCols.Key, minCol, maxCol); 
         minCol = col; 
         maxCol = col; 
         first = false; 
        } 
       } 
      } 
      AddRangeString(sb, first, rowCols.Key, minCol, maxCol); 
      first = false; 
     } 
     return sb.ToString(); 
    } 

    private void AddRangeString(StringBuilder sb, bool first, int row, char minCol, char maxCol) 
    { 
     if (!first) 
      sb.Append(','); 
     sb.Append(minCol); 
     sb.Append(row); 
     if (maxCol != minCol) 
     { 
      sb.Append(':'); 
      sb.Append(maxCol); 
      sb.Append(row); 
     } 
    } 
+0

一旦完全開發,這將是最佳答案。非常有用的+1。 我可以用R1C1風格生成單元格引用。甚至(行^列),這將使算法非常有用。如果時間允許,肯定會嘗試。但現在我認爲brettdj的解決方案是可以的。 –

+0

開發了一種類似的算法來「手動」獲取範圍內的線條和矩形。請參閱下面的答案。 –

1

首先有一些類持有引用...

public class CellRef : IEquatable<CellRef> 
{ 
    public int Row { get; private set; } 
    public int Col { get; private set; } 

    // some more code... 
} 

public class CellRange : IEquatable<CellRange> 
{ 
    public CellRef Start { get; private set; } 
    public CellRef Stop { get; private set; } 

    // some more code... 
} 

然後算法和方法... 單元格列表需要放在一個列表中,然後排序然後傳遞給此方法。

public static string GetSimplifiedRangeString(List<CellRef> cellList) 
    { 
     #region Column wise simplify (identify lines) 

     Dictionary<CellRef, CellRef> rowRanges = new Dictionary<CellRef, CellRef>(new CellRefEqualityComparer()); 

     int currentRangeStart = 0; 
     for (int currentRangeStop = 0; currentRangeStop < cellList.Count; currentRangeStop++) 
     { 
      CellRef currentCell = cellList[currentRangeStop]; 
      CellRef previousCell = (currentRangeStop == 0) ? null : cellList[currentRangeStop - 1]; 

      bool cont = IsContigousX(currentCell, previousCell); 

      if (!cont) 
      { 
       currentRangeStart = currentRangeStop; 
      } 

      if (!rowRanges.ContainsKey(cellList[currentRangeStart])) 
       rowRanges.Add(cellList[currentRangeStart], cellList[currentRangeStop]); 
      else 
       rowRanges[cellList[currentRangeStart]] = cellList[currentRangeStop]; 
     } 

     #endregion 


     #region Row wise simplify (identify rectangles) 

     List<CellRange> rangeList = new List<CellRange>(); 
     foreach (KeyValuePair<CellRef, CellRef> range in rowRanges) 
     { 
      rangeList.Add(new CellRange(range.Key, range.Value));     
     }    
     Dictionary<CellRange, CellRange> colRanges = new Dictionary<CellRange, CellRange>(new CellRangeEqualityComparer()); 

     currentRangeStart = 0; 
     for (int currentRangeStop = 0; currentRangeStop < rangeList.Count; currentRangeStop++) 
     { 
      CellRange currentCellRange = rangeList[currentRangeStop]; 
      CellRange previousCellRange = (currentRangeStop == 0) ? null : rangeList[currentRangeStop - 1]; 

      bool cont = IsContigousY(currentCellRange, previousCellRange); 

      if (!cont) 
      { 
       currentRangeStart = currentRangeStop; 
      } 

      if (!colRanges.ContainsKey(rangeList[currentRangeStart])) 
       colRanges.Add(rangeList[currentRangeStart], rangeList[currentRangeStop]); 
      else 
       colRanges[rangeList[currentRangeStart]] = rangeList[currentRangeStop]; 
     } 

     #endregion 


     #region Simplify ranges (identify atomic lines and rectangles) 

     StringBuilder retStr = new StringBuilder(); 
     foreach (KeyValuePair<CellRange, CellRange> ranges in colRanges) 
     { 
      string rangePart = string.Empty; 
      if (ranges.Key.Equals(ranges.Value)) 
      { 
       if (ranges.Key.Start.Equals(ranges.Key.Stop)) 
       { 
        rangePart = ranges.Key.Start.ToString(); 
       } 
       else 
       { 
        rangePart = ranges.Key.ToString(); 
       } 
      } 
      else 
      { 
       rangePart = new CellRange(ranges.Key.Start, ranges.Value.Stop).ToString(); 
      } 

      if (retStr.Length == 0) 
      { 
       retStr.Append(rangePart); 
      } 
      else 
      { 
       retStr.Append("," + rangePart); 
      } 
     } 

     return retStr.ToString(); 

     #endregion 
    } 

    /// <summary> 
    /// Checks whether the given two cells represent a line. 
    /// </summary> 
    /// <param name="currentCell">Line start</param> 
    /// <param name="previousCell">Line end</param> 
    /// <returns></returns> 
    private static bool IsContigousX(CellRef currentCell, CellRef previousCell) 
    { 
     if (previousCell == null) 
      return false; 
     return (currentCell.Row == previousCell.Row) && (currentCell.Col == (previousCell.Col + 1)); 
    } 

    /// <summary> 
    /// Checks whether the given two cells represents a rectangle. 
    /// </summary> 
    /// <param name="currentCellRange">Top-left cell</param> 
    /// <param name="previousCellRange">Bottom-right cell</param> 
    /// <returns></returns> 
    private static bool IsContigousY(CellRange currentCellRange, CellRange previousCellRange) 
    { 
     if (previousCellRange == null) 
      return false; 

     bool sameVertically = (currentCellRange.Start.Col == previousCellRange.Start.Col) && (currentCellRange.Stop.Col == previousCellRange.Stop.Col); 
     bool contigous = (currentCellRange.Start.Row == currentCellRange.Stop.Row) && (previousCellRange.Start.Row == previousCellRange.Stop.Row) && ((previousCellRange.Stop.Row + 1) == currentCellRange.Stop.Row); 
     return sameVertically && contigous; 
    } 

希望這可以幫助別人。