Alessandro Del Sole's Blog

{ A programming space about Microsoft® .NET® }
posts - 1909, comments - 2047, trackbacks - 352

My Links

News

Your host

This is me! Questo spazio è dedicato a Microsoft® .NET®, di cui sono molto appassionato :-)

Cookie e Privacy

Microsoft MVP

My MVP Profile

Microsoft Certified Professional

Microsoft Specialist

Xamarin Certified Mobile Developer

Il mio libro su VB 2015!

Pre-ordina il mio libro su VB 2015 Pre-ordina il mio libro "Visual Basic 2015 Unleashed". Clicca sulla copertina per informazioni!

Il mio libro su WPF 4.5.1!

Clicca sulla copertina per informazioni! E' uscito il mio libro "Programmare con WPF 4.5.1". Clicca sulla copertina per informazioni!

These postings are provided 'AS IS' for entertainment purposes only with absolutely no warranty expressed or implied and confer no rights.
If you're not an Italian user, please visit my English blog

Le vostre visite

I'm a VB!

Guarda la mia intervista a Seattle

Follow me on Twitter!

Altri spazi

GitHub
I miei progetti open-source su GitHub

Article Categories

Archives

Post Categories

Image Galleries

Privacy Policy

WPF: Introduzione al pattern Model-View-ViewModel per sviluppatori Visual Basic 2010 - parte 10

Finalmente con questo post concludiamo la serie di articoli introduttivi al pattern Model-View-ViewModel in WPF con Visual Basic 2010, in particolare nei confronti di ADO.NET Entity Framework. Vedremo quindi come costruire l'interfaccia grafica e come collegarla, in data-binding, ai ViewModel. Anche se concludiamo questa serie introduttiva, non vuol dire che non tornerò sull'argomento. Anzi, MVVM sarà sicuramente un argomento centrale per questo blog ma per tenere ben distinti gli argomenti, concludiamo qui l'applicazione dimostrativa. Al termine del post trovate il link per scaricare il progetto sorgente completo, dall'area Download di VB T&T.

Andrò abbastanza spedito in questo post, perché il codice è tanto e i concetti sono stati illustrati nel corso della serie. Niente di trascendentale, cose che già abbiamo imparato.

L'ErrorTemplate

Come ricorderete, nel primo post di questa parte dedicata a MVVM con Entity Framework abbiamo implementato regole di validazione dei dati nella classe Order, provvista inoltre dell'interfaccia IDataErrorInfo. Affinché l'interfaccia grafica sia in grado di riflettere gli errori di validazione, è necessario definire un ErrorTemplate per i controlli di tipo TextBox (che utilizzeremo nella View). Il codice di cui faccio utilizzo è stato già illustrato nella parte 4 della serie, ad ogni buon conto lo riporto di seguito e va inserito all'interno del file Application.xaml:

 

<Application x:Class="Application"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    StartupUri="Views\MainWindow.xaml">
    <Application.Resources>
        <Style x:Key="ButtonStyle" TargetType="Button">
            <Setter Property="Width" Value="80"/>
            <Setter Property="Height" Value="40"/>
            <Setter Property="Margin" Value="5"/>
        </Style>
 
        <!--Template per validazione controlli
            Mostrerà un bordo rosso e un messaggio
            quando la validazione dell'ordine fallisce
        -->
        <Style TargetType="Control" x:Key="myErrorTemplate">
            <Setter Property="Validation.ErrorTemplate">
                <Setter.Value>
                    <ControlTemplate>
                        <DockPanel LastChildFill="True">
                            <TextBlock DockPanel.Dock="Right" 
                       
 Foreground="Red"
                        FontSize="14pt" 
                       
 FontWeight="ExtraBold">*
                            </TextBlock>
                            <Border BorderBrush="Red" BorderThickness="4">
                                <AdornedElementPlaceholder Name="myControl"/>
                            </Border>
                        </DockPanel>
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
            <Style.Triggers>
                <Trigger Property="Validation.HasError" Value="True">
                    <Setter Property="ToolTip"
                Value="{Binding RelativeSource={x:Static RelativeSource.Self},
                Path=(Validation.Errors)[0].ErrorContent}"/>
                </Trigger>
            </Style.Triggers>
        </Style>
        <Style TargetType="TextBox" BasedOn="{StaticResource myErrorTemplate}"/>
 
        <Style TargetType="Label">
            <Setter Property="Foreground" Value="White"/>
        </Style>
    </Application.Resources>
</Application>

Il template così definito mostrerà un bordo rosso con asterisco intorno alle TextBox che conterranno dati non validi, finché i dati stessi non saranno corretti.

La View principale

Per come abbiamo visto la figura nel primo post per MVVM/Entity Framework, l'interfaccia dovrà essere così composta:

  • una Grid principale che contiene:
    • una ListBox per l'elenco clienti
    • una Grid che conterrà i controlli per visualizzare le informazioni sul singolo ordine
    • uno StackPanel che conterrà i pulsanti

Quindi, per prima cosa definiamo la Window come segue, specificando i riferimenti all'assembly dell'applicazione e aggiungendo anche un background a gradiente un po' carino:

<Window x:Class="MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="Northwind Traders Orders Management" Height="508" Width="876" mc:Ignorable="d" 
       
 xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
       
 xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
   
 xmlns:my="clr-namespace:adsMVVM_EntityFramework_Complete"
    WindowStartupLocation="CenterScreen">
    <Window.Background>
        <LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0">
            <GradientStop Color="Black" Offset="0" />
            <GradientStop Color="White" Offset="1" />
        </LinearGradientBrush>
    </Window.Background>

Chiaramente il nome dell'assembly varierà sulla vostra macchina, a seconda di come avete chiamato il progetto. Suddividiamo ora in tre parti la griglia principale, assegnando un nome che ci sarà utile nel file di code-behind:

    <Grid Name="MainGrid" ShowGridLines="False" >
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="200" />
            <ColumnDefinition />
            <ColumnDefinition/>
        </Grid.ColumnDefinitions>

Ora dichiariamo la ListBox per l'elenco clienti. Fate attenzione al binding:

        <ListBox Name="ListBox1" Grid.Column="0"  ItemsSource="{Binding Path=CustomerViewSource.View}" DisplayMemberPath="CompanyName"
                 SelectedItem="{Binding Path=Selection, Mode=TwoWay}" />

Avviene questo: il controllo è in binding (ItemsSource) con la proprietà CustomersViewSource.View dell'oggetto che verrà assegnato al DataContext della Grid principale (ossia il ViewModel). Quindi questa volta i controlli sono in binding con le view delle CollectionViewSource/ListCollectionView invece che con un ObservableCollection. DisplayMemberPath consente di specificare l'elemento della collezione che deve essere mostrato. Notate anche come l'elemento selezionato della ListBox sia in binding con la proprietà Selection del ViewModel, in lettura/scrittura. Ora è la volta della Grid centrale che contiene TextBox e DatePicker (questi per la visualizzazione di date) per rappresentare un singolo ordine. Notate come ogni controllo di questo tipo sia in binding con la corrispondente proprietà proveniente dalla sorgente associata (ossia la classe Order), ma soprattutto notate come la Grid si alimenti dall'oggetto CustomerOrdersView che viene esposto dal ViewModel che verrà associato tra poco alla View:

        <Grid Grid.Column="1" Name="Grid1" DataContext="{Binding Path=CustomerOrdersView}" >
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="Auto" />
                <ColumnDefinition Width="Auto" />
            </Grid.ColumnDefinitions>
            <Grid.RowDefinitions>
                <RowDefinition Height="Auto" />
                <RowDefinition Height="Auto" />
                <RowDefinition Height="Auto" />
                <RowDefinition Height="Auto" />
                <RowDefinition Height="Auto" />
                <RowDefinition Height="Auto" />
                <RowDefinition Height="Auto" />
                <RowDefinition Height="Auto" />
                <RowDefinition Height="Auto" />
                <RowDefinition Height="Auto" />
                <RowDefinition Height="Auto" />
                <RowDefinition Height="Auto" />
                <RowDefinition Height="Auto" />
                <RowDefinition Height="Auto" />
            </Grid.RowDefinitions>
            <Label Content="Order ID:" Grid.Column="0" Grid.Row="0" HorizontalAlignment="Left" Margin="3" VerticalAlignment="Center" />
            <TextBox Grid.Column="1" Grid.Row="0" Height="23" HorizontalAlignment="Left" Margin="3" Name="OrderIDTextBox" 
                     Text="{Binding Path=OrderID, Mode=TwoWay, ValidatesOnExceptions=true, NotifyOnValidationError=true}" VerticalAlignment="Center" Width="120" />
            <Label Content="Customer ID:" Grid.Column="0" Grid.Row="1" HorizontalAlignment="Left" Margin="3" VerticalAlignment="Center" />
            <TextBox Grid.Column="1" Grid.Row="1" Height="23" HorizontalAlignment="Left" Margin="3" Name="CustomerIDTextBox" 
                     Text="{Binding Path=CustomerID, Mode=TwoWay, ValidatesOnExceptions=true, NotifyOnValidationError=true}" VerticalAlignment="Center" Width="120" />
            <Label Content="Employee ID:" Grid.Column="0" Grid.Row="2" HorizontalAlignment="Left" Margin="3" VerticalAlignment="Center" />
            <TextBox Grid.Column="1" Grid.Row="2" Height="23" HorizontalAlignment="Left" Margin="3" Name="EmployeeIDTextBox" 
                     Text="{Binding Path=EmployeeID, Mode=TwoWay, ValidatesOnExceptions=true, NotifyOnValidationError=true}" VerticalAlignment="Center" Width="120" />
            <Label Content="Order Date:" Grid.Column="0" Grid.Row="3" HorizontalAlignment="Left" Margin="3" VerticalAlignment="Center" />
            <DatePicker Grid.Column="1" Grid.Row="3" Height="25" HorizontalAlignment="Left" Margin="3" Name="OrderDateDatePicker" 
                        SelectedDate="{Binding Path=OrderDate, Mode=TwoWay, ValidatesOnExceptions=true, NotifyOnValidationError=true}" VerticalAlignment="Center" Width="115" />
            <Label Content="Required Date:" Grid.Column="0" Grid.Row="4" HorizontalAlignment="Left" Margin="3" VerticalAlignment="Center" />
            <DatePicker Grid.Column="1" Grid.Row="4" Height="25" HorizontalAlignment="Left" Margin="3" Name="RequiredDateDatePicker" 
                        SelectedDate="{Binding Path=RequiredDate, Mode=TwoWay, ValidatesOnExceptions=true, NotifyOnValidationError=true}" VerticalAlignment="Center" Width="115" />
            <Label Content="Shipped Date:" Grid.Column="0" Grid.Row="5" HorizontalAlignment="Left" Margin="3" VerticalAlignment="Center" />
            <DatePicker Grid.Column="1" Grid.Row="5" Height="25" HorizontalAlignment="Left" Margin="3" Name="ShippedDateDatePicker" 
                        SelectedDate="{Binding Path=ShippedDate, Mode=TwoWay, ValidatesOnExceptions=true, NotifyOnValidationError=true}" VerticalAlignment="Center" Width="115" />
            <Label Content="Ship Via:" Grid.Column="0" Grid.Row="6" HorizontalAlignment="Left" Margin="3" VerticalAlignment="Center" />
            <TextBox Grid.Column="1" Grid.Row="6" Height="23" HorizontalAlignment="Left" Margin="3" Name="ShipViaTextBox" 
                     Text="{Binding Path=ShipVia, Mode=TwoWay, ValidatesOnExceptions=true, NotifyOnValidationError=true}" VerticalAlignment="Center" Width="120" />
            <Label Content="Freight:" Grid.Column="0" Grid.Row="7" HorizontalAlignment="Left" Margin="3" VerticalAlignment="Center" />
            <TextBox Grid.Column="1" Grid.Row="7" Height="23" HorizontalAlignment="Left" Margin="3" Name="FreightTextBox" 
                     Text="{Binding Path=Freight, Mode=TwoWay, ValidatesOnExceptions=true, NotifyOnValidationError=true}" VerticalAlignment="Center" Width="120" />
            <Label Content="Ship Name:" Grid.Column="0" Grid.Row="8" HorizontalAlignment="Left" Margin="3" VerticalAlignment="Center" />
            <TextBox Grid.Column="1" Grid.Row="8" Height="23" HorizontalAlignment="Left" Margin="3" Name="ShipNameTextBox" 
                     Text="{Binding Path=ShipName, Mode=TwoWay, ValidatesOnExceptions=true, NotifyOnValidationError=true}" VerticalAlignment="Center" Width="120" />
            <Label Content="Ship Address:" Grid.Column="0" Grid.Row="9" HorizontalAlignment="Left" Margin="3" VerticalAlignment="Center" />
            <TextBox Grid.Column="1" Grid.Row="9" Height="23" HorizontalAlignment="Left" Margin="3" Name="ShipAddressTextBox" 
                     Text="{Binding Path=ShipAddress, Mode=TwoWay, ValidatesOnExceptions=true, NotifyOnValidationError=true}" VerticalAlignment="Center" Width="120" />
            <Label Content="Ship City:" Grid.Column="0" Grid.Row="10" HorizontalAlignment="Left" Margin="3" VerticalAlignment="Center" />
            <TextBox Grid.Column="1" Grid.Row="10" Height="23" HorizontalAlignment="Left" Margin="3" Name="ShipCityTextBox" 
                     Text="{Binding Path=ShipCity, Mode=TwoWay, ValidatesOnExceptions=true, NotifyOnValidationError=true}" VerticalAlignment="Center" Width="120" />
            <Label Content="Ship Region:" Grid.Column="0" Grid.Row="11" HorizontalAlignment="Left" Margin="3" VerticalAlignment="Center" />
            <TextBox Grid.Column="1" Grid.Row="11" Height="23" HorizontalAlignment="Left" Margin="3" Name="ShipRegionTextBox" 
                     Text="{Binding Path=ShipRegion, Mode=TwoWay, ValidatesOnExceptions=true, NotifyOnValidationError=true}" VerticalAlignment="Center" Width="120" />
            <Label Content="Ship Postal Code:" Grid.Column="0" Grid.Row="12" HorizontalAlignment="Left" Margin="3" VerticalAlignment="Center" />
            <TextBox Grid.Column="1" Grid.Row="12" Height="23" HorizontalAlignment="Left" Margin="3" Name="ShipPostalCodeTextBox" 
                     Text="{Binding Path=ShipPostalCode, Mode=TwoWay, ValidatesOnExceptions=true, NotifyOnValidationError=true}" VerticalAlignment="Center" Width="120" />
            <Label Content="Ship Country:" Grid.Column="0" Grid.Row="13" HorizontalAlignment="Left" Margin="3" VerticalAlignment="Center" />
            <TextBox Grid.Column="1" Grid.Row="13" Height="23" HorizontalAlignment="Left" Margin="3" Name="ShipCountryTextBox" 
                     Text="{Binding Path=ShipCountry, Mode=TwoWay, ValidatesOnExceptions=true, NotifyOnValidationError=true, ValidatesOnDataErrors=True}" VerticalAlignment="Center" Width="120" />
        </Grid>

Fate attenzione al fatto che è importante impostare il markup ValidatesOnDataErrors = True nel binding della TextBox relativa alla proprietà ShipCountry, che è quella per la quale abbiamo previsto regole di validazione lato Model. Per completare l'interfaccia della finestra principale ci manca solo la dichiarazione dei pulsanti e il relativo binding dei comandi. Ecco il codice:

        <StackPanel Name="Stack2" Orientation="Vertical" Grid.Column="2" >
            <Button Content="Save changes" 
                   
 Command="{Binding SaveCommand}"
                    Name="SaveButton" Style="{StaticResource ButtonStyle}" />

           <Button Content="Next"
  
                   
 Name="NextButton"
 
                   
 Command="{Binding NextCommand}"
 
                   
 Style="{StaticResource ButtonStyle}" />
            <Button Content="Previous" Command="{Binding PreviousCommand}"
                    Name="PrevButton" Style="{StaticResource ButtonStyle}" />
            <Button Content="Delete order"
                    Name="DeleteButton"
  
                   
 Command="{Binding DeleteCommand}"
 
                   
 Style="{StaticResource ButtonStyle}" />
            <Button Content="New order"
 
                   
 Command="{Binding Path=InsertCommand}"
                    Name="NewButton"
 
                   
 Style="{StaticResource ButtonStyle}" />
            <Button Content="View details"
                    Command="{Binding ViewDetailsCommand}"
                    Name="Button1"
  
                   
 Style="{StaticResource ButtonStyle}"  />
        </StackPanel>
    </Grid>
</Window>

Ricordate quindi come non si gestisca l'evento Click, ma si associa la proprietà Command a una delle proprietà di tipo ICommand che abbiamo predisposto nel ViewModel. Passiamo al code-behind in Visual Basic per rendere operativa la finestra principale.

No code? No party!
Nei primi post di questa serie, abbiamo visto che nel code-behind della finestra principale, ossia la prima View, c'era solamente codice che istanziava il ViewModel e lo assegnava al DataContext della Window stessa. Ora, il principio è questo: la View può contenere altro codice, purché sia codice relativo alla gestione esclusiva dell'interfaccia grafica e che, quindi, non acceda a dati o cose simili. Nel nostro caso specifico, il code-behind della Window conterrà senza dubbio il codice che istanzia e assegna il ViewModel, ma in più si occuperà di registrare il messaggio che, una volta emesso, consentirà di visualizzare la finestra relativa agli Order Details, cosa che dobbiamo fare utilizzando il metodo Register della classe Messenger. In sostanza, questo è il code-behind:

Class MainWindow
 
    
Private Sub MainWindow_Loaded(ByVal sender As ObjectByVal e As System.Windows.RoutedEventArgsHandles Me.Loaded
        
Dim startupViewModel As New OrdersViewModel
        Me.MainGrid.DataContext = startupViewModel
 
        
Application.Msn.Register(Application.VIEW_DETAILS_EXECUTE, Sub()
                                                                       
Try
                                                                           'Ottiene l'istanza dell'Order corrente e ne passa l'ID al costruttore
                                                                           'della finestra OrderDetailsView
                                                                           Dim odv As New OrderDetailsView(CType(startupViewModel.CustomerOrdersViewSource.View.CurrentItem, Order).OrderID)
                                                                           odv.ShowDialog()
                                                                           odv = 
Nothing
                                                                       Catch ex As Exception
                                                                           MessageBox.Show(ex.Message)
                                                                       
End Try
                                                                   End Sub)
    
End Sub
End Class

Queste sono le considerazioni fondamentali:

  1. l'istanza del ViewModel viene assegnata non al DataContext della Window ma a quello della prima Grid. Questo è un caso particolare, poiché, per linea gerarchica, i vari controlli andranno a popolarsi da questo primo contenitore.
  2. il metodo Register riceve, come argomenti, il messaggio da registrare e l'azione da intraprendere nel momento il cui il messaggio è emesso (da parte del ViewModel). In questo caso il messaggio è stato definito come costante a livello di applicazione, mentre l'azione da intraprendere è stata definita attraverso una statement lambda invece che puntare a un delegate separato attraverso AddressOf (pratica che comunque andava bene ugualmente).
  3. Con riferimento al secondo argomento del metodo Register, la lambda ottiene l'istanza dell'Order selezionato e passa il suo identificativo al costruttore di una finestra chiamata OrderDetailsView, che creeremo tra breve, quindi istanzia e mostra tale finestra. Trattandosi di codice legato alla UI, si può tranquillamente scrivere a livello di View.
  4. Per quanto riguarda lo scambio di messaggi: la View registra il messaggio e l'applicazione rimarrà in ascolto -> il ViewModel emette il messaggio quando è invocato il command desiderato -> la View intercetta il messaggio e istanzia la nuova finestra

Completata la finestra principale, passiamo alla seconda View.

Una View per gli Order Details

Nella nostra applicazione di esempio gli Order Details verranno mostrati all'interno di una DataGrid, in una nuova finestra; per semplicità non implementeremo altri controlli ma nessuno vi vieta di farlo per conto vostro, una volta capita la logica. Quindi, alla cartella di progetto chiamata Views aggiungiamo una nuova Window chiamata OrderDetailsView.xaml. Quando pronti, potete facilitarvi il compito utilizzando il data-binding drag'n'drop, quindi trascinando dalla finestra Data Sources l'entità Order_Details sul designer della finestra. Questo abiliterà alcune funzioni di design-time per il lavoro sui dati, ad ogni buon conto lo XAML che dovremo scrivere è il seguente (comprensivo delle espressioni generate da Visual Studio 2010 a seguito di drag'n'drop, utili a design time):

<Window x:Class="OrderDetailsView"

    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"

    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"

    xmlns:my="clr-namespace:adsMVVM_EntityFramework_Complete"

    xmlns:dal="clr-namespace:DAL;assembly=DAL" ShowInTaskbar="True" WindowStartupLocation="CenterScreen"

    Title="OrderDetailsView" Height="300" Width="370" mc:Ignorable="d" xmlns:d="http://schemas.microsoft.com/expression/blend/2008"

    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:my1="clr-namespace:DAL;assembly=DAL">

    <Window.Resources>

        <CollectionViewSource x:Key="Order_DetailViewSource" Source="{Binding Path=Order_Details}" d:DesignSource="{d:DesignInstance dal:Order_Detail, CreateList=True}" />

    </Window.Resources>

    <Grid DataContext="{StaticResource Order_DetailViewSource}">

        <DataGrid AutoGenerateColumns="False" EnableRowVirtualization="True" ItemsSource="{Binding}" Name="Order_DetailsDataGrid"

                  RowDetailsVisibilityMode="VisibleWhenSelected">

            <DataGrid.Columns>

                <DataGridTextColumn x:Name="ProductIDColumn" Binding="{Binding Path=ProductID}" Header="Product ID" Width="SizeToHeader" />

                <DataGridTextColumn x:Name="UnitPriceColumn" Binding="{Binding Path=UnitPrice, StringFormat=c}" Header="Unit Price" Width="SizeToHeader" />

                <DataGridTextColumn x:Name="QuantityColumn" Binding="{Binding Path=Quantity}" Header="Quantity" Width="SizeToHeader" />

                <DataGridTextColumn x:Name="DiscountColumn" Binding="{Binding Path=Discount, StringFormat=p}" Header="Discount" Width="SizeToHeader" />

            </DataGrid.Columns>

        </DataGrid>

    </Grid>

</Window>

Potete quindi notare come sia possibile ricorrere a una CollectionViewSource per impostare, a design-time, il DataContext della nostra Grid. Tale sorgente punta alla proprietà Order_Details esposta dal ViewModel. A livello di code-behind, il discorso è simile alla View principale. Qui dobbiamo esplicitare il costruttore, prevedendo la ricezione, come parametro, dell'identificativo dell'ordine perché questo servirà al ViewModel per estrarre i dati relativi all'Order richiesto:

Public Class OrderDetailsView
    Private _orderID As Integer
    Private WithEvents OrderDetailsVM As OrderDetailsViewModel

    
Sub New(ByVal OrderID As Integer
)
        InitializeComponent()
        
' TODO: Complete member initialization 
        Me
._orderID = OrderID
    
End Sub
 
    
Private Sub Window_Loaded(ByVal sender As System.ObjectByVal e As System.Windows.RoutedEventArgsHandles MyBase
.Loaded
        
Me.Title = "Order details for order nr. " & Me
._orderID.ToString
        
Me.OrderDetailsVM = New OrderDetailsViewModel
(_orderID)
        
Me.DataContext = Me
.OrderDetailsVM
        
Application.Msn.Register(Application.VIEW_DETAILS_CLOSE, Sub
()
                                                                     
Me
.Close()
                                                                 
End Sub
)
    
End Sub
End Class

Essenzialmente qui il messaggio per la chiusura della finestra è implementato ma non utilizzato in modo effettivo, potete rimuoverlo, se preferite, oppure utilizzarlo agganciandolo a un pulsante di chiusura che punti al comando CloseCommand esposto dal ViewModel associato.

Premiamo F5, finalmente!
Al termine di tutto questo grande lavoro, possiamo finalmente avviare l'applicazione. Il tutto si presenterà più o meno così, con la finestra degli Order Details aperta:

Questo, invece, è ciò che otterremo nel caso di un errore di validazione:

Conclusioni e Download

Come ampiamente detto, potrebbero esserci delle imprecisioni nella serie di articoli ma spero che comunque sia risultata utile per iniziare ad approcciare MVVM, dal punto di vista di Visual Basic 2010, soprattutto in questa seconda parte dedicata ad Entity Framework perché la rete è veramente parca di esempi in merito. La discussione rimane chiaramente aperta e comunque tornerò presto sull'argomento, innanzitutto fornendo un post in cui sono riepilogate tutte le puntate della serie e poi vedendo come costruire un piccolo framework riutilizzabile. Il codice sorgente dell'applicazione completa può essere scaricata da questo indirizzo dell'area Download di VB T&T, ricordando che per poter utilizzare il codice dovete avere installato il database Northwind, reso disponibile sull'istanza di SQL Server (potete naturalmente cambiare la stringa di connessione se avete il file in locale, ad esempio).

Vi aspetto quindi con i prossimi post su MVVM!

Alessandro

Print | posted on martedì 10 agosto 2010 18:33 | Filed Under [ Visual Basic Windows Presentation Foundation Visual Studio 2010 ]

Powered by:
Powered By Subtext Powered By ASP.NET