web-dev-qa-db-fra.com

La largeur de colonne DataGrid ne se met pas à jour automatiquement

<DataGridTextColumn Binding="{Binding Name}" Width="*"/>
<DataGridTextColumn Binding="{Binding Change}" Width="Auto"/>

Lorsque la valeur de Change est mise à jour, sa colonne ne se met pas à jour pour s'adapter à la nouvelle valeur. La colonne reste donc trop petite et la valeur est tronquée.
Des idées?

26
user626528

Le DataGrid augmentera la taille des colonnes pour s'adapter à mesure que les données s'allongent, mais il ne diminue pas automatiquement la taille des colonnes lorsque la longueur des données diminue. Dans votre exemple, vous alignez à droite la colonne "Modifier" et utilisez le reste de l'espace pour la colonne "Nom".

À présent, lorsqu'une propriété "Modifier" devient suffisamment grande pour augmenter la largeur de la colonne, la colonne "Nom" refuse de se réduire pour s'adapter, vous devez donc forcer vous-même un rafraîchissement.

Les étapes suivantes devraient le faire pour vous (j'ai inclus un exemple d'application à démo):

1) Dans vos liaisons DataGridTextColumn (toutes sauf votre colonne de taille *), définissez NotifyTargetUpdated = True.
2) Sur votre DataGrid, ajoutez un gestionnaire à l'événement TargetUpdated.
3) Dans votre gestionnaire d'événements TargetUpdated:
- a) Définissez la largeur de la colonne de taille * du DataGrid sur 0.
- b) Appelez la méthode UpdateLayout () sur le DataGrid.
- c) Redéfinissez la largeur de la colonne de taille * du DataGrid sur le nouveau DataGridLength (1, DataGridLengthUnitType.Star)

Exemple XAML:

<Window x:Class="DataGridTest.MainWindow"
        xmlns="http://schemas.Microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.Microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="350" Width="525">
    <Window.Resources>
        <CollectionViewSource x:Key="MyObjectCollection" />
    </Window.Resources>
    <DockPanel>
        <Button DockPanel.Dock="Bottom" Content="Click to Make Item 1s Text Longer" Click="Button_Click" />
        <Grid>
            <DataGrid x:Name="dg" ItemsSource="{Binding Source={StaticResource MyObjectCollection}}" AutoGenerateColumns="False" TargetUpdated="dg_TargetUpdated">
                <DataGrid.Columns>
                    <DataGridTextColumn Binding="{Binding First}" Width="1*"/>
                    <DataGridTextColumn Binding="{Binding Last, NotifyOnTargetUpdated=True}"  Width="Auto" />
                </DataGrid.Columns>
            </DataGrid>
        </Grid>

    </DockPanel>
</Window>

Exemple de code derrière:

using System;
using System.Collections.ObjectModel;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.ComponentModel;

namespace DataGridTest
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        private ObservableCollection<MyObject> myObjectList = new ObservableCollection<MyObject>();

        public MainWindow()
        {
            InitializeComponent();
            (this.FindResource("MyObjectCollection") as CollectionViewSource).Source = this.myObjectList;
            this.myObjectList.Add(new MyObject() { First = "Bob", Last = "Jones" });
            this.myObjectList.Add(new MyObject() { First = "Jane", Last = "Doe" });
        }

        private void Button_Click(object sender, RoutedEventArgs e)
        {
            this.myObjectList[0].Last = "BillyOBrian";
        }

        private void dg_TargetUpdated(object sender, DataTransferEventArgs e)
        {
            dg.Columns[0].Width = 0;
            dg.UpdateLayout();
            dg.Columns[0].Width = new DataGridLength(1, DataGridLengthUnitType.Star);
        }
    }

    public class MyObject : INotifyPropertyChanged
    {
        private string firstName;
        public string First
        {
            get { return this.firstName; }
            set
            {
                if (this.firstName != value)
                {
                    this.firstName = value;
                    NotifyPropertyChanged("First");
                }
            }
        }

        private string lastName;
        public string Last
        {
            get { return this.lastName; }
            set
            {
                if (this.lastName != value)
                {
                    this.lastName = value;
                    NotifyPropertyChanged("Last");
                }
            }
        }

        public MyObject() { }

        #region -- INotifyPropertyChanged Contract --

        public event PropertyChangedEventHandler PropertyChanged;

        private void NotifyPropertyChanged(String info)
        {
            if (PropertyChanged != null)
            {
                PropertyChanged(this, new PropertyChangedEventArgs(info));
            }
        }

        #endregion INotifyPropertyChanged Contract
    }
}
55
Scott

j'ai eu un problème similaire avec ma listview, la solution que j'ai trouvée sur comment-autosize-et-aligner à droite-gridviewcolumn-data-in-wpf ici sur stackoverflow.

Dans mon cas, il ajoutait ce morceau de code dans le gestionnaire d'événements collectionchanged de la collection observable à laquelle la vue de liste était liée:

void listview_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e) {
        // this is a listview control
        GridView view = this.View as GridView;
        foreach(GridViewColumn c in view.Columns) {
            if(double.IsNaN(c.Width)) {
                c.Width = c.ActualWidth;
            }
            c.Width = double.NaN;
        }
    }

Cela fonctionne pour moi, bien que parfois l'utilisateur puisse remarquer un "clignotement" sur les colonnes.

2
Zee

WPF redimensionnera simplement la largeur de colonne d'une grille de données définie sur Auto si nécessaire, c'est-à-dire: le contenu ne peut pas être affiché entièrement. Ainsi, lorsque la largeur du contenu diminue, la colonne ne se redimensionne pas car le contenu peut toujours être vu entièrement.

la seule façon que je peux voir pour forcer wpf à recalculer les largeurs des colonnes serait de les forcer toutes à 0 puis de revenir à auto dans le code derrière, avec un ou deux updateLayout () ajoutés, mais ce n'est pas une programmation très agréable : - /

en gros, dans votre code derrière:

foreach (DataGridColumn c in dg.Columns)
    c.Width = 0;

// Update your DG's source here

foreach (DataGridColumn c in dg.Columns)
    c.Width = DataGridLength.Auto;

et vous avez probablement besoin d'une dg.UpdateLayout() ou deux quelque part là-dedans (après la mise à jour et le retour à auto probablement)

1
Anand Thangappan

Vous pouvez résoudre ce problème en définissant la propriété width de la colonne dans un paramètre de style et en liant ce paramètre à une propriété de l'objet auquel vous effectuez la liaison.

<DataGridTextColumn Binding="{Binding Change}" ElementStyle="{StaticResource ChangeColumnStyle}"/>

Dans votre ResourceDictionary:

<Style TargetType="{x:Type DataGridTextColumn }" x:Key="ChangeColumnStyle">
   <Setter Property="Width" Value="{Binding ColumnWidth}"
</Style>

ColumnWidth doit être une propriété de votre objet. Maintenant, si vous mettez à jour cette propriété à partir du setter de votre propriété 'Change' (en utilisant un algorithme auto-défini, en tenant compte de choses comme la police), et en appelant:

RaisePropertyChanged("ColumnWidth");

Il devrait mettre à jour la largeur de votre colonne.

 public int Change
   {
      get { return m_change; }
      set
      {
         if (m_change != value)
         {
            m_change = value;
            ColumnWidth = WidthAlgo(numberOfCharacters);
            RaisePropertyChanged("Change");
            RaisePropertyChanged("ColumnWidth");
         }
      }
   }
0
Edwin de Koning