martedì 15 marzo 2011

ListView Riordinabile

Una delle esigenze più frequenti durante lo sviluppo di applicazioni con Windows Presentation Foundation è la rappresentazione di dati nel classico formato Grid. In Windows Presentation Foundation, almeno fino alla versione 3.5 del framework, viene utilizzato il controllo ListView.

Il controllo ListView di WPF, infatti, mette a disposizione un tipo di visualizzazione di tipo, appunto, GridView che permette la gestione di più colonne. Nell’esempio seguente viene definito un controllo ListView con modalità di visualizzazione GridView composta da 4 colonne in binding con alcune proprietà dell’entità Person.

<ListView x:Name="listPersons" ItemsSource="{StaticResource persons}"
          BorderThickness="0" Margin="5"
          GridViewColumnHeader.Click="GridViewColumnHeader_Click" >
    <ListView.View>
        <GridView AllowsColumnReorder="True">
            <GridViewColumn Header="ID" 
                            DisplayMemberBinding="{Binding Path=ID}" 
                            Width="50" />
            <GridViewColumn Header="FirstName" 
                            DisplayMemberBinding="{Binding Path=FirstName}" 
                            Width="100"/>
            <GridViewColumn Header="LastName" 
                            DisplayMemberBinding="{Binding Path=LastName}" 
                            Width="100"/>
            <GridViewColumn Header="City" 
                            DisplayMemberBinding="{Binding Path=City}" 
                            Width="80"  />
        </GridView>
    </ListView.View>
</ListView>

Nel listato, come noterete, viene definito l’handler dell’evento Click di GridViewColumnHeader dove verrà gestito, appunto, il riordinamento della colonna cliccata.

Per ottenere l’ordinamento della collection utilizzeremo un metodo chiamato Sort che, attraverso l’uso di una CollectionViewSource effettua l’ordinamento per il campo passato come parametro al metodo (sortBy).

private void Sort(string sortBy, ListSortDirection direction)
{
    ICollectionView dataView = CollectionViewSource.GetDefaultView(listPersons.ItemsSource);

    dataView.SortDescriptions.Clear();
    SortDescription sd = new SortDescription(sortBy, direction);
    dataView.SortDescriptions.Add(sd);
    dataView.Refresh();
}

Passiamo ora alla gestione dell’evento di click sull’header di una colonna della ListView.

GridViewColumnHeader _lastSelectedHeader = null;
ListSortDirection _lastDirection = ListSortDirection.Ascending;

void GridViewColumnHeader_Click(object sender, RoutedEventArgs e)
{
    GridViewColumnHeader selectedHeader = e.OriginalSource as GridViewColumnHeader;
    ListSortDirection direction;

    if (selectedHeader != null)
    {
        if (selectedHeader.Role != GridViewColumnHeaderRole.Padding)
        {
            if (selectedHeader != _lastSelectedHeader)
            {
                direction = ListSortDirection.Ascending;
            }
            else
            {
                if (_lastDirection == ListSortDirection.Ascending)
                {
                    direction = ListSortDirection.Descending;
                }
                else
                {
                    direction = ListSortDirection.Ascending;
                }
            }

            string header = string.Empty;
            if (selectedHeader.Column.CellTemplateSelector != null)
            {
                header = selectedHeader.Column.Header.ToString();
            }
            else if (selectedHeader.Column.DisplayMemberBinding != null)
            {
                header = ((Binding)selectedHeader.Column.DisplayMemberBinding).Path.Path;
            }
            else
            {
                return;
            }

            Sort(header, direction);

            if (direction == ListSortDirection.Ascending)
            {
                selectedHeader.Column.HeaderTemplate =
                  FindResource("HeaderTemplateArrowUp") as DataTemplate;
            }
            else
            {
                selectedHeader.Column.HeaderTemplate =
                  FindResource("HeaderTemplateArrowDown") as DataTemplate;
            }

            // Remove arrow from previously sorted header
            if (_lastSelectedHeader != null && _lastSelectedHeader != selectedHeader)
            {
                _lastSelectedHeader.Column.HeaderTemplate = null;
            }

            _lastSelectedHeader = selectedHeader;
            _lastDirection = direction;
        }
    }
}

Come potete vedere, la logica presente nell’evento è abbastanza semplice. Qui, infatti, viene, innanzitutto, intercettato l’intestazione cliccata, e, in base alla direzione di ordinamento (Ascending o Descending) viene applicato un template diverso per visualizzare una freccetta nell’intestazione per identificarnet, appunto, l’orientamento.
Ovviemente, in questo evento, viene richiamata il metodo Sort dichiarato in precedenza. Il codice XAML per la definizione dei template da utilizzare per la visualizzazione delle freccetta di indicazione nelle intestazioni è il seguente.

<DataTemplate x:Key="HeaderTemplateArrowUp">
    <DockPanel>
        <TextBlock HorizontalAlignment="Center" Text="{Binding}"/>
        <Path x:Name="arrow"
    StrokeThickness = "1" Fill="Black"
    Data="M 5,10 L 15,10 L 10,5 L 5,10"/>
    </DockPanel>
</DataTemplate>

<DataTemplate x:Key="HeaderTemplateArrowDown">
    <DockPanel>
        <TextBlock HorizontalAlignment="Center" Text="{Binding }"/>
        <Path x:Name="arrow"
    StrokeThickness="1" Fill="Black" 
    Data= "M 5,5 L 10,10 L 15,5 L 5,5"/>
    </DockPanel>
</DataTemplate>

Concludendo, quindi, ad un click su qualsiasi intestazione di colonna, viene effettuato l’ordinamento per la colonna in oggetto e sostituito il template dell’intestazione stessa affinchè venga in essa visualizzata una freccetta per identificarne la direzione dell’ordinamento effettuato.

Nessun commento:

Posta un commento