该命令至less需要两行源数据

我得到这个错误:

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(); 

有两个工作表。 一个有一个数据透视表,一个有原始数据。 有时只有一行数据。 对于多行数据,上面的代码行总是有效的 ; 然而,对于只有一行数据,上面的代码有时会起作用,有时我会得到上面的错误信息。

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

这里发生了什么? 为什么有时候只有这个错误

非常感谢你的指导。

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

 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); } } 

我可以通过数据透视表复制这个错误的唯一方法就是试图创build一个没有列标题的范围,就像Stephan1010的答案截图一样。

GetPivotData Excel函数中,数据透视表字段的名称( =GETPIVOTDATA("EmailAddress",$A$3) ); 因此,禁止不具有这些数据源的数据源是有意义的。

解决方法是在Excel中select范围$A$1:$C$1格式为表格 (来自function区)的Excel中而不是Range内的List对象时,结果表将跨越$A$1:$C$2 ; 第一行的内容成为列标题,第二行是一个有效的空logging。 有趣的是,无论您是否检查“My table has headers”checkbox(数据将移动到第一行,表中将包含默认的“Column1” – “Column2 “ – ”Column3“标题,如果checkbox被清除)。

换句话说, ListObject 始终是数据透视表的有效数据源,而Range可能不包含足够的行。 另外,如果您没有列标题,并且创build了范围$A$1:$C$2的数据透视表,则$A$1:$C$2中的logging将用作列标题,这意味着第一条logging会丢失。

从您提供的代码中,我将假定数据透视表已经存在并连接到包含该macros的模板工作簿中的某个[named?]范围。 将范围转换为表格可能从function区select格式表格一样简单。 然后你可以像这样的代码删除所有不必要的行,同时仍然保持数据透视表的有效数据源:

  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); } } 

在循环的if (cell.Value2.ToString() == email )条件中也if (cell.Value2.ToString() == email ) ,这将最终从您的范围中删除所有行 – 即使唯一的区别是在单元格内值的结尾。 使用上面的代码,即使所有的电子邮件地址被删除,数据源仍然是一个有效的数据源连接到它的数据透视表。

编辑:在Excel中,通过select有问题的区域并单击“ 格式作为表格function区”button,从“ 主页”选项卡将Range转换为ListObject 。 或者,您可以创build一个像这样的:

  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 /表:

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

当行从表中添加时,它引用的实际范围将相应扩展; 使用myTable.Range来访问有问题的范围。

我想这种情况是由于你得到的数据透视表而发生的。 导致刷新都会触发数据透视表的刷新命令。 看下面的代码。 它可能会给你一个想法。 它不是约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(); 

显而易见的答案似乎是, 有时候你有一行数据作为你的数据透视表的来源, 有时你不知道 – 即使你认为你仍然这么做。 我还没有能够创build一个数据透视表(或更改数据透视表的来源)到一行数据:

PivotTableErrorExplanation

但如果你能够想出办法做到这一点,那么你已经find了你的答案。 从实际/理论的angular度来看,没有理由不能将一行数据作为源代码,但是看起来像是excel试图阻止这种情况的发生(也许是因为代码假设有两行)。 所以,如果你find一个方法,那么这可能是一个错误。 祝你好运。