2015-06-17 40 views
2

我正在使用Excel-DNA在Excel中開發一些UDF。從Excel傳遞到我的UDF的一個參數是一個範圍。使用特定範圍時,UDF可正常工作,如「A1:C50」。下面是我的函數定義的一個示例:如何針對UsedRange剪輯ExcelReference?

[ExcelCommand()] 
public static object CalcSMA(object[,] range, int num_points) { 
    ... 
} 

不過,我得到時整列範圍傳遞,如「走出內存」錯誤「A:C」。我可以通過設置參數屬性AllowReference = TRUE避免錯誤和更改參數類型爲對象,如下面的例子:

[ExcelCommand()] 
public static object CalcSMA([ExcelArgument("Range", AllowReference=true)]object range, int num_points) { 
    ExcelReference xref = (ExcelReference)range; 
    ... 
} 

但現在我堅持想多行如何實際需要的UDF。我可以嘗試迭代工作表中的所有行,但這非常低效。有沒有辦法將ExcelReference(xref)與使用的範圍進行剪輯?我想避免使函數volatile(IsMacroType = true),但如果需要的話會這樣做。

回答

2

基於查爾斯和霍弗特的建議,我最終實現以下:

public class UsedRangeCache 
{ 
    protected static Dictionary<IntPtr, ExcelReference> _usedRanges = new Dictionary<IntPtr, ExcelReference>(); 
    protected static Application _app; 

    /// <summary> 
    /// Call this method when the XLL is initialized 
    /// </summary> 
    public static void Initialize(Application app) 
    { 
     _app = app; 
     for (int i = 0; i < app.Workbooks.Count; i++) 
     { 
      app_WorkbookOpen(app.Workbooks[i + 1]); 
     } 
     app.WorkbookOpen += app_WorkbookOpen; 
     app.WorkbookBeforeClose += app_WorkbookBeforeClose; 
     app.AfterCalculate += app_AfterCalculate; 
    } 

    // Refresh references 
    static void app_AfterCalculate() 
    { 
     for (int i = 0; i < _app.Workbooks.Count; i++) 
     { 
      UpdateCache(_app.Workbooks[i + 1]); 
     } 
    } 

    // Remove references 
    static void app_WorkbookBeforeClose(Workbook book, ref bool Cancel) 
    { 
     for (int i = 0; i < book.Worksheets.Count; i++) 
     { 
      Worksheet sheet = book.Worksheets[i + 1] as Worksheet; 
      if (sheet != null) 
      { 
       ExcelReference xref = (ExcelReference)XlCall.Excel(XlCall.xlSheetId, sheet.Name); 
       if (_usedRanges.ContainsKey(xref.SheetId)) 
       { 
        _usedRanges.Remove(xref.SheetId); 
       } 
      } 
     } 
    } 

    // Create references 
    static void app_WorkbookOpen(Workbook book) 
    { 
     UpdateCache(book); 
    } 

    // Update cache 
    private static void UpdateCache(Workbook book) 
    { 
     for (int i = 0; i < book.Worksheets.Count; i++) 
     { 
      Worksheet sheet = book.Worksheets[i + 1] as Worksheet; 
      if (sheet != null) 
      { 
       ExcelReference xref = (ExcelReference)XlCall.Excel(XlCall.xlSheetId, sheet.Name); 
       ExcelReference xused = new ExcelReference(
        sheet.UsedRange.Row, 
        sheet.UsedRange.Row + sheet.UsedRange.Rows.Count, 
        sheet.UsedRange.Column, 
        sheet.UsedRange.Column + sheet.UsedRange.Columns.Count, 
        xref.SheetId); 

       if (_usedRanges.ContainsKey(xref.SheetId)) 
       { 
        _usedRanges.Remove(xref.SheetId); 
       } 
       _usedRanges.Add(xref.SheetId, xused); 
      } 
     } 
    } 


    /// <summary> 
    /// Get used range 
    /// </summary> 
    public static ExcelReference GetUsedRange(ExcelReference xref) 
    { 
     ExcelReference ret = null; 
     _usedRanges.TryGetValue(xref.SheetId, out ret); 
     return ret; 
    } 
} 
2

在VBA(或COM)中,可以將Range參數與Range參數的Parent的UsedRange相交。但是在XLL中,由於XLL接口沒有爲工作表提供UsedRange方法,因此獲取使用的範圍並不簡單。所以你必須使用COM接口(這在XLL UDF中是有問題的)。我構建了一個使用AfterCalculate事件來緩存每個工作表使用範圍的例程。

還有就是在這裏做這個 https://fastexcel.wordpress.com/2014/09/26/getting-used-range-in-an-xll-udf-multi-threading-and-com/

注的方式進行一些討論,如果你願意讓你的UDF,你可以使用GETDOCUMENT(10)XLL API單線程宏觀型UDF。但這種痛苦可能並不值得。

+1

也看到https://groups.google.com/d/topic/exceldna/GXs6xbnSTKY/discussion - 使用UsedRange在Excel-DNA中更容易一些,因爲您可以通過調用「ExcelDnaUtil.Application」來獲得COM對象模型應用程序對象。 – Govert

+0

感謝您的快速響應。如果您按照您的建議使用了緩存後的AfterCalculate事件,那麼在打開工作簿之後,是否還需要在第一次計算期間迭代行? –

+0

我陷阱工作簿打開事件最初填充緩存。 –