Почему часть раскрашивания ячеек не происходит, и есть ли связь между этим и сообщением об ошибке «Не удается выполнить привязку во время выполнения»?

#c# #excel #pivot-table #nullreferenceexception #excel-interop

#c# #excel #сводная таблица #исключение nullreferenceexception #excel-interop

Вопрос:

Я получаю следующее сообщение об ошибке:

введите описание изображения здесь

Это происходит в этом коде:

 private bool GetContractForDescription(string desc)
{
    int DESCRIPTION_COL_NUM = 2;
    int CONTRACT_COL_NUM = 7;
    bool contractVal = false;

    int rowsUsed = _xlBook.Worksheets["PivotData"].UsedRange.Rows.Count;
    int colsUsed = _xlBook.Worksheets["PivotData"].UsedRange.Columns.Count;
    string colsUsedAsAlpha 
ReportRunnerConstsAndUtils.GetExcelColumnName(colsUsed);
    string endRange = string.Format("{0}{1}", colsUsedAsAlpha, rowsUsed);

    Range sourceData = _xlBook.Worksheets["PivotData"].Range[string.Format("A2:{0}", endRange)];

    // This is blowing up for some reason, so look for more detail on why
    string currentDesc = string.Empty;
    try
    {
        for (int i = 1; i <= rowsUsed; i  )
        {
            var dynamicCell = (Range)sourceData.Cells[i, DESCRIPTION_COL_NUM];
            currentDesc = dynamicCell.Value2.ToString();
            if (currentDesc.Equals(desc))
            {
                var dynamicContractCell = (Range)sourceData.Cells[i, CONTRACT_COL_NUM];
                contractVal = (bool)dynamicContractCell.Value2;
                break;
            }
        }
        return contractVal;
    }
    catch (Exception  ex)
    {
        MessageBox.Show(string.Format("currentDesc is {0}; exception is {1}", currentDesc, ex.Message));
    }
    return false;
}
  

И что еще более странно, диапазон строк / столбцов, которые я раскрашиваю при определенных обстоятельствах, имеет «вырезанное отверстие», где оно не окрашивается.

Вот исходные данные, о которых идет речь (средняя строка (156) со значением «TRUE» в последнем столбце, является ключевой):

введите описание изображения здесь

Вот как эти данные отображаются в сводной таблице (средний блок данных, строки 293-297, являются ключевыми):

введите описание изображения здесь

Вот код, который я использую для поворота исходных данных:

 private void AddPivotTable()
{
    int FIRST_DESCRIPTION_ROW = 8;
    int LINES_BETWEEN_DESCRIPTION_VALS = 5;

    var pch = _xlBook.PivotCaches();
    int rowsUsed = _xlBook.Worksheets["PivotData"].UsedRange.Rows.Count;
    int colsUsed
_xlBook.Worksheets["PivotData"].UsedRange.Columns.Count;
    string colsUsedAsAlpha
ReportRunnerConstsAndUtils.GetExcelColumnName(colsUsed);
    string endRange = string.Format("{0}{1}", colsUsedAsAlph
rowsUsed);

    Range sourceData
_xlBook.Worksheets["PivotData"].Range[string.Format("A1:{0}", endRange)];

    PivotCache pc = pch.Create(XlPivotTableSourceType.xlDatabas
sourceData);
    PivotTable pvt = pc.CreatePivotTable(_xlPivotTableSheet.Range["A6"
"PivotTable");
    pvt.MergeLabels = true;

    pvt.PivotFields("Description").Orientation
XlPivotFieldOrientation.xlRowField;
    var monthField = pvt.PivotFields("MonthYr");
    monthField.Orientation = XlPivotFieldOrientation.xlColumnField;
    monthField.NumberFormat = "mmm yyyy";
    monthField.DataRange.Interior.Color
ColorTranslator.ToOle(Color.LightBlue);

    pvt.CompactLayoutColumnHeader = "Months";
    pvt.CompactLayoutRowHeader = "Description";

    pvt.AddDataField(pvt.PivotFields("TotalQty"), "Total Packages
XlConsolidationFunction.xlSum).NumberFormat = "###,##0";
    pvt.AddDataField(pvt.PivotFields("TotalSales"), "Total Purchases
XlConsolidationFunction.xlSum).NumberFormat = "$#,##0";
    PivotField avg = pvt.CalculatedFields().Add("Average Price
"=TotalSales/TotalQty", true);
    avg.Orientation = XlPivotFieldOrientation.xlDataField;

    // TODO: This calculation needs to change (it needs to actually 
made a calculation, rather than just the TotalSales val)
    pvt.CalculatedFields()._Add("PercentOfTotal", "=TotalSales");
    pvt.AddDataField(pvt.PivotFields("PercentOfTotal"), "% of Total
Type.Missing).NumberFormat = "###.##";
    . . .
  

…и вот код для раскрашивания рассматриваемого элемента:

 for (m = 1; m <= dataCnt   1; m  )
{
    string descVal = cellRng.Value2.ToString();
    bool isContractItem = GetContractForDescription(descVal);
    if (isContractItem)
    {
        mcellRng = _xlPivotTableSheet.Range[
            _xlPivotTableSheet.Cells[rowcnt, 1],
            _xlPivotTableSheet.Cells[rowcnt   4, _grandTotalsColumn-1]];
        mcellRng.Interior.Color = ColorTranslator.ToOle(CONTRACT_ITEM_COLOR);
        mcellRng.EntireRow.RowHeight = 15;
    }
    . . .
  

Я не понимаю, как код раскрашивания (непосредственно выше) может работать только частично — как видно на втором скриншоте, причем первые несколько ячеек в столбце B являются белыми, а не желтыми, как остальная часть диапазона. Как будто есть
что-то «ядовитое» в этих данных, что мешает их раскрашиванию.

Если я создаю меньше столбцов (используя другой диапазон дат для запуска отчета), он запускается без сообщения об ошибке (но все еще имеет «дыру» в рассматриваемом диапазоне).

Если я просто создаю два месяца, все в порядке; но если я попробую шесть (запись «проблема» существует в любом случае), генерируется исключение …?!?

UPDATE

When I comment out the entire while loop, it runs without the error, but of course I then do not get the ContractItems colorized.

The problematic while loop is:

 while ((cellRng.Value2 != null) amp;amp; cellRng.Value2.ToString() != "Total Total Packages")
{
    cellRng.RowHeight = 25;
    for (m = 1; m <= pivotTableRowCount; m  )
    {
        string descVal = cellRng.Value2.ToString();
        bool isContractItem = GetContractForDescription(descVal);
        if (isContractItem)
        {
            mcellRng = _xlPivotTableSheet.Range[
                _xlPivotTableSheet.Cells[rowCounter, 1],
                _xlPivotTableSheet.Cells[rowCounter   4, _grandTotalsColumn - 1]];
            mcellRng.Interior.Color = ColorTranslator.ToOle(CONTRACT_ITEM_COLOR);
            mcellRng.EntireRow.RowHeight = 15;
        }

        rowCounter = rowCounter   LINES_BETWEEN_DESCRIPTION_VALS;
        cellRng = (Range)_xlPivotTableSheet.Cells[rowCounter, 1];
    }
}
  

Что здесь может быть причиной сообщения об ошибке?

Проблема не в методе GetContractForDescription(), потому что игнорирование его таким образом:

 bool isContractItem = false; //GetContractForDescription(descVal);
  

… не предотвращает сообщение об ошибке.

ОБНОВЛЕНИЕ 2

Поскольку моя голова раскалывается от того, что я так долго стучал ею по обоям, я решил «вернуться к кодированию» и начать заново. Теперь у меня есть этот код для определения того, какие элементы сжаты:

 List<String> contractItemDescs = GetContractItemDescriptions();
MessageBox.Show(contractItemDescs.Count.ToString());
. . .
private List<string> GetContractItemDescriptions()
{
    int FIRST_PIVOTDATA_DATA_ROW = 2;
    int DESCRIPTION_COLUMN = 2;
    int CONTRACT_COLUMN = 7;
    List<string> descriptionsOfContractedItems = new List<string>();
    int pivotDataRowCount = _xlBook.Worksheets["PivotData"].UsedRange.Rows.Count;

    // Loop through the data sheet, adding the Descriptions for all Contract Items to the generic list
    for (int i = FIRST_PIVOTDATA_DATA_ROW; i <= pivotDataRowCount; i  )
    {
        Range contractItemCell = _xlPivotDataSheet.Cells[i, CONTRACT_COLUMN];
        //if (contractItemCell.Value2.ToString() == "TRUE") <= seems to be the same logic as below, but this doesn't work, whereas the below code does...?!?
        if (contractItemCell.Value2.ToString().ToUpper() != "FALSE")
        {
            Range descriptionCell = _xlPivotDataSheet.Cells[i, DESCRIPTION_COLUMN];
            String desc = descriptionCell.Value2.ToString();
            descriptionsOfContractedItems.Add(desc);
        }
    }
    return descriptionsOfContractedItems;
}
  

Это работает правильно — окно сообщения показывает мне допустимое количество. Обратите внимание, однако, на закомментированный оператор «if». Используя это, я ничего не добавлял в список; с кажущимся аналогом, который я сейчас использую, я получаю правильное / ожидаемое поведение, хотя я не знаю почему.

Комментарии:

1. TL; DR но более простым способом отладки должно быть закомментирование инструкций try / catch и запуск в режиме отладки, чтобы точно увидеть, где возникает исключение. Я предполагаю, что эта строка contractVal = (bool)dynamicContractCell.Value2;

2. Я отлаживал его, выполнив пошаговое выполнение, но найти, какое именно условие вызывает исключение, — это проблема.

3. Что заставляет вас подозревать эту строку? Единственными значениями, которые он имеет в тестовом листе, являются «TRUE» и «FALSE»

4. явные приведения в целом выглядят для меня подозрительно :] и сообщение об ошибке, похоже, предполагает, что исключение возникло после currentDesc = dynamicCell.Value2.ToString(); и до return contractVal;

Ответ №1:

Я не совсем понимаю, почему предыдущий код не работал, но я предполагаю, что логика была просто в целом слишком запутанной. Я переработал и упростил его (вместо того, чтобы пытаться адаптировать какой-то существующий код, написанный кем-то другим, я начал заново и сделал это «по-своему»), и теперь он работает просто отлично. Новый код:

 private static readonly Color CONTRACT_ITEM_COLOR = Color.FromArgb(255, 255, 204);
. . .

private void AddPivotTable()
{
    . . .
    List<String> contractItemDescs = GetContractItemDescriptions();
    ColorizeContractItemBlocks(contractItemDescs);
    . . .
}

private List<string> GetContractItemDescriptions()
{
    int FIRST_PIVOTDATA_DATA_ROW = 2;
    int DESCRIPTION_COLUMN = 2;
    int CONTRACT_COLUMN = 7;
    List<string> descriptionsOfContractedItems = new List<string>();
    int pivotDataRowCount = _xlBook.Worksheets["PivotData"].UsedRange.Rows.Count;

    // Loop through the data sheet, adding the Descriptions for all Contract Items to the generic list
    for (int i = FIRST_PIVOTDATA_DATA_ROW; i <= pivotDataRowCount; i  )
    {
        Range contractItemCell = _xlPivotDataSheet.Cells[i, CONTRACT_COLUMN];
        if (contractItemCell.Value2.ToString().ToUpper() != "FALSE")
        {
            Range descriptionCell = _xlPivotDataSheet.Cells[i, DESCRIPTION_COLUMN];
            String desc = descriptionCell.Value2.ToString();
            descriptionsOfContractedItems.Add(desc);
        }
    }
    return descriptionsOfContractedItems;
}

private void ColorizeContractItemBlocks(List<string> contractItemDescs)
{
    int FIRST_DESCRIPTION_ROW = 8;
    int DESCRIPTION_COL = 1;
    int ROWS_BETWEEN_DESCRIPTIONS = 5;
    int rowsUsed = _xlBook.Worksheets["PivotTable"].UsedRange.Rows.Count;
    int colsUsed = _xlBook.Worksheets["PivotTable"].UsedRange.Columns.Count;
    int currentRowBeingExamined = FIRST_DESCRIPTION_ROW;

    while (currentRowBeingExamined < rowsUsed) 
    {
        Range descriptionCell = _xlPivotTableSheet.Cells[currentRowBeingExamined, DESCRIPTION_COL];
        String desc = descriptionCell.Value2.ToString();
        if (contractItemDescs.Contains(desc))
        {
            Range rangeToColorize = _xlPivotTableSheet.Range[
                _xlPivotTableSheet.Cells[currentRowBeingExamined, 1],
                _xlPivotTableSheet.Cells[currentRowBeingExamined   4, colsUsed]];
            rangeToColorize.Interior.Color = ColorTranslator.ToOle(CONTRACT_ITEM_COLOR);
        }
        currentRowBeingExamined = currentRowBeingExamined   ROWS_BETWEEN_DESCRIPTIONS;
    }
}