4

このエラーが発生しています:

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

2 つのワークシートがあります。1 つにはピボット テーブルがあり、もう 1 つには生データがあります。データが 1 行しかない場合もあります。複数行のデータの場合、上記のコード行は常に機能します。ただし、データが 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);
    }
}
4

3 に答える 3

6

ピボット テーブルでこのエラーを再現できる唯一の方法は、Stephan1010 の回答のスクリーンショットのように、列ヘッダーを持たない範囲から 1 つを作成しようとすることでした。

GetPivotData Excel 関数では、ピボット フィールドは名前 ( =GETPIVOTDATA("EmailAddress",$A$3)) で参照されます。したがって、それらを持たないデータ ソースを禁止することは理にかなっています。

解決策は、(リボンから)テーブルとして範囲とフォーマットを選択したときに、ExcelListObjectで a ではなくa をピボットすることです。最初の行の内容は列ヘッダーになり、2 番目の行は有効な空のレコードです。「My table has headers」チェックボックスをオンにするかどうかに関係なく、これが発生することに注意してください (2 行スパン) (データは最初の行に移動され、テーブルにはデフォルトの「Column1」-「Column2」が含まれます)。チェックボックスがオフの場合は "-"Column3" ヘッダー)。Range$A$1:$C$1$A$1:$C$2

つまり、ListObject常にピボット テーブルの有効なデータ ソースですが、 にRangeは十分な行が含まれていない可能性があります。また、列ヘッダーがなく、 range でピボット テーブルを作成すると$A$1:$C$2、 のレコード$A$1:$C$1が列ヘッダーとして使用されます。つまり、最初のレコードが失われます。

あなたが提供したコードから、ピボット テーブルが既に存在し、マクロを含むテンプレート ワークブックの [named?] 範囲に接続されていると推測します。範囲をテーブルに変換することは、リボンからテーブルとしてフォーマットを選択するのと同じくらい簡単かもしれません。次に、次のようなコードを使用して、ピボット テーブルの有効なデータ ソースを維持しながら不要な行をすべて削除できます。

    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 )ます。これにより、範囲からすべての行が削除されることになります - 唯一の違いがセル内の値の末尾にある余分なスペースであっても。上記のコードでは、すべての電子メール アドレスが削除されても、データ ソースは、それに接続されるピボット テーブルに対して有効なままになります。

編集: Excelでは、問題の範囲を選択し、 [ホーム] タブから [テーブルとして書式設定]リボン ボタンをクリックして、aRangeを a に変換します。または、次のように作成することもできます。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問題の範囲にアクセスするために使用します。

于 2012-10-13T19:59:14.040 に答える
4

この状況は、取得したピボット テーブルが原因で発生したと思います。cause refresh all は、ピボット テーブルの更新コマンドもトリガーします。以下のコードを見てください。それはあなたにそれについての考えを与えるかもしれません。その約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();
于 2012-10-12T11:10:21.423 に答える
2

明らかな答えは、ピボット テーブルのソースとして 1 行のデータがある場合とそうでない場合があるということです。ピボット テーブルを 1 行のデータに作成する (またはピボット テーブルのソースを変更する) ことができませんでした。

PivotTableError説明

しかし、どうにかしてこれを行う方法を見つけることができれば、答えを見つけたことになります。実用的/理論的な観点から、ソースとして1行のデータを使用できない理由はありませんが、Excelはそれが起こらないようにしようとしているようです(おそらくコードが2行を想定しているため)。したがって、方法が見つかった場合、それはおそらくバグです。幸運を。

于 2012-10-08T18:55:48.553 に答える