OpenXML: ширина столбца Auto Size в Excel

Я написал код для создания файла Excel с помощью OpenXML. Ниже приведен код, который генерирует столбцы в Excel.

  Worksheet worksheet = new Worksheet();
            Columns columns = new Columns();
            int numCols = dt1.Columns.Count;
            for (int col = 0; col < numCols; col++)
            {
                    Column c = CreateColumnData((UInt32)col + 1, (UInt32)numCols + 1, 20.42578125D);

                columns.Append(c);
            }
            worksheet.Append(columns);

Кроме того, я попытался использовать строку ниже для создания столбцов.

 Column c = new Column() { Min = (UInt32Value)1U, Max = (UInt32Value)1U, Width = 25.42578125D, BestFit = true,  CustomWidth = true};

Я думал, что с помощью BestFit он должен работать. Но он не устанавливает автоматический размер.

Пожалуйста, помогите мне в этом.

Спасибо,

Ответ 1

Свойство BestFit является информационным свойством (возможно, для оптимизации Excel). Вам все равно нужно предоставить ширину для столбца. Это означает, что вы должны фактически рассчитать ширину столбца в зависимости от содержимого ячейки. Open XML SDK не делает это автоматически для вас, поэтому лучше использовать для этого стороннюю библиотеку.

Ответ 2

Вы должны рассчитать это сами, к сожалению

Это то, что у меня есть. Для моих данных он работает с таблицей с некоторым дополнительным кодом, чтобы позаботиться о некоторых стилях, которые я установил. Это не идеально, но работает для того, что мне нужно.

 private WorksheetPart mySheetPart;
 private void WriteToTable()
 {
      //Get your sheet data - write Rows and Cells
      SheetData sheetData = GetSheetData();

      //get your columns (where your width is set)
      Columns columns = AutoSize(sheetData);

      //add to a WorksheetPart.WorkSheet
      mySheetPart.Worksheet = new Worksheet();
      mySheetPart.Worksheet.Append(columns);
      mySheetPart.Worksheet.Append(sheetData);
 }

 private Columns AutoSize(SheetData sheetData)
 {
        var maxColWidth = GetMaxCharacterWidth(sheetData);

        Columns columns = new Columns();
        //this is the width of my font - yours may be different
        double maxWidth = 7;
        foreach (var item in maxColWidth)
        {
            //width = Truncate([{Number of Characters} * {Maximum Digit Width} + {5 pixel padding}]/{Maximum Digit Width}*256)/256
            double width = Math.Truncate((item.Value * maxWidth + 5) / maxWidth * 256) / 256;

            //pixels=Truncate(((256 * {width} + Truncate(128/{Maximum Digit Width}))/256)*{Maximum Digit Width})
            double pixels = Math.Truncate(((256 * width + Math.Truncate(128 / maxWidth)) / 256) * maxWidth);

            //character width=Truncate(({pixels}-5)/{Maximum Digit Width} * 100+0.5)/100
            double charWidth = Math.Truncate((pixels - 5) / maxWidth * 100 + 0.5) / 100;

            Column col = new Column() { BestFit = true, Min = (UInt32)(item.Key + 1), Max = (UInt32)(item.Key + 1), CustomWidth = true, Width = (DoubleValue)width };
            columns.Append(col);
        }

        return columns;
  }


  private Dictionary<int, int> GetMaxCharacterWidth(SheetData sheetData)
    {
        //iterate over all cells getting a max char value for each column
        Dictionary<int, int> maxColWidth = new Dictionary<int, int>();
        var rows = sheetData.Elements<Row>();
        UInt32[] numberStyles = new UInt32[] { 5, 6, 7, 8 }; //styles that will add extra chars
        UInt32[] boldStyles = new UInt32[] { 1, 2, 3, 4, 6, 7, 8 }; //styles that will bold
        foreach (var r in rows)
        {
            var cells = r.Elements<Cell>().ToArray();

            //using cell index as my column
            for (int i = 0; i < cells.Length; i++)
            {
                var cell = cells[i];
                var cellValue = cell.CellValue == null ? string.Empty : cell.CellValue.InnerText;
                var cellTextLength = cellValue.Length;

                if (cell.StyleIndex != null && numberStyles.Contains(cell.StyleIndex))
                {
                    int thousandCount = (int)Math.Truncate((double)cellTextLength / 4);

                    //add 3 for '.00' 
                    cellTextLength += (3 + thousandCount);
                }

                if (cell.StyleIndex != null && boldStyles.Contains(cell.StyleIndex))
                {
                    //add an extra char for bold - not 100% acurate but good enough for what i need.
                    cellTextLength += 1;
                }

                if (maxColWidth.ContainsKey(i))
                {
                    var current = maxColWidth[i];
                    if (cellTextLength > current)
                    {
                        maxColWidth[i] = cellTextLength;
                    }
                }
                else
                {
                    maxColWidth.Add(i, cellTextLength);
                }
            }
        }

        return maxColWidth;
    }

Ответ 3

У меня не было времени изучить его, но вместо того, чтобы просто оставить комментарий и ссылка, я подумал, d поделиться комментарием от кого-то, кто, по-видимому, сделал некоторые исследования по этому поводу.

У меня лично возникли проблемы с получением официальных формул в соответствии с реальностью. То есть Короткие строки получили слишком маленькие ячейки, более длинные строки получили слишком большие ячейки и, самое главное, значение, представленное в Excel, было пропорционально меньше значения, которое я ввел в DocumentFormat.OpenXml.Spreadsheet.Column Width -property. Моим быстрым решением было просто иметь минимальную ширину.

В любом случае, вот комментарий:

Я должен был сделать это в конце, потому что файлы xlsx, которые меня интересуют, генерируются автоматически и должны выглядеть красивыми, как только они открываются, поэтому я немного заглянул в это и обнаружил, что есть несколько проблем, чтобы точно размер столбцов в Excel.

  • Необходимо использовать точный размер символа, а это означает, что вместо использования MeasureString вам необходимо использовать MeasureCharacterRanges, см. http://groups.google.com/group/microsoft.public.office.developer.com.add_ins/browse_thread/thread/2fc33557feb72ab4/adaddc50480b8cff?lnk=raot

  • Несмотря на то, что спецификация говорит, чтобы добавить 5 пикселей (1 для границы и 2 для каждого бокового поля), Excel, похоже, использует 9 - 1 для границы, 5 для ведущего пространства и 3 для конечного пробела - я только нашел это, используя приложение доступности. Лупа и подсчет пикселей после использования Excel для автоматической установки столбцов

На самом деле я основывал свои вычисления на основных метриках шрифтов, поэтому я фактически не использую ни MeasureCharacterRanges, ни MeasureString. Если кто-то заинтересован в выполнении этого из метрик шрифта, то:

Width = Truncate( {DesiredWidth} + 9 / {MaxDigitWidth} ) / 256

{MaxDigitWidth} - целое число, округленное до ближайшего пикселя любого из 0..9 цифр при 96 dpi {DesiredWidth} - сумма суммирования всех ширины символов вместе, где каждая ширина символов - ширина символа с 96 dpi, округленная до ближайшего целого. Обратите внимание, что каждый символ округляется не общей суммой

Ответ 4

Здесь возможная формула width = Truncate ([{Количество символов} * {Максимальная ширина знака} + {5 пиксельных отступов}]/{Максимальная ширина знака} * 256)/256