web-dev-qa-db-ja.com

OpenXML:Excelで列幅を自動調整

OpenXMLを使用してExcelファイルを生成するコードを記述しました。以下は、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を使用するとうまくいくと思いました。ただし、自動サイズは設定されません。

14
Mittal Patel

BestFitプロパティは情報プロパティです(Excelによる最適化のため)。列の幅を提供する必要があります。つまり、セルの内容に応じて実際に列幅を計算する必要があります。 Open XML SDKはこれを自動的に実行しないので、サードパーティのライブラリを使用することをお勧めします。

7
Vincent Tan

残念ながら自分で計算する必要があります

これは私が持っているものです。これは、設定したいくつかのスタイルを処理するためのいくつかの追加コードを含む表形式のデータに対して機能します。それは決して完璧ではありませんが、私がそれを必要とするもののために機能します。

 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;
    }
16
Hath

調査する時間はありませんでしたが、コメントと a link を残すだけでなく、これについていくつかの調査を行ったと思われる誰かからのコメントを共有したいと思いました。

私は個人的に 公式公式 を現実に合わせるために問題を抱えていました。つまり短い文字列ではセルが小さすぎ、長い文字列ではセルが大きすぎます。何よりも、Excelに表示される値は、_DocumentFormat.OpenXml.Spreadsheet.Column_のWidth-プロパティに挿入した値よりも比例的に小さくなりました。私の簡単な解決策は、幅を最小限にすることでした。

とにかく、ここにコメントがあります:

目的のxlsxファイルは自動生成され、開くとすぐに見栄えがよくなるので、最後にこれを実行する必要がありました。これをもう少し詳しく調べたところ、列のサイズを正確に決定するためにいくつかの問題があることがわかりました。 Excel。

  1. 正確な文字サイズを使用する必要があります。つまり、MeasureStringを使用する代わりに、MeasureCharacterRangesを使用する必要があります。詳細は http://groups.google.com/group/Microsoft.public.office.developer.com.add_ins/browse_threadを参照してください)/thread/2fc33557feb72ab4/adaddc50480b8cff?lnk = raot

  2. 仕様に5ピクセル(境界線に1つ、各側面のマージンに2つ)を追加するように言われているにもかかわらず、Excelは境界線に9 – 1、先行スペースに5、後続スペースに3を使用しているようです–私はアクセシビリティを使用することによってのみこれを見つけましたアプリ。 Excelを使用して列を自動調整した後の拡大鏡とピクセルのカウント

実際、基になるフォントメトリックに基づいて計算を行っていたため、MeasureCharacterRangesもMeasureStringも実際には使用していません。フォントメトリックからこれを行うことに興味がある場合は、次のようにします。

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

{MaxDigitWidth}は、96 dpiで0..9桁の最も近いピクセルに丸められた整数です。{DesiredWidth}は、すべての文字幅を合計した合計です。各文字幅は、96 dpiで丸められた文字の幅です。最も近い整数。合計ではなく各文字が丸められることに注意してください

1
Aske B.

ここで可能な式の幅= Truncate([{文字数} * {最大桁幅} + {5ピクセルのパディング}]/{最大桁幅} * 256)/ 256

0
Cyclion

@Mittal Patelが私の answer を見てください。投稿されたコードはDOMメソッドですが、SAXアプローチでもソリューションを作成し、結果は優れています。メモリドレインがなく(DataReaderから直接書き込む)、Interopライブラリよりはるかに高速です。欠点は自動調整機能だけです-同じデータを2回読み取る必要があります。

0
Lucy82