Esporta (0) Stampa
Espandi tutto

Cenni preliminari sui modelli di dati

Aggiornamento: novembre 2007

Il modello di creazione di modelli di dati WPF consente una notevole flessibilità per definire la presentazione dei dati. I controlli WPF dispongono di funzionalità incorporate per supportare la personalizzazione della presentazione dei dati. In questo argomento viene innanzitutto illustrato come definire un oggetto DataTemplate e vengono presentate altre funzionalità di creazione di modelli di dati, quali la selezione di modelli in base a logica personalizzata e il supporto per la visualizzazione di dati gerarchici.

Nel presente argomento sono contenute le seguenti sezioni.

In questo argomento vengono illustrate le funzionalità relative ai modelli di dati, non vengono presentati concetti di associazione dati. Per informazioni sui concetti di base relativi all'associazione dati, vedere Cenni preliminari sull'associazione dati.

DataTemplate concerne la presentazione dei dati e rappresenta una delle molte funzionalità fornite dal modello di creazione di stili e modelli WPF. Per un'introduzione al modello di creazione di stili e modelli WPF, ad esempio per informazioni sull'utilizzo di un oggetto Style per l'impostazione di proprietà sui controlli, vedere l'argomento Applicazione di stili e modelli.

È inoltre importante comprendere il concetto di Resources, essenziale per rendere riutilizzabili oggetti quali Style e DataTemplate. Per ulteriori informazioni sulle risorse, vedere Cenni preliminari sulle risorse.

Nella presente sezione sono contenute le seguenti sottosezioni.

Di seguito viene fornito un esempio di associazione dati con cui viene illustrata l'importanza di DataTemplate. In questo esempio, ListBox è associato a un elenco di oggetti Task. Ogni oggetto Task dispone di TaskName (stringa), Description (stringa), Priority (int) e una proprietà di tipo TaskType, costituita da Enum con valori Home e Work.

<Window x:Class="SDKSample.Window1"
  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  xmlns:local="clr-namespace:SDKSample"
  Title="Introduction to Data Templating Sample">
  <Window.Resources>
    <local:Tasks x:Key="myTodoList"/>


...



</Window.Resources>
  <StackPanel>
    <TextBlock Name="blah" FontSize="20" Text="My Task List:"/>
    <ListBox Width="400" Margin="10"
             ItemsSource="{Binding Source={StaticResource myTodoList}}"/>


...


  </StackPanel>
</Window>


Senza DataTemplate

Senza DataTemplate, l'oggetto ListBox attualmente si presenta come segue:

Schermata dell'esempio Introduction to Data Templating

In assenza di istruzioni specifiche, per impostazione predefinita ListBox chiama ToString quando tenta di visualizzare gli oggetti nell'insieme. Pertanto, se l'oggetto Task esegue l'override del metodo ToString, ListBox visualizza la rappresentazione di stringa di ogni oggetto di origine nell'insieme sottostante.

Ad esempio, se la classe Task esegue l'override del metodo ToString in questo modo, dove name è il campo per la proprietà TaskName:

public override string ToString()
{
    return name.ToString();
}


ListBox avrà l'aspetto seguente:

Schermata dell'esempio Introduction to Data Templating

Ciò è tuttavia limitante e poco flessibile. Inoltre, in caso di associazione a dati XML, non sarà possibile eseguire l'override di ToString.

Definizione di DataTemplate semplice

La soluzione consiste nel definire un oggetto DataTemplate. A tale scopo è possibile impostare la proprietà ItemTemplate di ListBox su DataTemplate. Quanto viene specificato in DataTemplate diventa la struttura visiva dell'oggetto dati. L'oggetto DataTemplate seguente è abbastanza semplice. Vengono fornite istruzioni affinché ogni elemento venga visualizzato come tre elementi TextBlock in un oggetto StackPanel. Ogni elemento TextBlock è associato a una proprietà della classe Task.

<ListBox Width="400" Margin="10"
         ItemsSource="{Binding Source={StaticResource myTodoList}}">
   <ListBox.ItemTemplate>
     <DataTemplate>
       <StackPanel>
         <TextBlock Text="{Binding Path=TaskName}" />
         <TextBlock Text="{Binding Path=Description}"/>
         <TextBlock Text="{Binding Path=Priority}"/>
       </StackPanel>
     </DataTemplate>
   </ListBox.ItemTemplate>
 </ListBox>


I dati sottostanti per gli esempi riportati in questo argomento sono rappresentati da un insieme di oggetti CLR. In caso di associazione a dati XML, i concetti fondamentali sono gli stessi, ma vi è una leggera differenza sintattica. Ad esempio, anziché avere Path=TaskName, è necessario impostare XPath su @TaskName (se TaskName è un attributo del nodo XML).

Ora ListBox avrà l'aspetto seguente:

Schermata dell'esempio Introduction to Data Templating

Creazione di DataTemplate come una risorsa

Nell'esempio precedente è stato definito l'oggetto DataTemplate inline. È più comune definire questo oggetto nella sezione risorse in modo che possa essere riutilizzabile, come nell'esempio seguente:

<Window.Resources>


...


<DataTemplate x:Key="myTaskTemplate">
  <StackPanel>
    <TextBlock Text="{Binding Path=TaskName}" />
    <TextBlock Text="{Binding Path=Description}"/>
    <TextBlock Text="{Binding Path=Priority}"/>
  </StackPanel>
</DataTemplate>


...


</Window.Resources>


È ora possibile utilizzare myTaskTemplate come risorsa, come nell'esempio seguente:

<ListBox Width="400" Margin="10"
         ItemsSource="{Binding Source={StaticResource myTodoList}}"
         ItemTemplate="{StaticResource myTaskTemplate}"/>


Poiché myTaskTemplate è una risorsa, è possibile utilizzarlo in altri controlli che dispongono di una proprietà che accetta un tipo DataTemplate. Come illustrato nell'esempio precedente, per gli oggetti ItemsControl quali ListBox, si tratta della proprietà ItemTemplate. Per gli oggetti ContentControl, si tratta della proprietà ContentTemplate.

Proprietà DataType

La classe DataTemplate ha una proprietà DataType molto simile alla proprietà TargetType della classe Style. Pertanto, anziché specificare x:Key per l'oggetto DataTemplate nell'esempio precedente, è possibile eseguire le operazioni seguenti:

<DataTemplate DataType="{x:Type local:Task}">
  <StackPanel>
    <TextBlock Text="{Binding Path=TaskName}" />
    <TextBlock Text="{Binding Path=Description}"/>
    <TextBlock Text="{Binding Path=Priority}"/>
  </StackPanel>
</DataTemplate>


Questo oggetto DataTemplate viene automaticamente applicato a tutti gli oggetti Task. Si noti che in questo caso x:Key viene impostato implicitamente. Pertanto, se si assegna a questo oggetto DataTemplate un valore x:Key, viene eseguito l'override del valore x:Key implicito e DataTemplate non viene applicato automaticamente.

Se si associa un oggetto ContentControl a un insieme di oggetti Task, ContentControl non utilizza automaticamente l'oggetto DataTemplate illustrato in precedenza. Questo comportamento è dovuto al fatto che l'associazione su un oggetto ContentControl richiede ulteriori informazioni per distinguere se si desidera effettuare l'associazione a un insieme intero o a singoli oggetti. Se ContentControl sta rilevando la selezione di un tipo ItemsControl, è possibile impostare la proprietà Path dell'oggetto ContentControl associato a "/" per indicare che si è interessati all'elemento corrente. Per un esempio, vedere Procedura: eseguire l'associazione di un insieme e visualizzare informazioni in base alla selezione effettuata. In caso contrario, è necessario specificare in modo esplicito DataTemplate impostando la proprietà ContentTemplate.

La proprietà DataType è particolarmente utile quando si ha un oggetto CompositeCollection costituito da tipi diversi di oggetti dati. Per un esempio, vedere Procedura: implementare un oggetto CompositeCollection.

Attualmente i dati vengono visualizzati con le informazioni necessarie, ma sono possibili miglioramenti. Di seguito verrà migliorata la presentazione con l'aggiunta di Border, Grid e alcuni elementi TextBlock che descrivono i dati visualizzati.


<DataTemplate x:Key="myTaskTemplate">
  <Border Name="border" BorderBrush="Aqua" BorderThickness="1"
          Padding="5" Margin="5">
    <Grid>
      <Grid.RowDefinitions>
        <RowDefinition/>
        <RowDefinition/>
        <RowDefinition/>
      </Grid.RowDefinitions>
      <Grid.ColumnDefinitions>
        <ColumnDefinition />
        <ColumnDefinition />
      </Grid.ColumnDefinitions>
      <TextBlock Grid.Row="0" Grid.Column="0" Text="Task Name:"/>
      <TextBlock Grid.Row="0" Grid.Column="1" Text="{Binding Path=TaskName}" />
      <TextBlock Grid.Row="1" Grid.Column="0" Text="Description:"/>
      <TextBlock Grid.Row="1" Grid.Column="1" Text="{Binding Path=Description}"/>
      <TextBlock Grid.Row="2" Grid.Column="0" Text="Priority:"/>
      <TextBlock Grid.Row="2" Grid.Column="1" Text="{Binding Path=Priority}"/>
    </Grid>
  </Border>


...


</DataTemplate>


Nella schermata riportato di seguito viene illustrato l'oggetto ListBox con l'oggetto DataTemplate modificato:

Schermata dell'esempio Introduction to Data Templating

È possibile impostare HorizontalContentAlignment su Stretch sull'oggetto ListBox per assicurarsi che la larghezza degli elementi occupi l'intero spazio:

<ListBox Width="400" Margin="10"
     ItemsSource="{Binding Source={StaticResource myTodoList}}"
     ItemTemplate="{StaticResource myTaskTemplate}" 
     HorizontalContentAlignment="Stretch"/>


Con la proprietà HorizontalContentAlignment impostata su Stretch, ListBox ha ora l'aspetto seguente:

Schermata dell'esempio Introduction to Data Templating

Utilizzo di DataTrigger per applicare i valori di proprietà

Nella presentazione corrente non viene indicato se Task è un'attività di tipo domestico o aziendale. Tenere presente che l'oggetto Task ha una proprietà TaskType di tipo TaskType che è un'enumerazione con valori Home e Work.

Nell'esempio riportato di seguito DataTrigger imposta l'oggetto BorderBrush dell'elemento denominato border su Yellow se la proprietà TaskType è TaskType.Home.

<DataTemplate x:Key="myTaskTemplate">


...


<DataTemplate.Triggers>
  <DataTrigger Binding="{Binding Path=TaskType}">
    <DataTrigger.Value>
      <local:TaskType>Home</local:TaskType>
    </DataTrigger.Value>
    <Setter TargetName="border" Property="BorderBrush" Value="Yellow"/>
  </DataTrigger>
</DataTemplate.Triggers>


...


</DataTemplate>


L'applicazione ha ora l'aspetto seguente. Le attività domestiche sono visualizzate con un bordo giallo, mentre quelle aziendali hanno un bordo azzurro:

Schermata dell'esempio Introduction to Data Templating

In questo esempio l'oggetto DataTrigger utilizza Setter per impostare un valore di proprietà. Anche le classi trigger dispongono di proprietà EnterActions e ExitActions che consentono di avviare un insieme di azioni, ad esempio le animazioni. È inoltre disponibile una classe MultiDataTrigger che consente di applicare modifiche basate su più valori di proprietà con associazione a dati.

In alternativa, per ottenere lo stesso effetto è possibile associare la proprietà BorderBrush alla proprietà TaskType e utilizzare un convertitore di valori per restituire il colore in base al valore TaskType. Per un esempio simile, vedere Procedura: alternare il colore di sfondo delle righe di un ListView. La creazione dell'effetto precedente utilizzando un convertitore offre una maggiore efficienza in termini di prestazioni. Inoltre, la creazione di un convertitore personalizzato garantisce maggiore flessibilità grazie alla possibilità di utilizzare logica personalizzata. La scelta ottimale dipende in ultima analisi dallo scenario e dalle preferenze. Per informazioni su come scrivere un convertitore, vedere IValueConverter.

Elementi appartenenti a DataTemplate

Nell'esempio precedente, è stato inserito un trigger in DataTemplate utilizzando la proprietà DataTemplate.Triggers. L'oggetto Setter del trigger imposta il valore di una proprietà di un elemento (elemento Border) incluso in DataTemplate. Tuttavia, se le proprietà relative ai Setters non sono proprietà di elementi inclusi nell'oggetto DataTemplate corrente, può essere consigliabile impostare le proprietà utilizzando un oggetto Style per la classe ListBoxItem (se il controllo associato è un oggetto ListBox). Ad esempio, se si desidera che l'oggetto Trigger animi il valore Opacity dell'elemento quando si posiziona il mouse su un elemento, occorre definire trigger in uno stile ListBoxItem. Per un esempio, vedere Esempio di introduzione agli stili e ai modelli.

In genere, tenere presente che l'oggetto DataTemplate viene applicato a ogni oggetto ListBoxItem generato (per ulteriori informazioni sulle effettive modalità di applicazione, vedere la pagina relativa a ItemTemplate). L'oggetto DataTemplate riguarda esclusivamente la presentazione e l'aspetto degli oggetti dati. Nella maggior parte dei casi, tutti gli altri aspetti della presentazione, ad esempio l'aspetto di un elemento quando viene selezionato o il tipo di layout applicato dall'oggetto ListBox per gli elementi, non fanno parte della definizione di DataTemplate. Per un esempio, vedere la sezione Applicazione di stili e modelli di ItemsControl.

Nella sezione Proprietà DataType Property, è stato illustrato come definire modelli di dati diversi per diversi oggetti dati. Si tratta di una soluzione particolarmente utile quando è presente un oggetto CompositeCollection di diversi tipi o insiemi con elementi di diversi tipi. Nella sezione Utilizzo di DataTrigger per applicare valori di proprietà, è stato illustrato che se è presente un insieme dello stesso tipo di oggetti dati è possibile creare DataTemplate e utilizzare trigger per applicare modifiche in base ai valori di proprietà di ogni oggetto dati. Tuttavia, i trigger consentono di applicare valori di proprietà o di avviare animazioni, ma non offrono la flessibilità necessaria per ricostruire la struttura degli oggetti dati. In alcuni scenari può essere necessario creare un oggetto DataTemplate diverso per gli oggetti dati dello stesso tipo, ma con proprietà diverse.

Ad esempio, può essere opportuno fornire un aspetto completamente diverso a un oggetto Task quando il relativo valore Priority è pari a 1, affinché funga da avviso. In tal caso, creare un oggetto DataTemplate per la visualizzazione di oggetti Task con priorità elevata. Verrà quindi aggiunto il seguente oggetto DataTemplate alla sezione risorse:

<DataTemplate x:Key="importantTaskTemplate">
  <DataTemplate.Resources>
    <Style TargetType="TextBlock">
      <Setter Property="FontSize" Value="20"/>
    </Style>
  </DataTemplate.Resources>
  <Border Name="border" BorderBrush="Red" BorderThickness="1"
          Padding="5" Margin="5">
    <DockPanel HorizontalAlignment="Center">
      <TextBlock Text="{Binding Path=Description}" />
      <TextBlock>!</TextBlock>
    </DockPanel>
  </Border>
</DataTemplate>


Si noti che in questo esempio viene utilizzata la proprietà DataTemplate.Resources. Le risorse definite in questa sezione sono condivise dagli elementi in DataTemplate.

Per fornire la logica per scegliere quale DataTemplate utilizzare in base al valore Priority dell'oggetto dati, creare una sottoclasse di DataTemplateSelector ed eseguire l'override del metodo SelectTemplate. Nell'esempio riportato di seguito il metodo SelectTemplate fornisce la logica per restituire il modello adatto in base al valore della proprietà Priority. Il modello da restituire viene trovato nelle risorse dell'elemento Window di protezione.

using System.Windows;
using System.Windows.Controls;

namespace SDKSample
{
    public class TaskListDataTemplateSelector : DataTemplateSelector
    {
        public override DataTemplate
            SelectTemplate(object item, DependencyObject container)
        {
            if (item != null && item is Task)
            {
                Task taskitem = item as Task;
                Window window = Application.Current.MainWindow;

                if (taskitem.Priority == 1)
                    return
                        window.FindResource("importantTaskTemplate") as DataTemplate;
                else
                    return
                        window.FindResource("myTaskTemplate") as DataTemplate;
            }

            return null;
        }
    }
}


È quindi possibile dichiarare l'oggetto TaskListDataTemplateSelector come risorsa:

<Window.Resources>


...


<local:TaskListDataTemplateSelector x:Key="myDataTemplateSelector"/>


...


</Window.Resources>


Per utilizzare la risorsa selettore di modelli, assegnarla alla proprietà ItemTemplateSelector di ListBox. ListBox chiama il metodo SelectTemplate di TaskListDataTemplateSelector per ciascuno degli elementi dell'insieme sottostante. La chiamata passa l'oggetto dati come parametro dell'elemento. L'oggetto DataTemplate restituito dal metodo viene quindi applicato a tale oggetto dati.

<ListBox Width="400" Margin="10"
         ItemsSource="{Binding Source={StaticResource myTodoList}}"
         ItemTemplateSelector="{StaticResource myDataTemplateSelector}"
         HorizontalContentAlignment="Stretch"/>


Dopo aver posizionato il selettore di modelli, ListBox ha l'aspetto seguente:

Schermata dell'esempio Introduction to Data Templating

Con questo passaggio si conclude la discussione di questo esempio. Per l'esempio completo, vedere Esempio di introduzione ai modelli di dati.

Anche se ItemsControl non è l'unico tipo di controllo con cui è possibile utilizzare DataTemplate, costituisce uno scenario molto comune per l'associazione di ItemsControl a un insieme. Nella sezione Elementi appartenenti a DataTemplate è stato illustrato che la definizione di DataTemplate deve riguardare solo la presentazione dei dati. Per sapere quando non è consigliabile utilizzare DataTemplate, è importante comprendere le diverse proprietà di stile e modello fornite da ItemsControl. Nell'esempio riportato di seguito viene illustrata la funzione di ciascuna di queste proprietà. L'oggetto ItemsControl di questo esempio è associato allo stesso insieme Tasks dell'esempio precedente. A scopo dimostrativo, gli stili e i modelli di questo esempio sono tutti dichiarati inline.

<ItemsControl Margin="10"
              ItemsSource="{Binding Source={StaticResource myTodoList}}">
  <!--The ItemsControl has no default visual appearance.
      Use the Template property to specify a ControlTemplate to define
      the appearance of an ItemsControl. The ItemsPresenter uses the specified
      ItemsPanelTemplate (see below) to layout the items. If an
      ItemsPanelTemplate is not specified, the default is used. (For ItemsControl,
      the default is an ItemsPanelTemplate that specifies a StackPanel.-->
  <ItemsControl.Template>
    <ControlTemplate TargetType="ItemsControl">
      <Border BorderBrush="Aqua" BorderThickness="1" CornerRadius="15">
        <ItemsPresenter/>
      </Border>
    </ControlTemplate>
  </ItemsControl.Template>
  <!--Use the ItemsPanel property to specify an ItemsPanelTemplate
      that defines the panel that is used to hold the generated items.
      In other words, use this property if you want to affect
      how the items are laid out.-->
  <ItemsControl.ItemsPanel>
    <ItemsPanelTemplate>
      <WrapPanel />
    </ItemsPanelTemplate>
  </ItemsControl.ItemsPanel>
  <!--Use the ItemTemplate to set a DataTemplate to define
      the visualization of the data objects. This DataTemplate
      specifies that each data object appears with the Proriity
      and TaskName on top of a silver ellipse.-->
  <ItemsControl.ItemTemplate>
    <DataTemplate>
      <DataTemplate.Resources>
        <Style TargetType="TextBlock">
          <Setter Property="FontSize" Value="18"/>
          <Setter Property="HorizontalAlignment" Value="Center"/>
        </Style>
      </DataTemplate.Resources>
      <Grid>
        <Ellipse Fill="Silver"/>
        <StackPanel>
          <TextBlock Margin="3,3,3,0"
                     Text="{Binding Path=Priority}"/>
          <TextBlock Margin="3,0,3,7"
                     Text="{Binding Path=TaskName}"/>
        </StackPanel>
      </Grid>
    </DataTemplate>
  </ItemsControl.ItemTemplate>
  <!--Use the ItemContainerStyle property to specify the appearance
      of the element that contains the data. This ItemContainerStyle
      gives each item container a margin and a width. There is also
      a trigger that sets a tooltip that shows the description of
      the data object when the mouse hovers over the item container.-->
  <ItemsControl.ItemContainerStyle>
    <Style>
      <Setter Property="Control.Width" Value="100"/>
      <Setter Property="Control.Margin" Value="5"/>
      <Style.Triggers>
        <Trigger Property="Control.IsMouseOver" Value="True">
          <Setter Property="Control.ToolTip"
                  Value="{Binding RelativeSource={x:Static RelativeSource.Self},
                          Path=Content.Description}"/>
        </Trigger>
      </Style.Triggers>
    </Style>
  </ItemsControl.ItemContainerStyle>
</ItemsControl>


Nella figura riportata di seguito viene illustrato il risultato del rendering dell'esempio:

Schermata di esempio ItemsControl

Si noti che, anziché utilizzare ItemTemplate, è possibile utilizzare ItemTemplateSelector. Per un esempio, fare riferimento alla sezione precedente. Analogamente, anziché utilizzare ItemContainerStyle, è possibile utilizzare ItemContainerStyleSelector.

Vi sono altre due proprietà correlate allo stile di ItemsControl che non sono illustrate qui, ovvero GroupStyle e GroupStyleSelector.

Finora sono state analizzate unicamente l'associazione e la visualizzazione di un solo insieme. Talvolta è presente un insieme che contiene altri insiemi. La classe HierarchicalDataTemplate è progettata per essere utilizzata con tipi HeaderedItemsControl per visualizzare tali dati. Nell'esempio riportato di seguito ListLeagueList è un elenco di oggetti League. Ogni oggetto League ha un oggetto Name e un insieme di oggetti Division. Ogni oggetto Division ha un oggetto Name e un insieme di oggetti Team e ciascun oggetto Team ha un oggetto Name.

<Window x:Class="SDKSample.Window1"
  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  Title="HierarchicalDataTemplate Sample"
  xmlns:src="clr-namespace:SDKSample">
  <DockPanel>
    <DockPanel.Resources>
      <src:ListLeagueList x:Key="MyList"/>

      <HierarchicalDataTemplate DataType    = "{x:Type src:League}"
                                ItemsSource = "{Binding Path=Divisions}">
        <TextBlock Text="{Binding Path=Name}"/>
      </HierarchicalDataTemplate>

      <HierarchicalDataTemplate DataType    = "{x:Type src:Division}"
                                ItemsSource = "{Binding Path=Teams}">
        <TextBlock Text="{Binding Path=Name}"/>
      </HierarchicalDataTemplate>

      <DataTemplate DataType="{x:Type src:Team}">
        <TextBlock Text="{Binding Path=Name}"/>
      </DataTemplate>
    </DockPanel.Resources>

    <Menu Name="menu1" DockPanel.Dock="Top" Margin="10,10,10,10">
        <MenuItem Header="My Soccer Leagues"
                  ItemsSource="{Binding Source={StaticResource MyList}}" />
    </Menu>

    <TreeView>
      <TreeViewItem ItemsSource="{Binding Source={StaticResource MyList}}" Header="My Soccer Leagues" />
    </TreeView>

  </DockPanel>
</Window>


Nell'esempio viene illustrata la possibilità di utilizzare HierarchicalDataTemplate per visualizzare in modo semplice dati elenco che contengono altri elenchi. Di seguito viene riportata una schermata dell'esempio.

Schermata di esempio HierarchicalDataTemplate

Per l'esempio completo, vedere Esempio di visualizzazione di dati gerarchici.

Aggiunte alla community

AGGIUNGI
Microsoft sta conducendo un sondaggio in linea per comprendere l'opinione degli utenti in merito al sito Web di MSDN. Se si sceglie di partecipare, quando si lascia il sito Web di MSDN verrà visualizzato il sondaggio in linea.

Si desidera partecipare?
Mostra:
© 2015 Microsoft