In WPF il modo più semplice per visualizzare dei dati in una griglia è quello di utilizzare la ListView. Utilizzando questo controllo però mi sono scontrato più volte con il problema che le colonne non si adattano automaticamente al contenuto ma si costringe l’utente a ridimensionarle a mano. Di seguito propongo una soluzione a questo problema.
La proprietà Width della GridViewColumn, di tipo double, può essere settata ad un valore fisso oppure ad “Auto”; nel secondo caso la proprietà viene inizializzata al valore double.NaN e solamente la prima volta che viene settata la proprietà ItemsSource della ListView viene calcolata la larghezza della colonna (larghezza massima degli oggetti contenuti in quella colonna).
Per fare in modo che WPF ricalcoli la larghezza delle colonne possiamo quindi pensare di resettare la proprietà Width al valore double.NaN ogni volta che viene modificata l’ItemsSource della ListView, attraverso un metodo come questo:
1: private void UpdateColumnsWidth(ListView list)
2: {
3: GridView view = (GridView)list.View;
4: foreach (GridViewColumn col in view.Columns)
5: {
6: col.Width = 0;
7: col.Width = double.NaN;
8: }
9: }
Per ottenere il comportamento desiderato dobbiamo lanciare questo metodo ogni volta che:
- viene settata la proprietà ItemsSource
- viene aggiunto o rimosso un elemento dalla lista
L’oggetto ListView non fornisce un evento che notifica la modifica del valore della proprietà ItemsSource ma, poiché si tratta di una DependencyProperty, è possibile ottenere tale notifica utilizzando il metodo AddValueChanged dell’oggetto DependencyPropertyDescriptor:
1: ...
2: DependencyPropertyDescriptor descriptor = DependencyPropertyDescriptor.FromProperty(ListView.ItemsSourceProperty, typeof(ListView));
3: descriptor.AddValueChanged(lv, new EventHandler(OnItemsSourcePropertyChanged));
4: ...
5:
6: private void OnItemsSourcePropertyChanged(object sender, EventArgs e)
7: {
8: ListView lv = sender as ListView;
9: UpdateColumnsWidth(lv);
10: }
Per avere la notifica quando viene aggiunto o rimosso un elemento, invece, è necessario che la lista che andiamo a settare come ItemsSource implementi l’interfaccia INotifyCollectionChanged (ad esempio, ObservableCollection<T>), che fornisce l’evento CollectionChanged:
1: private void OnItemsSourcePropertyChanged(object sender, EventArgs e)
2: {
3: ...
4:
5: INotifyCollectionChanged itemsSource = lv.ItemsSource as INotifyCollectionChanged;
6: if (itemsSource != null)
7: {
8: itemsSource.CollectionChanged += new NotifyCollectionChangedEventHandler(itemsSource_CollectionChanged);
9: }
10: }
11:
12: void itemsSource_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
13: {
14: UpdateColumnsWidth(lv);
15: }
A questo punto abbiamo ottenuto il comportamento desiderato. Per poter riutilizzare questo meccanismo senza dover riscrivere tutto questo codice per ogni ListView, andiamo a definire una Attached Property:
1: public static readonly DependencyProperty IsEnableAutoSizeProperty =
2: DependencyProperty.RegisterAttached("IsEnableAutoSize",
3: typeof(bool),
4: typeof(ListViewColumnAutoSize),
5: new PropertyMetadata(false, new PropertyChangedCallback(IsEnableAutoSizePropertyChanged)));
6:
7: public static bool GetIsEnableAutoSize(ListView lv)
8: {
9: return (bool)lv.GetValue(IsEnableAutoSizeProperty);
10: }
11:
12: public static void SetIsEnableAutoSize(ListView lv, bool value)
13: {
14: lv.SetValue(IsEnableAutoSizeProperty, value);
15: }
In fase di registrazione dell’attached property, andiamo a specificare nei metadata un metodo di callback per gestire la cambiamento del valore della proprietà. In questo metodo andremo ad abbonarci (o a disabbonarci) alla modifica della proprietà ItemsSource della ListView utilizzando il DependencyPropertyDescriptor, come visto precedentemente:
1: private static void IsEnableAutoSizePropertyChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e)
2: {
3: ListView lv = obj as ListView;
4: DependencyPropertyDescriptor descriptor = DependencyPropertyDescriptor.FromProperty(ListView.ItemsSourceProperty, typeof(ListView));
5: if ((bool)e.NewValue)
6: {
7: descriptor.AddValueChanged(lv, new EventHandler(OnItemsSourcePropertyChanged));
8: }
9: else
10: {
11: descriptor.RemoveValueChanged(lv, new EventHandler(OnItemsSourcePropertyChanged));
12: }
13: }
Nel metodo OnItemsSourcePropertyChanged si andrà a lanciare il metodo UpdateColumnsWidth e ad abbonarsi all’evento CollectionChanged della lista sorgente, come visto in precedenza.
Una volta definita l’attached property è possibile settarla direttamente dallo XAML in questo modo:
1: ...
2: <ListView local:ListViewColumnsAutoSize.IsEnableAutoSize="True">
3: ...
[Scarica l’esempio completo]
Currently rated 5.0 by 3 people
- Currently 5/5 Stars.
- 1
- 2
- 3
- 4
- 5
WPF
wpf, attached properties