Alessandro Del Sole's Blog

/* A programming space about Microsoft® .NET® */
posts - 159, comments - 0, trackbacks - 0

My Links

News

Your host

This is me! This space is about Microsoft® .NET® and Microsoft® Visual Basic development. Enjoy! :-)

These postings are provided 'AS IS' for entertainment purposes only with absolutely no warranty expressed or implied and confer no rights.

Microsoft MVP

My MVP Profile

I'm a VB!

Watch my interview in Seattle

My new book on VB 2015!

Pre-order VB 2015 Unleashed Pre-order my new book "Visual Basic 2015 Unleashed". Click for more info!

My new book on LightSwitch!

Visual Studio LightSwitch Unleashed My book "Visual Studio LightSwitch Unleashed" is available. Click the cover!

Your visits

Follow me on Twitter!

CodePlex download Download my open-source projects from CodePlex!

Article Categories

Archives

Post Categories

.NET Framework

Blogroll

Help Authoring

Microsoft & MSDN

Setup & Deployment

Visual Basic 2005/2008/2010

WPF: the drag'n'drop data-binding with Visual Basic 2010 Beta 1 - (part two, Entity Framework)

In my previous post I introduced one of the new features available in Microsoft Visual Studio 2010, the drag'n'drop data-binding for WPF applications. In that article we saw how we can use this kind of technique versus DataSets and, in that particular case, using a Microsoft Access database for tabular data representations. 

 

In this post I'm going to reach the same objective but this time we'll work using an Entity Data Model generated by the ADO.NET Entity Framework, versus a SQL Server database.

 

So, once Visual Studio 2010 Beta 1 is running, let's create a new Visual Basic project for a WPF application performing the same steps shown in the previous post. Now let's add a new Entity Data Model to our project, via the Project|Add new item command and then selecting the appropriate item template from the Add new item window:

The first step of the Wizard will require if the model must be generated from scratch or starting from an existing database. Let's choose the existing database option:

and then click Next. In the second window of the Wizard we have to set the data connection, in my example I chose the Northwind database. We can leave unchanged the other options and then click Next:

The third step is selecting tables from the database that we want to be mapped as entities inside the new Entity Data Model. We can still choose Customers and Orders, so that we can build a typical master-details scenario. An interesting new feature here is that we can pluralize or singularize entities' names just flagging the appropriate CheckBox. Visual Studio will then pluralize or singularize names for the EntitySets based on our selection:

 

 

In this case each entity's name will be singularized while EntitySets will be pluralized (each order is an Order object, the collection of orders if now Ordes object).

 

After a few seconds the new object model is available inside the Entity Framework designer for Visual Studio, as we can see in the following figure:

 

At this point it would be good saving and compiling the project. Now let's open the Data Sources window, via the Data|Show Data Sources command. We should see something like this:

 

It's worth mentioning that now in Dev10 the Data Sources window offers support for the ADO.NET Entity Framework.

We can repeat the same steps we saw in my previous post, so let's drag the CompanyName item of the Customers object onto the Window and we'll see how Visual Studio will generate the following XAML:

 

    <Window.Resources>

        <CollectionViewSource x:Key="CustomersViewSource__NorthwindEntities" d:DesignSource="{d:DesignInstance my:Customer,

                              CreateList=True}" />

    </Window.Resources>

    <Grid>

        <Grid DataContext="{StaticResource CustomersViewSource__NorthwindEntities}" Height="31.96" Margin="0,0,48,230" Name="Grid1"

              Width="230.136666666667">

            <Grid.ColumnDefinitions>

                <ColumnDefinition Width="Auto" />

                <ColumnDefinition Width="Auto" />

            </Grid.ColumnDefinitions>

            <Grid.RowDefinitions>

                <RowDefinition Height="Auto" />

            </Grid.RowDefinitions>

            <Label Content="Company Name:" Grid.Column="0" Grid.Row="0" HorizontalAlignment="Left" Margin="3"

                   VerticalAlignment="Center" />

            <ComboBox DisplayMemberPath="CompanyName" Grid.Column="1" Grid.Row="0" Height="23" HorizontalAlignment="Left"

                      IsSynchronizedWithCurrentItem="True"

                      ItemsSource="{Binding}" Margin="3" Name="CompanyNameComboBox" SelectedIndex="0" VerticalAlignment="Center"

                      Width="120">

                <ComboBox.ItemsPanel>

                    <ItemsPanelTemplate>

                        <VirtualizingStackPanel />

                    </ItemsPanelTemplate>

                </ComboBox.ItemsPanel>

            </ComboBox>

        </Grid>

    </Grid>

 

So let's mention again that the IDE generated a CollectionViewSource whose work is binding the data source to the UI and that is also the source for the ItemsSource properties of the ComboBox (this is because assigning the DataContext property of the Grid panel will populate nested controls). So the Combo is populated via binding and shows (DisplayMemberPath) the value of the property CompanyName from the bound data source, in this case a list of customers.

 

From the Data Sources window let's then drag onto the Window the Orders object nested in Customers, so that we can generate a new DataGrid which will be automatically populated via data-binding. In such situation Visual Studio will generate the following XAML for the DataGrid:

 

        <DataGrid AutoGenerateColumns="False" EnableRowVirtualization="True" Height="200" HorizontalAlignment="Left"

                  IsSynchronizedWithCurrentItem="True" ItemsSource="{Binding}"

                  Margin="60,106,0,0" Name="OrdersDataGrid" RowDetailsVisibilityMode="VisibleWhenSelected" SelectedIndex="0"

                  VerticalAlignment="Top" Width="400">

            <DataGrid.Columns>

                <DataGridTextColumn Binding="{Binding Path=OrderID}" Header="Order ID" Width="SizeToHeader" />

                <DataGridTextColumn Binding="{Binding Path=EmployeeID}" Header="Employee ID" Width="SizeToHeader" />

                <DataGridTemplateColumn Header="Order Date" Width="SizeToHeader">

                    <DataGridTemplateColumn.CellTemplate>

                        <DataTemplate>

                            <DatePicker SelectedDate="{Binding Path=OrderDate}" />

                        </DataTemplate>

                    </DataGridTemplateColumn.CellTemplate>

                </DataGridTemplateColumn>

                <DataGridTemplateColumn Header="Required Date" Width="SizeToHeader">

                    <DataGridTemplateColumn.CellTemplate>

                        <DataTemplate>

                            <DatePicker SelectedDate="{Binding Path=RequiredDate}" />

                        </DataTemplate>

                    </DataGridTemplateColumn.CellTemplate>

                </DataGridTemplateColumn>

                <DataGridTemplateColumn Header="Shipped Date" Width="SizeToHeader">

                    <DataGridTemplateColumn.CellTemplate>

                        <DataTemplate>

                            <DatePicker SelectedDate="{Binding Path=ShippedDate}" />

                        </DataTemplate>

                    </DataGridTemplateColumn.CellTemplate>

                </DataGridTemplateColumn>

                <DataGridTextColumn Binding="{Binding Path=ShipVia}" Header="Ship Via" Width="SizeToHeader" />

                <DataGridTextColumn Binding="{Binding Path=Freight}" Header="Freight" Width="SizeToHeader" />

                <DataGridTextColumn Binding="{Binding Path=ShipName}" Header="Ship Name" Width="SizeToHeader" />

                <DataGridTextColumn Binding="{Binding Path=ShipAddress}" Header="Ship Address" Width="SizeToHeader" />

                <DataGridTextColumn Binding="{Binding Path=ShipCity}" Header="Ship City" Width="SizeToHeader" />

                <DataGridTextColumn Binding="{Binding Path=ShipRegion}" Header="Ship Region" Width="SizeToHeader" />

                <DataGridTextColumn Binding="{Binding Path=ShipPostalCode}" Header="Ship Postal Code" Width="SizeToHeader" />

                <DataGridTextColumn Binding="{Binding Path=ShipCountry}" Header="Ship Country" Width="SizeToHeader" />

            </DataGrid.Columns>

        </DataGrid>

 

The DataGrid is also populated via data-binding through the assignment of the ItemsSource. Generation of columns is not automatic and this allows Visual Studio to use the most appropriate controls depending of what data type we have to represent, e.g. DatePicker inside custom columns (DataGridTemplateColumn) for showing DateTime objects while for each other fields a default DataGridTextColumn is used.

 

Moreover, the IDE generated the following line of XAML inside the resources that defines a new CollectionViewSource for working with orders:

 

        <CollectionViewSource x:Key="CustomersOrdersViewSource" Source="{Binding Path=Orders, Source={StaticResource

                              CustomersViewSource__NorthwindEntities}}" />

 

Now let's switch to the Visual Basic code. First, we can observe how VS generated a method that queries the customers list. This query result will then populate the Combo: 

 

    Private Function GetCustomersQuery(ByVal NorthwindEntities As TabularDataWithEntityFramework.NorthwindEntities) As

                                             System.Data.Objects.ObjectQuery(Of

                                             TabularDataWithEntityFramework.Customer)

 

        Dim CustomersQuery As System.Data.Objects.ObjectQuery(Of TabularDataWithEntityFramework.Customer) = NorthwindEntities.Customers

        'Update the query to include Orders data in Customers. You can modify this code as needed.

        CustomersQuery = CustomersQuery.Include("Orders")

        'Do not modify the following code.

        Return CustomersQuery

    End Function

 

It's a basic query that retrieves the list of customers with related orders (Include method) in form of an ObjectQuery(Of Customer). The subsequent code loads the window and performs the data-binding:

 

    Private Sub Window_Loaded(ByVal sender As System.Object, ByVal e As System.Windows.RoutedEventArgs) Handles MyBase.Loaded

 

        Dim NorthwindEntities As TabularDataWithEntityFramework.NorthwindEntities = New TabularDataWithEntityFramework.NorthwindEntities()

        'Load data into Customers. You can modify this code as needed.

        Dim CustomersViewSource__NorthwindEntities As System.Windows.Data.CollectionViewSource = CType(Me.FindResource("CustomersViewSource__NorthwindEntities"),

                                                                                                       System.Windows.Data.CollectionViewSource)

        Dim CustomersQuery As System.Data.Objects.ObjectQuery(Of TabularDataWithEntityFramework.Customer) = Me.GetCustomersQuery(NorthwindEntities)

        CustomersViewSource__NorthwindEntities.Source = CustomersQuery.Execute(System.Data.Objects.MergeOption.AppendOnly)

        CustomersViewSource__NorthwindEntities.View.MoveCurrentToFirst()

    End Sub

 

This event handler retrieves the instance of the Entity Framework's ObjectContext (NorthwindEntities), then retrieves the instance of the CollectionViewSource related to customers. Since the above query is including also related orders, the DataGrid can auto-populate based on this situation.

 

But we might need some modifications. Let's go back to the XAML code editor so that we can rearrange UI controls and add a new button for saving changes. Let's divide the main Grid into three rows:

 

    <Grid DataContext="{StaticResource CustomersOrdersViewSource}">

        <Grid.RowDefinitions>

            <RowDefinition Height="30" />

            <RowDefinition />

            <RowDefinition Height="40" />

        </Grid.RowDefinitions>

 

Let's move the controls for choosing the customers to the first row:

 

        <Grid DataContext="{StaticResource CustomersViewSource__NorthwindEntities}"  Name="Grid1" Grid.Row="0">

 

Then, let's move the DataGrid to the second row:

 

        <DataGrid AutoGenerateColumns="False" EnableRowVirtualization="True" IsSynchronizedWithCurrentItem="True"

                  ItemsSource="{Binding}" Margin="0,34,0,0" Name="OrdersDataGrid"

                  RowDetailsVisibilityMode="VisibleWhenSelected" SelectedIndex="0" Grid.Row="1">

 

In the end, let's add a new button to the third row as follows:

 

        <Button Grid.Row="2" Width="100" Height="30" Name="SaveButton" Content="Save changes" />

 

Now we should get something like this:

 

Let's double-click the new button so that the IDE can generate an event-handler stub for the Click event that we'll handle as follows: 

 

    Private Sub SaveButton_Click(ByVal sender As System.Object, ByVal e As System.Windows.RoutedEventArgs) Handles SaveButton.Click

        Try

            Me.NorthwindEntities.SaveChanges()

        Catch ex As Exception

            MessageBox.Show(ex.ToString)

        End Try

    End Sub

 

NorthwindEntities is not reachable now. We must move it from the Window_Loaded method to class level. We'll have the following declaration:

 

    'TabularDataWithEntityFramework is my project name and also the first-level namespace

    Dim NorthwindEntities As TabularDataWithEntityFramework.NorthwindEntities

 

Then, within the Window_Loaded, let's edit the line of code as follows:

 

        NorthwindEntities = New TabularDataWithEntityFramework.NorthwindEntities()

 

Now let's try and run the application. We should get a result similar to the following figure, where you can see how you can take advantage of specific UI controls for selecting/viewing DateTime:

As we mentioned in the previous post, the DataGrid control allows the Two Ways databinding, so we can just edit data inside the DataGrid and we can be sure that, when invoking SaveChanges, they will be written to the underlying database. Morevore, the DataGrid adds a new row by default so that it's very easy adding new records. 

 

In the following two posts we'll begin seeing something different, such as details views and moving between items using WPF's Views.

 

Alessandro

Print | posted on giovedì 18 giugno 2009 15:10 | Filed Under [ Visual Studio 2010 Visual Basic Windows Presentation Foundation ]

Feedback

No comments posted yet.

Post Comment

Title  
Name  
Email
Url
Comment   
Please add 3 and 2 and type the answer here:

Powered by:
Powered By Subtext Powered By ASP.NET