data binding Windows Presentation Foundation: parte 2

 

Shawn Wildermuth

Maggio 2006

Si applica a:
   Microsoft Windows Presentation Foundation

Riepilogo: La parte 2 di questa serie continua a illustrare come usare il data binding basato su XAML per eseguire la manipolazione dei dati nei progetti di Microsoft Windows Presentation Foundation. (21 pagine stampate)

Contenuto

Introduzione
Associazione ai dati del database
Dove siamo?
Riferimenti

Introduzione

La maggior parte degli esempi di Windows Presentation Foundation (WPF) che fanno rumore nella community riguarda il rumore del motore grafico. Per la maggior parte degli sviluppatori di interfacce utente, la maggior parte del loro lavoro sta sviluppando moduli di immissione dei dati quotidiani nel mondo dello sviluppo aziendale. WPF ha una soluzione per risolvere i problemi? È sicuro che...

Associazione ai dati del database

Nella prima parte di questa serie di articoli è stata esaminata la sintassi di associazione non elaborata ed è stato esaminato come associare oggetti semplici agli oggetti XAML. Anche se questa è una parte importante del puzzle, per la maggior parte delle situazioni, il requisito reale sarà il binding ai dati archiviati in un database. Nella maggior parte dei casi, questo è il supporto per l'associazione in due scenari diversi: Dati di database (ad esempio, DataSet, DataTable e DataRows) e oggetti business personalizzati.

Associazione ai dati del database

I database sono ancora al centro della maggior parte dello sviluppo svolto oggi, soprattutto per lo sviluppo aziendale. Per eseguire questa operazione, è possibile usare un semplice esempio di una finestra di dialogo WPF che consentirà a un utente di esplorare i dipendenti in un database. Vogliamo essere in grado di mostrare una piccola quantità di informazioni, inclusa un'immagine del dipendente, nel nostro browser. Sarà necessario caricare una tabella con tutte le informazioni necessarie. A tale scopo, è possibile creare una nuova tabella DataTable con le informazioni del database, come indicato di seguito.

C#

DataTable theTable = new DataTable();
string connString = 
  ConfigurationManager.ConnectionStrings["Northwind"].ConnectionString;
string query = @"SELECT EmployeeID, FirstName, LastName, Title, HireDate, Photo 
                 FROM Employees";

// Fill the Set with the data
using (SqlConnection conn = new SqlConnection(connString))
{
  SqlDataAdapter da = new SqlDataAdapter(query, conn);
  da.Fill(theTable);
}

Visual Basic .NET

Dim theTable As DataTable =  New DataTable() 
String connString = 
  ConfigurationManager.ConnectionStrings("Northwind").ConnectionString
String query = "SELECT EmployeeID, FirstName, LastName, Title, HireDate, Photo " + _
               "  FROM Employees"
 
' Fill the Set with the data
Using conn as New SqlConnection(connString))
  Dim da As SqlDataAdapter =  New SqlDataAdapter(query,conn) 
  da.Fill(theTable)
End Using

Dopo aver ottenuto i dati, è possibile usarli per impostare DataContext, in modo da consentirlo di essere associato nel codice XAML.

C#

// Set the Data Context
DataContext = theTable;

Visual Basic .NET

' Set the Data Context
DataContext = theTable

Dopo aver ottenuto i dati e fornito alla finestra, è possibile eseguire il data binding nel codice XAML. Il binding in ComboBox indica semplicemente all'associazione di ottenere i dati dal DataContext dell'elemento padre( in questo caso, scorre l'albero dei controlli fino a quando non trova un Oggetto DataContext nella finestra).

    <ComboBox Name="theCombo" 
              IsSynchronizedWithCurrentItem="True"                                 ItemsSource="{Binding}" 
              ... />

L'attributo IsSynchronizedWithCurrentItem è importante in quanto, quando la selezione viene modificata, ciò che modifica l'elemento corrente per quanto riguarda la finestra. Ciò indica al motore WPF che questo oggetto verrà usato per modificare l'elemento corrente. Senza questo attributo, l'elemento corrente in DataContext non verrà modificato e pertanto le caselle di testo presuppongono che sia ancora presente nel primo elemento dell'elenco.

Per visualizzare i nomi dei dipendenti nella casella combinata, vengono creati binding in ItemsTemplate per visualizzare firstname e LastName da DataTable.

    <DataTemplate x:Key="EmployeeListTemplate">
      <StackPanel Orientation="Horizontal">
      <TextBlock Text="{Binding Path=FirstName}" />
      <TextBlock Text=" " />
      <TextBlock Text="{Binding Path=LastName}" />
      </StackPanel>
    </DataTemplate>

Aggiungere quindi caselle di testo per contenere il nome, il titolo e la data di assunzione.

      <TextBlock Canvas.Top="5">First Name:</TextBlock>
      <TextBox Canvas.Top="5" Text="{Binding Path=FirstName}" />
      <TextBlock Canvas.Top="25">Last Name:</TextBlock>
      <TextBox Canvas.Top="25" Text="{Binding Path=LastName}" />
      <TextBlock Canvas.Top="45">Title:</TextBlock>
      <TextBox Canvas.Top="45" Text="{Binding Path=Title}" />
      <TextBlock Canvas.Top="65">Hire Date:</TextBlock>
      <TextBox Canvas.Top="65" Text="{Binding Path=HireDate}" />

Poiché vogliamo anche la foto, dobbiamo aggiungere un'immagine al codice XAML.

      <Image Name="theImage" Canvas.Top="5" Canvas.Left="5" Width="75"/>

L'unico problema con l'immagine è che non supporta il binding automatico dei dati di foto all'immagine. Per facilitare questa operazione, è possibile gestire l'evento SelectionChanged di ComboBox per compilare l'immagine.

    <ComboBox Name="theCombo" 
              IsSynchronizedWithCurrentItem="True"                   
              Width="200" 
              ItemsSource="{Binding}" 
              ItemTemplate="{StaticResource EmployeeListTemplate}"
              SelectionChanged="theCombo_OnSelectionChanged" />

Nel codice è necessario caricare l'immagine da DataTable e creare un oggetto BitmapImage per compilare il tag Image . Si noti che non si tratta di una bitmap di GDI+ (System.Drawing), ma di un nuovo oggetto Bitmap in WPF.

C#

// Handler to show the image
void theCombo_OnSelectionChanged(object sender, RoutedEventArgs e)
{
  ShowPhoto();
}

// Shows the Photo for the currently selected item
void ShowPhoto()
{
  object selected = theCombo.SelectedItem;
  DataRow row = ((DataRowView)selected).Row;
  
  // Get the raw bytes of the image
  byte[] photoSource = (byte[])row["Photo"];

  // Create the bitmap object
  // NOTE: This is *not* a GDI+ Bitmap object
  BitmapImage bitmap = new BitmapImage();
  MemoryStream strm = new MemoryStream();

  // Well-known work-around to make Northwind images work
  int offset = 78;
  strm.Write(photoSource, offset, photoSource.Length - offset);

  // Read the image into the bitmap object
  bitmap.BeginInit();
  bitmap.StreamSource = strm;
  bitmap.EndInit();

  // Set the Image with the Bitmap
  theImage.Source = bitmap;
  
}

Visual Basic .NET

' Handler to show the image
Sub theCombo_OnSelectionChanged(ByVal sender As Object, ByVal e As RoutedEventArgs)

  ShowPhoto();

End Sub

// Shows the Photo for the currently selected item
Sub ShowPhoto()

  Dim selected As Object =  theCombo.SelectedItem 
  Dim row As DataRow = (CType(selected, DataRowView)).Row 
 
  ' Get the raw bytes of the image
  Dim photoSource() As Byte = CType(row("Photo"), Byte())
 
  ' Create the bitmap object
  ' NOTE: This is *not* a GDI+ Bitmap object
  Dim bitmap As BitmapImage =  New BitmapImage() 
  Dim strm As MemoryStream =  New MemoryStream() 
 
  ' Well-known work-around to make Northwind images work
  Dim offset As Integer =  78 
  strm.Write(photoSource, offset, photoSource.Length - offset)
 
  ' Read the image into the bitmap object
  bitmap.BeginInit()
  bitmap.StreamSource = strm
  bitmap.EndInit()
 
  ' Set the Image with the Bitmap
  theImage.Source = bitmap
 
End Sub

L'oggetto SelectedItem viene afferrato da ComboBox e convertito in un oggetto DataRow in modo che sia possibile ottenere i dati. Si afferra quindi la matrice di byte dalla colonna Photo . Questa è la foto archiviata nel database Northwind. È possibile usare un flusso in memoria per trasmettere i byte di foto in un oggetto BitmapImage . L'unica modifica è una soluzione comunemente usata per ignorare i primi 78 byte dell'intestazione dell'immagine di Northwind, perché non viene più usata. Dopo aver letto il flusso nella bitmap, è possibile assegnarlo all'oggetto Image come origine.

Si vuole assicurarsi che il data binding sia bidirezionale, quindi si creerà un pulsante che mostri le informazioni correnti, in modo che sappiamo che si trova in DataRow.

C#

void SaveButton_OnClick(object sender, RoutedEventArgs e)
{
  object selected = theCombo.SelectedItem;
  DataRow row = ((DataRowView)selected).Row;

  MessageBox.Show(string.Format("{0} {1} {2} - {3:d}", 
    row["Title"], row["FirstName"], row["LastName"],  row["HireDate"]));
}

Visual Basic .NET

Sub SaveButton_OnClick(ByVal sender As Object, ByVal e As RoutedEventArgs)

  Dim selected As Object =  theCombo.SelectedItem 
  Dim row As DataRow = (CType(selected, DataRowView)).Row 
 
  MessageBox.Show(String.Format("{0} {1} {2} - {3:d}", _
    row("Title"), row("FirstName"), row("LastName"),  row("HireDate")))

End Sub

L'intero file XAML finisce per cercare come indicato di seguito.

<Window x:Class="ExampleCS.EmployeeBrowser"
    xmlns="https://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="https://schemas.microsoft.com/winfx/2006/xaml"
    Title="Employee Browser"
    Loaded="OnLoaded" 
    Width="300"
    Height="170" 
    WindowStartupLocation="CenterScreen"
    >
  <Window.Resources>
    <DataTemplate x:Key="EmployeeListTemplate">
      <StackPanel Orientation="Horizontal">
        <TextBlock Text="{Binding Path=FirstName}" />
        <TextBlock Text=" " />
        <TextBlock Text="{Binding Path=LastName}" />
      </StackPanel>
    </DataTemplate>
  </Window.Resources>
  <Window.Background>
    <LinearGradientBrush StartPoint="0,0" EndPoint="1,1">
      <LinearGradientBrush.GradientStops>
        <GradientStop Color="DarkGray" Offset="0" />
        <GradientStop Color="White" Offset=".75" />
        <GradientStop Color="DarkGray" Offset="1" />
      </LinearGradientBrush.GradientStops>
    </LinearGradientBrush>
  </Window.Background>
  <StackPanel Name="theStackPanel" 
              VerticalAlignment="Top">
    <ComboBox Name="theCombo" 
              IsSynchronizedWithCurrentItem="True" 
              Width="200" 
              ItemsSource="{Binding}" 
              ItemTemplate="{StaticResource EmployeeListTemplate}"
              SelectionChanged="theCombo_OnSelectionChanged" />
    <Canvas>
      <Canvas.Resources>
        <Style TargetType="{x:Type TextBox}">
          <Setter Property="Canvas.Left" Value="160" />
          <Setter Property="Padding" Value="0" />
          <Setter Property="Height" Value="18" />
          <Setter Property="Width" Value="120" />
        </Style>
        <Style TargetType="{x:Type TextBlock}">
          <Setter Property="Canvas.Left" Value="90" />
          <Setter Property="Padding" Value="0" />
          <Setter Property="Height" Value="18" />
          <Setter Property="FontWeight" Value="Bold" />
        </Style>
      </Canvas.Resources>
      <Image Name="theImage" Canvas.Top="5" Canvas.Left="5" Width="75"/>
      <TextBlock Canvas.Top="5">First Name:</TextBlock>
      <TextBox Canvas.Top="5" Text="{Binding Path=FirstName}" />
      <TextBlock Canvas.Top="25">Last Name:</TextBlock>
      <TextBox Canvas.Top="25" Text="{Binding Path=LastName}" />
      <TextBlock Canvas.Top="45">Title:</TextBlock>
      <TextBox Canvas.Top="45" Text="{Binding Path=Title}" />
      <TextBlock Canvas.Top="65">Hire Date:</TextBlock>
      <TextBox Canvas.Top="65" Text="{Binding Path=HireDate}" />
      <Button Canvas.Top="85" Canvas.Left="90" Width="190" 
              Name="SaveButton" Click="SaveButton_OnClick">Save</Button>
    </Canvas>
  </StackPanel>
</Window>

Ora, quando si esegue il browser, si ottiene un'interfaccia simile a quella illustrata nella figura 1.

Aa480226.wpfdabndpt201(en-us,MSDN.10).gif

Figura 1. Visualizzatore dipendenti

Questo semplice esempio è piuttosto semplice, ma cosa accade se si usano tabelle DataTable correlate all'interno di un dataset? Vediamo se è altrettanto facile.

Estendere il Visualizzatore dipendenti per includere gli ordini per i quali i dipendenti sono il venditore. A tale scopo, sarà necessario ottenere informazioni sull'ordine. È possibile eseguire questa operazione con una nuova query ogni volta che si cambiano utenti, ma si caricherà i dati in un set di dati insieme a Employee e si userà DataRelation per correlare le due informazioni.

C#

DataSet theSet = new DataSet();

string connString = ConfigurationManager.ConnectionStrings["Northwind"].ConnectionString;
string employeeQuery = @"
  SELECT EmployeeID, FirstName, LastName, Title, HireDate, Photo 
  FROM Employees
";
string orderQuery = @"
  SELECT o.OrderID, EmployeeID, CompanyName, OrderDate, SUM((UnitPrice * Quantity)* (1-Discount)) as OrderTotal
  FROM Orders o
  JOIN [Order Details] od on o.OrderID = od.OrderID
   JOIN Customers c on c.CustomerID = o.CustomerID
  GROUP BY o.OrderID, o.EmployeeID, o.OrderDate, CompanyName";

// Fill the Set with the data
using (SqlConnection conn = new SqlConnection(connString))
{
  SqlDataAdapter da = new SqlDataAdapter(employeeQuery, conn);
  da.Fill(theSet, "Employees");
  da.SelectCommand.CommandText = orderQuery;
  da.Fill(theSet, "Orders");
}

// Create the relationship
DataTable empTable = theSet.Tables["Employees"];
DataTable ordTable = theSet.Tables["Orders"];
theSet.Relations.Add("Emp2Ord", 
                     empTable.Columns["EmployeeID"], 
                     ordTable.Columns["EmployeeID"], 
                     false);

// Set the Context of the Window to be the 
// DataTable we've created
DataContext = empTable;

Visual Basic .NET

Dim theSet As DataSet =  New DataSet() 
 
Dim connString As String = _
    ConfigurationManager.ConnectionStrings("Northwind").ConnectionString 
String employeeQuery = _
  "SELECT EmployeeID, FirstName, LastName, Title, HireDate, Photo " + _
  "  FROM Employees"

String orderQuery = _
  "SELECT o.OrderID, EmployeeID, CompanyName, OrderDate, " + _
  "       SUM((UnitPrice * Quantity)* (1-Discount)) as OrderTotal " + 
  "FROM Orders o " +
  "JOIN (Order Details) od on o.OrderID = od.OrderID " +
  "JOIN Customers c on c.CustomerID = o.CustomerID " +
  "GROUP BY o.OrderID, o.EmployeeID, o.OrderDate, CompanyName"
 
' Fill the Set with the data
Using conn as New SqlConnection(connString)

  Dim da As SqlDataAdapter =  New SqlDataAdapter(employeeQuery,conn) 
  da.Fill(theSet, "Employees")
  da.SelectCommand.CommandText = orderQuery
  da.Fill(theSet, "Orders")

End Using
 
' Create the relationship
Dim empTable As DataTable =  theSet.Tables("Employees") 
Dim ordTable As DataTable =  theSet.Tables("Orders") 
theSet.Relations.Add("Emp2Ord", 
                     empTable.Columns("EmployeeID"), 
                     ordTable.Columns("EmployeeID"), 
                     False)

' Set the Context of the Window to be the 
' DataTable we've created
DataContext = empTable

Questo codice creerà un set di dati con due tabelle: Employees e Orders. Queste tabelle sono correlate da EmployeeID tramite una relazione denominata Emp2Ord. È comunque possibile eseguire il binding a Employee DataTable in modo che il data binding originale in XAML funzioni correttamente. Analogamente Windows Forms o ASP.NET data binding, è possibile eseguire il binding al nome di Relation, per consentire l'associazione a un set di record correlati.

        <ListBox Name="OrderList" Width="280" Height="200"
               ItemsSource="{Binding Emp2Ord}" 
               ItemTemplate="{StaticResource OrderListTemplate}" />

Questa casella di riepilogo usa ancora lo stesso DataContext del resto del Browser dipendente; specifica semplicemente l'associazione tramite la relazione. Dopo aver associato la casella di riepilogo alla relazione, è possibile eseguire il binding ai singoli campi dell'elemento ItemTemplate, esattamente come è stato fatto nella casella combinata dipendente.

    <DataTemplate x:Key="OrderListTemplate">
      <StackPanel Orientation="Horizontal">
        <TextBlock VerticalAlignment="Top" Width="100" 
                   Text="{Binding Path=CompanyName}" />
        <StackPanel>
          <TextBlock Text="{Binding Path=OrderID}" />
          <TextBlock Text="{Binding Path=OrderDate}" />
          <TextBlock Text="{Binding Path=OrderTotal}" />
        </StackPanel>
      </StackPanel>
    </DataTemplate>

Con questo data binding aggiuntivo, viene ora visualizzata una casella di riepilogo delle informazioni sull'ordine correlate solo all'utente selezionato, come illustrato nella figura 2.

Aa480226.wpfdabndpt202(en-us,MSDN.10).gif

Figura 2. Browser dei dipendenti migliorato

In questo modo è possibile eseguire il binding a dati più complessi rispetto a semplici parti rettangolari di dati. In molte organizzazioni usano tipi .NET personalizzati (o oggetti business) per contenere i dati e la logica di business. WPF può essere associato a questi oggetti con la facilità possibile con i set di dati?

Associazione a oggetti business

Nell'incarnazione originale di .NET, tra cui Windows Forms e ASP.NET, dataset e i relativi oggetti erano cittadini di prima classe. Limitano i dati e funzionano bene. Tuttavia, se si è scelto di compilare modelli a oggetti o oggetti business per contenere i dati, è stato lasciato associare manualmente i dati dagli oggetti ai controlli. In .NET 2.0 gli oggetti sono stati generati ai cittadini di prima classe, consentendo l'associazione semplificata agli oggetti. In WPF questa situazione continua a essere vera. In WPF è semplice eseguire il binding a oggetti come DataSet.

Per creare il browser dipendente preferito con gli oggetti business, creare prima una classe per contenere il dipendente.

C#

public class Employee
{
  // Fields
  int _employeeID;
  string _firstName;
  string _lastName;
  string _title;
  DateTime _hireDate;
  BitmapImage _photo;

  // Constructor
  public Employee(IDataRecord record)
  {
    _employeeID = (int) record["EmployeeID"];
    _firstName = (string) record["FirstName"];
    _lastName = (string)record["LastName"];
    _title = (string)record["Title"];
    _hireDate = (DateTime)record["HireDate"];
    CreatePhoto((byte[])record["Photo"]);
  }

  // BitmapImage creation
  void CreatePhoto(byte[] photoSource)
  {
    // Create the bitmap object
    // NOTE: This is *not* a GDI+ Bitmap object
    _photo = new BitmapImage();
    MemoryStream strm = new MemoryStream();

    // Well-known hack to make Northwind images work
    int offset = 78;
    strm.Write(photoSource, offset, photoSource.Length - offset);

    // Read the image into the bitmap object
    _photo.BeginInit();
    _photo.StreamSource = strm;
    _photo.EndInit();

  }
}

Visual Basic .NET

Public Class Employee

  ' Fields
  Dim _employeeID As Integer
  Dim _firstName As String
  Dim _lastName As String
  Dim _title As String
  Dim _hireDate As DateTime
  Dim _photo As BitmapImage
 
  ' Constructor
  Public  Sub New(ByVal record As IDataRecord)

    _employeeID = CType(record("EmployeeID"), Integer)
    _firstName = CType(record("FirstName"), String)
    _lastName = CType(record("LastName"), String)
    _title = CType(record("Title"), String)
    _hireDate = CType(record("HireDate"), DateTime)
    CreatePhoto(CType(record("Photo"), Byte()))

  End Sub
 
  ' BitmapImage creation
  Private  Sub CreatePhoto(ByVal photoSource() As Byte)

    ' Create the bitmap object
    ' NOTE: This is *not* a GDI+ Bitmap object
    _photo = New BitmapImage()
    Dim strm As MemoryStream =  New MemoryStream() 
 
    ' Well-known hack to make Northwind images work
    Dim offset As Integer =  78 
    strm.Write(photoSource, offset, photoSource.Length - offset)
 
    ' Read the image into the bitmap object
    _photo.BeginInit()
    _photo.StreamSource = strm
    _photo.EndInit()
 
  End Sub
End Class

Questa classe accetta una classe IDataRecord (un singolo risultato da un Oggetto DataReader, ma ci si otterrà in un minuto) e compila gli stessi campi usati con l'esempio DataTable riportato in precedenza in questo articolo. Si noti che la creazione di BitmapImage è stata spostata qui nell'oggetto business per semplificare l'uso del dipendente nella classe dell'interfaccia utente.

Successivamente, verranno richieste le funzioni di accesso alle proprietà per i campi.

C#

// Read-Only
public int EmployeeID
{
  get { return _employeeID; }
}

public string FirstName
{
  get { return _firstName; }
  set { _firstName = value; }
}

public string LastName
{
  get { return _lastName; }
  set { _lastName = value; }
}

public string Title
{
  get { return _title; }
  set { _title = value; }
}

public DateTime HireDate
{
  get { return _hireDate; }
  set { _hireDate = value; }
}

// Read-Only
public BitmapImage Photo
{
  get { return _photo; }
}

Visual Basic .NET

' Read-Only
Public ReadOnly Property EmployeeID() As Integer
  Get 
     Return _employeeID
  End Get
End Property
 
Public Property FirstName() As String
  Get 
     Return _firstName
  End Get
  Set (ByVal Value As String) 
     _firstName = value
  End Set
End Property
 
Public Property LastName() As String
  Get 
     Return _lastName
  End Get
  Set (ByVal Value As String) 
     _lastName = value
  End Set
End Property
 
Public Property Title() As String
  Get 
     Return _title
  End Get
  Set (ByVal Value As String) 
     _title = value
  End Set
End Property
 
Public Property HireDate() As DateTime
  Get 
     Return _hireDate
  End Get
  Set (ByVal Value As DateTime) 
     _hireDate = value
  End Set
End Property
 
' Read-Only
Public ReadOnly Property Photo() As BitmapImage
  Get 
     Return _photo
  End Get
End Property

In questi casi è sufficiente consentire l'accesso in lettura/scrittura (o di sola lettura) ai campi della classe .

È ora possibile scrivere una raccolta per contenere i dipendenti.

C#

public class EmployeeList : ObservableCollection<Employee>
{
  public EmployeeList()
  {
    string connString =
           ConfigurationManager.ConnectionStrings["Northwind"].ConnectionString;
    string query = @"
      SELECT EmployeeID, FirstName, LastName, Title, HireDate, Photo 
      FROM Employees
    ";

    // Fill the Set with the data
    using (SqlConnection conn = new SqlConnection(connString))
    {
      try
      {
        SqlCommand cmd = conn.CreateCommand();
        cmd.CommandText = query;

        conn.Open();
        SqlDataReader rdr = cmd.ExecuteReader();
        while (rdr.Read())
        {
          Add(new Employee(rdr));
        }
      }
      finally
      {
        if (conn.State != ConnectionState.Closed) conn.Close();
      }
    }
  }
}

Visual Basic .NET

Public Class EmployeeList
   Inherits ObservableCollection<Employee>
  Public  Sub New()
    String connString =
           ConfigurationManager.ConnectionStrings("Northwind").ConnectionString

    String query = _
      "SELECT EmployeeID, FirstName, LastName, Title, HireDate, Photo " + _
      "  FROM Employees"

    ' Fill the Set with the data
    Using conn as New SqlConnection(connString)
    
      Try
        Dim cmd As SqlCommand =  conn.CreateCommand() 
        cmd.CommandText = query
 
        conn.Open()
        Dim rdr As SqlDataReader =  cmd.ExecuteReader() 
        While rdr.Read()
          Add(New Employee(rdr))
        End While
      Finally
        If conn.State <> ConnectionState.Closed Then
            conn.Close()
        End If
      End Try
    
    End Using

  End Sub
End Class

La classe base della raccolta è la classe ObservableCollection , che fornisce un meccanismo per consentire all'interfaccia utente di sapere se vengono aggiunti nuovi membri alla raccolta. È stato spostato l'accesso ai dati dalla pagina dell'interfaccia utente alla classe di raccolta. Quando questa classe viene creata, viene eseguita una query sul database e si aggiungono nuovi dipendenti alla raccolta da DataReader.

Dopo aver creato la raccolta e i singoli oggetti, è possibile importare le classi in XAML con un mapping (illustrato in dettaglio nella prima parte di questa serie di articoli).

<Window
    ...
    xmlns:e="Example"    DataContext="{StaticResource EmployeeList}"
    >
  <Window.Resources>
    <e:EmployeeList x:Key="EmployeeList" />
    ...
  </Window.Resources>
  ...
</Window>

Importare la classe nel documento XAML usando ? Dichiarazione di mapping . Inoltre, specificare EmployeeList nelle risorse, in modo che sia possibile usarla come DataContext della finestra. In questo modo, il resto del file XAML è identico al browser dipendente originale, perché si sta ancora legando agli stessi nomi di campo presenti nell'esempio di DataSet . L'unica modifica che è possibile apportare consiste nell'associare bitmapImage nel documento XAML, anziché eseguirla nel code-behind.

...
      <Image Name="theImage" Canvas.Top="5" Canvas.Left="5" Width="75" 
             Source="{Binding Path=Photo}"/>
...

A questo momento è disponibile un browser dipendente con comportamento identico (vedere la figura 3).

Aa480226.wpfdabndpt203(en-us,MSDN.10).gifAa480226.wpfdabndpt203(en-us,MSDN.10

Figura 3. Browser dipendente basato su oggetti business

Oltre a usare il mapping dei tipi, è anche possibile usare ObjectDataProvider per inserire gli oggetti in XAML. Come illustrato nella prima parte di questa serie di articoli, è necessario specificare una chiave e il nome del tipo.

    <ObjectDataProvider x:Key="EmployeeList" 
                        TypeName="Example.Data.EmployeeList, ExampleCS"/>

X :Key è solo un moniker da usare nell'associazione e typename è il nome della classe e l'assembly (in questo caso lo stesso assembly in cui risiede l'interfaccia utente). Il resto del codice XAML rimane invariato, perché vengono caricati negli stessi dati.

Dove siamo?

È ora possibile caricare i dati da un database usando DataSet o oggetti personalizzati e associare i dati direttamente agli oggetti WPF. È ora necessario essere pronti per approfondire il primo progetto di database WPF.

Riferimenti