web-dev-qa-db-fra.com

Comment puis-je définir la largeur d'un DataGridColumn pour s'adapter au contenu ("Auto"), mais remplir complètement l'espace disponible pour le DataGrid dans MVVM?

J'ai un WPF DataGrid qui contient des données. J'aimerais définir la largeur des colonnes de manière à ce que le contenu tienne dans le contenu et ne soit jamais recadré (au lieu de cela, une barre de défilement horizontale devrait devenir visible). De plus, je veux que la DataGrid remplisse toute la place disponible (je travaille avec une DockPanel). J'utilise le code suivant (simplifié):

<DataGrid ItemsSource="{Binding Table}">
    <DataGrid.Columns>
        <DataGridTextColumn MinWidth="100" Width="Auto" Header="Column 1" Binding="{Binding Property1}" />
        <DataGridTextColumn MinWidth="200" Width="Auto" Header="Column 2" Binding="{Binding Property2}" />
    </DataGrid.Columns>
</DataGrid>

Apparemment, cela ne fonctionne pas immédiatement avec Width="Auto" car cela ressemble toujours à ceci:

DataGrid: only the first part of the row is marked as "selected"

Cela semble évidemment moche. Je voudrais avoir toute la ligne sélectionnée, ou, ce qui serait beaucoup mieux, les colonnes pour remplir toute la largeur, mais comme on peut le voir, cela ne fonctionne pas.

Si j'utilise plutôt Width="*", le contenu des colonnes est recadré, ce qui est encore pire pour moi.

J'ai trouvé une question similaire ici , et une solution de contournement a été posté là. Cela peut fonctionner, mais je travaille avec le modèle MVVM. Le ItemsSource est donc mis à jour dans le ViewModel et je ne peux pas penser à un moyen de le faire à partir de là, car je ne peux pas accéder à la propriété ActualWidth de DataGridColumn. Aussi, je voudrais le faire uniquement en XAML si possible.

J'apprécierais toute aide. Merci!

Edit: Comme je ne sais toujours pas quoi faire à ce sujet, je commence une petite prime. Je serais très heureux d'une suggestion de ce que l'on pourrait faire pour résoudre mon problème. Merci encore!

Edit 2: Après la réponse de saus, j'ai de nouveau réfléchi aux options. Le problème est que je dois mettre à jour les propriétés Width et MinWidth également pendant l'exécution de l'application, donc pas seulement après avoir chargé la fenêtre. J'ai déjà essayé de faire quelque chose comme

column.Width = new DataGridLength(1, DataGridLengthUnitType.Auto);
column.MinWidth = column.ActualWidth;
column.Width = new DataGridLength(1, DataGridLengthUnitType.Star);

dans certains cas, elle est déclenchée lorsque la ItemsSource sous-jacente de la DataGrid est mise à jour. Toutefois, cela ne fonctionne pas, car la propriété ActualWidth ne semble pas changer après la définition de Width sur Auto. Existe-t-il une option pour le "repeindre" de façon à obtenir la propriété ActualWidth mise à jour? Merci!

21
Sören

Je suggère d'utiliser la solution de contournement à laquelle vous avez lié. Ça marche. Dans le codebehind de votre vue, ajoutez ce qui suit au constructeur après InitializeComponent ():

Griddy.Loaded += SetMinWidths; // I named my datagrid Griddy

et définissez SetMinWidths comme:

public void SetMinWidths(object source, EventArgs e )
        {
            foreach (var column in Griddy.Columns)
            {
                column.MinWidth = column.ActualWidth;
                column.Width = new DataGridLength(1, DataGridLengthUnitType.Star);
            }
        }

Je suppose que la raison pour laquelle vous ne souhaitez pas utiliser cette solution est que vous croyez que MVVM interdit le code dans codebehind. Mais comme il s’agit d’une logique tout à fait spécifique, je pense que cela est justifié dans le cas présent. Le tout "MVVM interdit le code dans le codebehind" est un peu une idée fausse. 

Toutefois, si vous êtes lié par des guides de style ou si vous souhaitez que cette logique soit disponible pour toutes les datagrides de votre application, vous pouvez créer un comportement attaché qui fonctionne comme suit:

public class SetMinWidthToAutoAttachedBehaviour 
    {
        public static bool GetSetMinWidthToAuto(DependencyObject obj)
        {
            return (bool)obj.GetValue(SetMinWidthToAutoProperty);
        }

        public static void SetSetMinWidthToAuto(DependencyObject obj, bool value)
        {
            obj.SetValue(SetMinWidthToAutoProperty, value);
        }

        // Using a DependencyProperty as the backing store for SetMinWidthToAuto.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty SetMinWidthToAutoProperty =
            DependencyProperty.RegisterAttached("SetMinWidthToAuto", typeof(bool), typeof(SetMinWidthToAutoAttachedBehaviour), new UIPropertyMetadata(false, WireUpLoadedEvent));

        public static void WireUpLoadedEvent(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            var grid = (DataGrid)d;

            var doIt = (bool)e.NewValue;

            if (doIt)
            {
                grid.Loaded += SetMinWidths;
            }
        }

        public static void SetMinWidths(object source, EventArgs e)
        {
            var grid = (DataGrid)source;

            foreach (var column in grid.Columns)
            {
                column.MinWidth = column.ActualWidth;
                column.Width = new DataGridLength(1, DataGridLengthUnitType.Star);
            }
        }
    }

et ensuite, pour toute grille de données à laquelle vous souhaitez appliquer ce comportement, ajoutez ce qui suit:

<DataGrid ItemsSource="{Binding Table}" local:SetMinWidthToAutoAttachedBehaviour.SetMinWidthToAuto="true">

Et alors votre conscience, ainsi que votre code ci-dessous, seront clairs.

30
saus

Utilisez un TextBlock avec un retour à la ligne dans la colonne du modèle:

<DataGrid ItemsSource="{Binding Table}">      
    <DataGrid.Columns>          
        <DataGridTextColumn MinWidth="100" Width="Auto" Header="Column 1"
                            Binding="{Binding Property1}" />          
        <DataGridTemplateColumn Width="*" Header="Column 2">
            <DataGridTemplateColumn.CellTemplate>
                <DataTemplate>
                    <TextBlock Text="{Binding Property2}" TextWrapping="Wrap" />  
                </DataTemplate>
            </DataGridTemplateColumn.CellTemplate>
        </DataGridTemplateColumn>
    </DataGrid.Columns>  
</DataGrid>
4
Rafa Castaneda

Si vous souhaitez que la colonne ne coupe jamais vos données, ajoutez ce qui suit à votre définition Window/UserControl:

xmlns:System="clr-namespace:System;Assembly=mscorlib"

Ensuite, pour la largeur des colonnes, vous pouvez utiliser pour suivre.

 Width="{x:Static System:Double.NaN}"

Cela fait que la colonne a toujours une largeur suffisante pour accueillir la valeur la plus longue de la colonne.

Comme le code ci-dessus ne fonctionnait pas dans le DataGrid, pourquoi ne pas ajouter un ScrollViewer autour du DataGrid comme ceci:

     <ScrollViewer HorizontalScrollBarVisibility="Auto" 
                  VerticalScrollBarVisibility="Auto">
        <DataGrid ItemsSource="{Binding Table}" AutoGenerateColumns="False">
            <DataGrid.Columns>
                <DataGridTextColumn MinWidth="10" Width="Auto"  Header="Column 1" Binding="{Binding Property1}" />
                <DataGridTextColumn MinWidth="200" Width="*" Header="Column 2" Binding="{Binding Property2}" />                    
            </DataGrid.Columns>
        </DataGrid>
    </ScrollViewer>
2
Wes Grant

que dis-tu de ça:

pour la largeur minimale de vos colonnes, vous configurez une liaison à la largeur de ScrollContent du dataGrid, avec un convertisseur qui divise cette largeur par le nombre de colonnes

vous avez besoin de code derrière pour le convertisseur, mais cela garderait votre structure MVVM intacte.

pourrait être un long coup cependant, n'a pas eu le temps de l'essayer.

Edit: J'ai réfléchi un peu plus avant et il y a un pb: ça ne marchera pas bien si vous avez, par exemple, une énorme colonne et quelques petites. J'ai supposé que tous les cols ont la même largeur, ce qui n'est évidemment pas le cas ici.

Vous voudrez peut-être explorer cette façon si. Utiliser des liaisons sur les largeurs avec des convertisseurs est la seule chose à laquelle je peux penser qui puisse fonctionner: puisque vous avez essentiellement 2 conditions à prendre en compte lors du calcul de la largeur d'une colonne, il n'y aura pas de moyen facile de procéder.

1
David

J'ai ajouté la variable Event à la variable DataGrid qui redéfinit leur largeur par rapport à la largeur réelle de la variable DataGrid:

 private static void OnLoaded(object sender, RoutedEventArgs e)
    {
        DataGrid dg = sender as DataGrid;
        foreach (var column in dg.Columns)
        {
            column.Width = new DataGridLength(dg.ActualWidth / dg.Columns.Count, DataGridLengthUnitType.Pixel);
        }
    }

La ligne:

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

était absolument sans âme. La définition de DataGridLengthUnitType.Star réduit la largeur des colonnes à leur largeur minimale et rend leur largeur statique et non modifiable.

1
Mr.B

utilisez ceci pour toute colonne que vous voulez adapter à son contenu: Width = "SizeToCells"

0
user534957

Forcer la dernière colonne à occuper tout l'espace restant dans la grille de données

grid.Columns[grid.Columns.Count -1].Width = new 
                  DataGridLength(250, DataGridLengthUnitType.Star);
0
Nabeel