web-dev-qa-db-fra.com

Comment redimensionner WPF DataGrid pour l'adapter à son contenu?

Le but

Je voudrais définir une telle taille pour le DataGrid (standard, de WPF) afin que toutes les cellules (texte) soient entièrement visibles. J'ai une fenêtre avec DockPanel, avec DataGrid dedans, donc quand je redimensionne la fenêtre, tous les widgets imbriqués (DockPanel et DataGrid) sont redimensionnés en conséquence.

Exemple (edit-1)

Disons que vous avez une fenêtre de 100 pixels de large et que vous avez DataGrid avec une colonne, dont la cellule est "le renard brun rapide ..." (400 pixels de large). Ainsi, le DataGrid devrait être redimensionné à 400 pixels (probablement plus, à cause du rembourrage) et la fenêtre devrait être redimensionnée à 400 pixels aussi (également plus, à cause du remplissage).

Je n'ai trouvé aucune méthode standard pour le faire (AFAIK WPF fournit un moyen de couper le contenu à la largeur souhaitée, mon problème est exactement opposé), donc je trouve une solution de contournement aussi moche, qui ne fonctionne pas trop bien.

La solution de contournement

  1. itérer sur les en-têtes DataGrid (en supposant qu'il ne s'agit que de chaînes) et calculer la largeur requise pour le texte
  2. itérer sur les lignes DataGrid pour chaque colonne (en supposant qu'elles sont TextBlock ou TextBox) et calculer la largeur maximale requise pour le texte - ajouter des remplissages horizontaux pour TextBlock/TextBox et des marges horizontales pour la cellule DataGrid
  3. additionner toutes les différences entre DataGrid ActualWidth pour les colonnes et la largeur maximale calculée dans (2)
  4. augmenter la largeur de la fenêtre de la différence calculée en (3)

LE PROBLÈME

J'ai fait plusieurs tests, et dans certains cas, la largeur calculée est trop grande (c'est un problème mineur), dans certains cas, elle est trop petite. Le problème commence à sa procédure de base - calcul de la largeur requise pour TextBox/TextBlock, la largeur calculée est toujours inférieure de 1 unité à ce qu'elle devrait être (si je règle la largeur sur un calculé, 1 pixel du texte est toujours coupé).

Alors quel facteur j'ignore ici? Ou peut-être mieux - existe-t-il déjà une méthode pour redimensionner DataGrid pour l'adapter à son contenu?

Le code

Largeur de calcul requise pour le texte (ici pour TextBlock):

    public static double TextWidth(this TextBlock widget, string text)
    {
        var formattedText = new FormattedText(text, // can use arbitrary text
                                              System.Globalization.CultureInfo.CurrentCulture,
                                              widget.FlowDirection,
                                              widget.FontFamily.GetTypefaces().FirstOrDefault(),
                                              widget.FontSize, 
                                              widget.Foreground);

        return formattedText.Width+widget.Padding.Left+widget.Padding.Right;
    }

Ajuster la taille de la fenêtre pour qu'elle s'adapte au contenu de DataGrid (ugly_factor est une solution de contournement laide ;-) puisque je n'ai pas compris comment le réparer correctement, je la mets à 1,3 et de cette façon, ma fenêtre n'est "jamais" trop petite):

    public static void AdjustWidthToDataGrid(this Window window, DataGrid dataGrid, double ugly_factor)
    {
        var max_widths = dataGrid.Columns.Select(it => window.TextWidth(it.Header as string) 
                                                                        * ugly_factor).ToArray();

        foreach (var row in Enumerable.Range(0, dataGrid.Items.Count))
            foreach (var col in Enumerable.Range(0, dataGrid.Columns.Count))
            {
                var cell = dataGrid.GetCell(row, col);
                double width = 0;
                if (cell.Content is TextBlock)
                    width = (cell.Content as TextBlock).TextWidth();
                else if (cell.Content is TextBox)
                    width = (cell.Content as TextBox).TextWidth();

                if (cell.Content is FrameworkElement)
                {
                    var widget = cell.Content as FrameworkElement;
                    width = width + widget.Margin.Left + widget.Margin.Right;
                }

                max_widths[col] = Math.Max(max_widths[col], 
                                           width*ugly_factor+cell.Padding.Left+cell.Padding.Right);
            }

        double width_diff = 0;

        foreach (var col in Enumerable.Range(0, dataGrid.Columns.Count))
            width_diff += Math.Max(0,max_widths[col] - dataGrid.Columns[col].ActualWidth);

        if (width_diff > 0)
            window.Width = window.ActualWidth+ width_diff;
    }
21
greenoldman

Commentaire de Marko est la réponse:

Je suis un peu perdu avec votre logique de redimensionnement. Tout d'abord, si vous définissez le SizeToContent="WidthAndHeight" De la fenêtre, la fenêtre est affichée à la taille complète de la grille de données. D'un autre côté, si vous affichez une fenêtre à une taille prédéfinie et que vous souhaitez redimensionner la fenêtre une fois la fenêtre affichée, le redimensionnement à la grille de données souhaitée avec est alors très contre-intuitif. Parce que le redimensionnement passera de disons 100px à 400px, mais que se passe-t-il si je veux redimensionner à seulement 250px ... (en supposant que vous redimensionnez en faisant glisser le coin de la fenêtre)

2
greenoldman

Je viens de sortir du même problème où je devais donner des options dans la colonne d'une grille de données pour adapter sa largeur selon le contenu de l'en-tête et de la cellule. J'ai utilisé le code suivant:

private void FitToContent()
    {
        // where dg is my data grid's name...
        foreach (DataGridColumn column in dg.Columns)
        {
            //if you want to size ur column as per the cell content
            column.Width = new DataGridLength(1.0, DataGridLengthUnitType.SizeToCells);
            //if you want to size ur column as per the column header
            column.Width = new DataGridLength(1.0, DataGridLengthUnitType.SizeToHeader);
            //if you want to size ur column as per both header and cell content
            column.Width = new DataGridLength(1.0, DataGridLengthUnitType.Auto);
        }
    }

J'ai également fourni une option sur les colonnes pour s'adapter à l'affichage (largeur de la grille de données). pour cela, j'ai utilisé le même code ci-dessus avec la modification mineure suivante:

column.Width = new DataGridLength(1.0, DataGridLengthUnitType.Star);

POUR TOUTES LES COLONNES .... :) Assurez-vous de conserver HorizontalScrollVisibility sur Auto.

21
Sonal Savaratkar

Si j'ai bien compris votre question et que vous souhaitez:

  1. Un DataGrid où les colonnes sont aussi larges que leur contenu le plus large.
  2. Le DataGrid s'adapte à son contenu.

Ceci peut être réalisé avec la liaison de données:

<DataGrid AutoGenerateColumns="False"
          EnableRowVirtualization="True"
          Height="111"
          HorizontalAlignment="Left"
          ItemsSource="{Binding}"
          Margin="72,203,0,0"
          Name="dataGrid"
          RowDetailsVisibilityMode="VisibleWhenSelected"
          VerticalAlignment="Top"
          Width="{Binding Path=ActualWidth, ElementName=grid}">
    <DataGrid.Columns>
        <DataGridTextColumn x:Name="Column1"
                            Binding="{Binding Path=Something1}"
                            Header="Column1" 
                            Width="Auto"  />
        <DataGridTextColumn x:Name="Column2"
                            Binding="{Binding Path=Something2}"
                            Header="Column2"
                            Width="*" />
    </DataGrid.Columns>
</DataGrid>

Ici, la première colonne est aussi large que nécessaire et la seconde est un espace étalé qui reste. Cependant, la largeur du DataGrid est la même que la largeur de la grille qui l'entoure, donc le résultat souhaité est atteint.

8
GC87

SizeToCells est ce qui a fonctionné pour moi. J'aime ça parce que c'est en XAML et c'est concis.

<DataGridTextColumn x:Name="nameColumn" Binding="{Binding Name}" Header="Name" Width="SizeToCells"/>
1
Pale Ale

Dans mon cas, j'ai trouvé DataGrid utiliser HorizontalAlignment="Stretch" VerticalAlignment="Stretch" par défaut, je l'ai donc réglé sur

<DataGrid ItemsSource="{Binding Source}" Width="Auto" Height="Auto" 
          HorizontalAlignment="Center" VerticalAlignment="Center"/>

Fonctionne ensuite.

1
yu yang Jian