web-dev-qa-db-fra.com

OpenXML: Largeur de colonne Taille automatique dans Excel

J'ai écrit un code pour générer un fichier Excel à l'aide d'OpenXML. Voici le code qui génère les colonnes dans 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);

Aussi, j'ai essayé en dessous de la ligne pour créer des colonnes.

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

J'ai pensé en utilisant BestFit cela devrait fonctionner. Mais cela ne définit pas la taille automatique.

12
Mittal Patel

La propriété BestFit est une propriété d’information (éventuellement pour une optimisation par Excel). Vous devez toujours fournir la largeur pour la colonne. Cela signifie que vous devez réellement calculer la largeur de la colonne en fonction du contenu de la cellule. Open XML SDK ne le fait pas automatiquement pour vous, il est donc préférable d'utiliser une bibliothèque tierce pour cela.

7
Vincent Tan

Vous devez le calculer vous-même malheureusement

C'est ce que j'ai. Cela fonctionne pour mes données tabulaires avec du code supplémentaire pour prendre en charge certains styles que j'ai définis. Ce n'est pas parfait par tous les moyens mais fonctionne pour ce dont j'ai besoin.

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

Je n’ai pas eu le temps d’examiner la question, mais au lieu de laisser un commentaire et un lien , je pensais partager un commentaire de quelqu'un qui a apparemment mené des recherches à ce sujet.

J'ai personnellement eu des problèmes pour obtenir les formules officielles pour correspondre à la réalité. C'est à dire. Les chaînes courtes ont des cellules trop petites, les chaînes plus longues, et surtout, la valeur présentée dans Excel est proportionnellement inférieure à la valeur que j'ai insérée dans la propriété Width- de DocumentFormat.OpenXml.Spreadsheet.Column. Ma solution rapide était juste d'avoir une largeur minimale.

Quoi qu'il en soit, voici le commentaire:

J'ai dû le faire à la fin car les fichiers xlsx qui m'intéressent sont générés automatiquement et devraient ressembler à Nice dès qu'ils sont ouverts. J'ai donc regardé cela un peu plus loin et constaté qu'il existe quelques problèmes pour dimensionner avec précision les colonnes dans Exceller.

  1. Vous devez utiliser une taille de caractère précise, ce qui signifie qu'au lieu d'utiliser MeasureString, vous devez utiliser MeasureCharacterRanges, voir http://groups.google.com/group/Microsoft.public.office.developer.com.add_ins/browse_thread/thread/2fc33557feb72ab4/adaddc50480b8cff? Lnk = raot

  2. Malgré la spécification spécifiant d'ajouter 5 pixels (1 pour la bordure et 2 pour chaque marge latérale), Excel semble utiliser 9 - 1 pour la bordure, 5 pour l'espace de début et 3 pour l'espace de fin - je ne l'ai trouvé qu'en utilisant l'accessibilité app. Loupe et comptage des pixels après avoir utilisé Excel pour ajuster automatiquement les colonnes

En fait, je basais mes calculs sur les métriques de police sous-jacentes afin que je n’utilise réellement ni MeasureCharacterRanges ni MeasureString. Si cela vous intéresse à partir des métriques de police, alors:

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

{MaxDigitWidth} est un entier arrondi au pixel le plus proche de l'un des 0..9 chiffres à 96 dpi {DesiredWidth} est la somme de la somme de toutes les largeurs de caractères, la largeur de chaque caractère étant la largeur du caractère. à 96 dpi arrondi à l'entier le plus proche. Notez que chaque caractère est arrondi et non la somme globale

1
Aske B.

Voici la formule possible Width = Truncate ([{Nombre de caractères} * {Largeur maximale de chiffres} + {5 pixels}}]/{Largeur maximale de chiffres} * 256)/256

0
Cyclion