【问题标题】:This command requires at least two rows of source data此命令至少需要两行源数据
【发布时间】:2012-10-05 23:20:17
【问题描述】:

我收到此错误:

This command requires at least two rows of source data. You cannot use the command on a selection in only one row. Try the following:

- If you're using an advanced filter, select a range of cells that contains at least two rows of data. Then click the Advanced Filter command again.
- I you're creating a PivotTable, type a cell reference or select a range that includes at least two rows of data

断断续续地在这行代码上:

xlWorkBook.RefreshAll();

有两个工作表。一个有数据透视表,一个有原始数据。有时只有一行数据。对于多行数据,上面的代码行总是有效;但是,只有 1 行数据,上面的代码有时可以工作,有时我会收到上面的错误消息。

除此之外,包含数据透视表的工作表刷新;但是,如果我重新打开文件,它也不会刷新,除非我手动显式刷新它。

这里发生了什么?为什么我只是有时会收到此错误?

非常感谢您的指导。

如果有帮助,我将包括整个方法:

private void SortandCreateFile(string column, string email, string emailStartPos) {
    string replacetext = "";

    try {
        var valueRange = xlWorkSheet.get_Range(column + emailStartPos, column + range.Rows.Count.ToString());
        var deleteRange = valueRange;
        xlApp.Visible = false;
        int startpos = 0;
        int endPos=0;
        bool foundStart = false;

        Excel.Range rng = xlWorkSheet.get_Range(column + "1", column + range.Rows.Count.ToString());

        string tempstring = "d";
        int INTemailStartPos = Convert.ToInt16(emailStartPos);

        for (int rCnt = INTemailStartPos; rCnt <= rng.Count; rCnt++) {
            Excel.Range cell = (Excel.Range)rng[rCnt, 1];

            try {
                if (cell.Value2 != null)
                    tempstring = cell.Value2.ToString();
                else {
                    startpos = rCnt;
                    releaseObject(cell);  /////////
                    break;
                }
            }
                catch (Exception ee)
            {
            MessageBox.Show(ee.ToString());
        }
        //grab the text from column link texdtbox
        Excel.Range rngLinkColumn;
        Excel.Range replacetextcell=null;

        if (FormControls.ColumnLink.Length > 0) {
            rngLinkColumn = xlWorkSheet.get_Range(FormControls.ColumnLink + "1", FormControls.ColumnLink + range.Rows.Count.ToString());
            replacetextcell = (Excel.Range)rngLinkColumn[rCnt, 1];
        }    
        //locate email
        if (cell.Value2.ToString() == email ) {
            //we found the starting position of the email we want!
            //this will tell us which row of data to start from
            startpos = rCnt;

            if (FormControls.ColumnLink.Length > 0)
                replacetext = replacetextcell.Value2.ToString();
            releaseObject(cell);  /////////
            break;
        }
        releaseObject(cell);
    }
    int foundstartminusONE = startpos - 1;
    int rngcount = rng.Count + INTemailStartPos;

    //delete everything from the top UNTIL the row of the email address that we need
    if (startpos != INTemailStartPos) {
        deleteRange = xlWorkSheet.get_Range(column + INTemailStartPos.ToString() + ":" + "CF" + foundstartminusONE.ToString(), Type.Missing);
        deleteRange = deleteRange.EntireRow;
        deleteRange.Delete(Excel.XlDeleteShiftDirection.xlShiftUp);
    }

    for (int rCnt = INTemailStartPos; rCnt <= rng.Count; rCnt++) {
        Excel.Range cell = (Excel.Range)rng[rCnt, 1];

        try {
            if (cell.Value2 != null )
                tempstring = cell.Value2.ToString();
            else {
                endPos = rCnt - 1;
                releaseObject(cell);////////
                break;
            }
        }
        catch (Exception ee) {
            //MessageBox.Show(ee.ToString());
        }    
        //locate email
        if (cell.Value2.ToString() != email ) {
            //we found where the last email address is that we need
            //this is where the issue is occurring i think with the deleting the last row
            endPos = rCnt;
            releaseObject(cell);////////
            break;
        }
        releaseObject(cell);
    }

    //delete all the stuff AFTER the email address that we need
    if (endPos != 0) {
        deleteRange = xlWorkSheet.get_Range(column + endPos + ":" + "CF" + rngcount.ToString(), Type.Missing);
        deleteRange = deleteRange.EntireRow;
        deleteRange.Delete(Excel.XlDeleteShiftDirection.xlShiftUp);
    }

    //when the user opens the excel file, we want the focus to be here
    var rangehome = xlWorkSheet.get_Range(FormControls.FocusOn, FormControls.FocusOn);
    xlWorkSheet.Activate();
    rangehome.Select();

    string filename = xlWorkBook.Path + @"\" + email + ".xlsx";
    string fileSubstring = filename.Substring(0, filename.IndexOf(".xlsx"));
    string randomfileString = Guid.NewGuid().ToString("N").Substring(0, 10) + ".xlsx";
    string targetfilenameRename = fileSubstring + randomfileString;

    //((Excel.Worksheet)this.Application.ActiveWorkbook.Sheets[FormControls.WorksheetFocus]).Activate();
    //((Excel.Worksheet)Excel.Application.ActiveWorkbook.Sheets[1]).Activate();  

    Excel.Worksheet xlWorkSheetFocus = (Excel.Worksheet)xlWorkBook.Worksheets.get_Item(FormControls.WorksheetFocus);
    xlWorkSheetFocus.Activate();
    xlWorkBook.SaveAs(targetfilenameRename, Excel.XlFileFormat.xlWorkbookDefault, Type.Missing, Type.Missing,
                false, false, Excel.XlSaveAsAccessMode.xlNoChange,
                Type.Missing, Type.Missing, Excel.XlSaveConflictResolution.xlLocalSessionChanges, Type.Missing, Type.Missing);

    try {
        xlWorkBook.RefreshAll();
    }
    catch { }
        xlWorkBook.Save();
        string targetfile = xlWorkBook.Path + @"\" + FormControls.FileName + " - "
                    + email.Substring(0, email.IndexOf("@")) + ".xlsx";
        System.IO.File.Copy(targetfilenameRename, targetfile, true);

        string body = FormControls.eMailBody;
        body = body.Replace("%replacetext%", replacetext);
        //replace %replacetext% in body
        string targetfileSubstring = targetfile.Substring(0, targetfile.IndexOf(".xlsx"));
        string randomString = Guid.NewGuid().ToString("N").Substring(0, 10)+".xlsx";
        string targetfileRename = targetfileSubstring+randomString;

        while (true) {
            try {
                SendEmail(targetfile, email, FormControls.eMailSubject, body,FormControls.eMailFrom);                                  
            }
            catch (Exception ee) {
                MessageBox.Show(ee.ToString());
                continue;
            }

            // all is good
            break;
        }
        releaseObject(valueRange);
        releaseObject(deleteRange);
        File.Copy(targetfile, targetfileRename, true);
    }
    catch (Exception e) {
        MessageBox.Show(e.ToString());
    }
    finally {
        //DisposeMe();
        // Release all COM RCWs.
        // The "releaseObject" will just "do nothing" if null is passed,
        // so no need to check to find out which need to be released.
        // The "finally" is run in all cases, even if there was an exception
        // in the "try". 
        // Note: passing "by ref" so afterwords "xlWorkSheet" will
        // evaluate to null. See "releaseObject".
        releaseObject(range);
        releaseObject(xlWorkSheet);
        releaseObject(xlWorkBook);
        // The Quit is done in the finally because we always
        // want to quit. It is no different than releasing RCWs.
        if (xlApp != null) {
            xlApp.Quit();
        }
        releaseObject(xlApp);
    }
}

【问题讨论】:

  • 当数据透视表引用的数据只有一行时,是字段名还是值?
  • 试试if,例如if (rowcount &gt; 1) { RefreshAll } else { RefreshActiveSheet }
  • @BerkerYüceer 真是个好主意!!你能把它编码成答案吗?
  • 您说您有数据透视表,因此解决方案现在基于它们的属性准备答案。
  • @scott 它的字段名称和估值器

标签: c# .net excel interop


【解决方案1】:

我可以使用数据透视表复制此错误的唯一方法是尝试在没有列标题的范围内创建一个,就像 Stephan1010 答案的屏幕截图一样。

GetPivotData Excel 函数中,透视字段由它们的名称引用 (=GETPIVOTDATA("EmailAddress",$A$3));因此,禁止没有它们的数据源是有意义的。

解决方案是在 Excel 中切换 ListObject 而不是 Range - 当您选择范围 $A$1:$C$1格式为表格(来自功能区)时,结果将跨越$A$1:$C$2的表;第一行的内容成为列标题,第二行是有效的空记录。有趣的是,无论您是否选中“我的表格有标题”复选框,都会发生这种情况(2 行跨度)(数据将移动到第一行,并且表格将包含默认的“Column1”-“Column2 "-"Column3" 标题,如果复选框被清除)。

换句话说,ListObject始终是数据透视表的有效数据源,而Range 可能没有足够的行。此外,如果您没有列标题并且您创建了一个范围为$A$1:$C$2 的数据透视表,则$A$1:$C$1 处的记录将用作列标题,这意味着第一条记录将丢失。

根据您提供的代码,我假设数据透视表已经存在并连接到包含宏的模板工作簿中的某个 [named?] 范围。将您的范围转换为表格可能就像从功能区中选择 format as table 一样简单。然后你可以有这样的代码来删除所有不必要的行,同时仍然为数据透视表保留一个有效的数据源:

    public void DeleteExtraTableRows(string emailAddress, Excel.ListObject table)
    {
        try
        {
            var rowIndex = 0;
            var wasDeleted = false;
            while (rowIndex <= table.ListRows.Count)
            {
                if (!wasDeleted) rowIndex++;
                var row = table.ListRows[rowIndex];

                var range = (Excel.Range)row.Range.Cells[1, 1];
                var value = range.Value2;

                if (value != null && !string.Equals(emailAddress, value.ToString()))
                {
                    row.Delete();
                    wasDeleted = true;
                }
            }
        }
        catch (Exception e)
        {
            MessageBox.Show(e.Message + "\n\n" + e.StackTrace);
        }
    }

还有一种可能是 email 永远不会在循环的 if (cell.Value2.ToString() == email ) 条件中找到,这最终会从您的范围中删除所有行 - 即使唯一的区别是 in 末尾的额外空格-单元格值。使用上面的代码,即使所有电子邮件地址都被删除,数据源对于将连接到它的数据透视表仍然是有效的。

编辑: 在 Excel 中,您可以将Range 转换为ListObject,方法是选择相关范围并单击主页 选项卡中的格式化为表格功能区按钮。或者,您可以像这样创建一个:

            var range = ((Excel.Range)(worksheet.Range[worksheet.Cells[1, 1], worksheet.Cells[3, 1]]));
            var table = worksheet.ListObjects.Add(SourceType: Excel.XlListObjectSourceType.xlSrcRange, Source: range,
                                      XlListObjectHasHeaders: Excel.XlYesNoGuess.xlYes);
            table.TableStyle = "TableStyleMedium3";

在代码中,您可以使用ListObjects 属性访问工作表上的所有ListObjects

        var worksheet = (Excel.Worksheet) Globals.ThisAddIn.Application.ActiveSheet;
        var tables = worksheet.ListObjects;

然后,您可以通过几种不同的方式访问特定的ListObject /table:

        var myTable = tables[1];
        var myTable = tables.Item["Table1"];
        var myTable = tables.OfType<Excel.ListObject>().FirstOrDefault(t => t.Name == "Table1");

随着从表中添加行,它所引用的实际范围将相应扩大;使用myTable.Range 访问相关范围。

【讨论】:

  • 很抱歉,我最初认为这可以解决问题,但事实并非如此。有同样的问题
  • 没问题。请使用新的更新代码更新问题,将数据透视表连接到 ListObject 而不是 Range 肯定会使许多范围处理代码无用,并且可能改变了您访问这些数据的方式(例如,@ 987654344@ 没有了)。我相信我们可以解决这个问题。此外,在您要完成的工作中,我可能还没有完全理解:我的方法从第一列包含电子邮件地址的表中删除所有行,但电子邮件地址与指定字符串匹配的行除外。这是你打算做的吗?
  • 对不起,我不明白如何选择列表对象而不是范围,你能解释一下吗?
  • 谢谢!!我还想说我不认为这是 tostring==email 问题。我检查了这个。
  • 非常感谢!所以在你代码的最后一行之后,我应该运行 RefreshAll?
【解决方案2】:

我想这种情况的发生是因为你得到了数据透视表。 导致全部刷新也会触发数据透视表的刷新命令。 看看下面的代码。它可能会让您对此有所了解。我确定它不是大约 1 行。我检查过它一切正常,最有可能是由数据透视表引起的。

Microsoft.Office.Interop.Excel.Application xlApp = new Microsoft.Office.Interop.Excel.Application();
Microsoft.Office.Interop.Excel.Workbook xlWorkbook = xlApp.Workbooks.Open("some.xlsx");
// For each worksheet we got
foreach (Microsoft.Office.Interop.Excel.Worksheet worksheet in xlWorkbook.Sheets) 
{   // and each pivot table in each worksheet
    foreach (Microsoft.Office.Interop.Excel.PivotTable pivot in worksheet.PivotTables())
    {   // disable BackgroundQuery
        pivot.PivotTableWizard(BackgroundQuery: false);
    }
}
// try to refresh all sheet
try { xlWorkbook.RefreshAll(); } catch { }
// then save
xlWorkbook.Save();

【讨论】:

  • 非常感谢您的帮助。但我无法执行 backgroundquery = false。
  • 正如我之前提到的,是的。这帮助我解决了这个问题,但如果它仍然给你同样的错误尝试在pivot.PivotTableWizard(BackgroundQuery: false);之后添加pivot.RefreshTable();
  • 如果您无法定义 bgquery,请尝试pivot.PivotTableWizard( Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, false);
【解决方案3】:

显而易见的答案似乎是,有时您有一行数据作为数据透视表的来源,而 有时您没有 - 即使您认为仍然这样做。我无法为一行数据创建数据透视表(或更改数据透视表的来源):

但是,如果您能够以某种方式找到一种方法来做到这一点,那么您已经找到了答案。仅从实践/理论的角度来看,没有理由不能将一行数据作为源,但看起来 excel 试图阻止这种情况的发生(可能是因为代码假设有两行)。因此,如果您确实找到了方法,那么它可能是一个错误。祝你好运。

【讨论】:

  • 谢谢。但有时它对一行数据有效,有时则无效
  • 好吧,就像我说的,那么它很可能是一个错误。祝你好运,让微软的人来这里承认这一点。
  • 我同意你的观点,但我认为如果他们向我承认任何事情对我没有多大用处:)
  • 是的,我同意。所以我们是一致的。因此,如果我们是对的,那么最好的解决方案是确保您的数据透视表永远不会尝试仅引用一行数据。顺便说一句,我现在对 C# 并不熟悉,所以我无法提供任何关于最佳方式的建议。祝你好运。
  • 非常感谢您的努力、时间和愿意为此提供帮助的意愿
猜你喜欢
  • 2020-11-13
  • 2019-05-02
  • 2014-10-13
  • 2021-09-21
  • 1970-01-01
  • 2021-11-25
  • 2014-10-13
  • 1970-01-01
  • 2013-02-24
相关资源
最近更新 更多