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 in Visual Basic 2010 Beta 1 - part three (DataSets)

In this blog post I'm going to retake what I discussed in the previous ones, regarding the new drag'n'drop techniques for WPF data-binding in Visual Studio 2010 Beta 1, specifically for Visual Basic applications. We saw how easy is creating applications for tabular data representations implementing the new DataGrid  control both using DataSets and Entity Data Models.

 

Now we're going to do something different and we'll take the advantage of the drag'n'drop data-binding for generating an interesting master-details representation. We'll begin working versus a DataSet based on a Microsoft Access database, as we did in the first blog post of this series. In the next and last post we'll repeat the same scenario working with an Entity Data Model based on a SQL Server database.

 

So, let's create a new WPF project in Visual Basic 2010 and repeat all the steps we performed in the first post, until the generation of the DataSet; basically we must be able to see in the Data Sources window the availability of the Customers and Orders objects. Then let's build the project so that all data references can be updated.

At this point we can divide the main Grid into two columns and two rows, moreover we want to add a Orders control that will store the list of Customers, as follows:

 

    <Grid>

        <Grid.ColumnDefinitions>

            <ColumnDefinition Width="200"/>

            <ColumnDefinition/>

        </Grid.ColumnDefinitions>

        <Grid.RowDefinitions>

            <RowDefinition/>

            <RowDefinition Height="40"/>

        </Grid.RowDefinitions>

        <ListBox Grid.Column="0" Grid.Row="0"/>

    </Grid>

 

Let's switch to the Data Sources window, expanding Customers and selecting the item named CompanyName. Then let's drag this item onto the ListBox and release:

Visual Studio will therefore generate the following XAML code (the one edit I made is related to the position of the control in rows/columns):

 

        <ListBox Grid.Column="0" DisplayMemberPath="CompanyName" Grid.Row="0"

                 IsSynchronizedWithCurrentItem="True"

                 ItemsSource="{Binding}" SelectedIndex="0" />

 

So the ListBox is populated via data-binding, as we should already know. To make this happen, Visual Studio generated also two new resources: the DataSet reference and a CollectionViewSource pointing to the Customers that are exposed by the DataSet itself.

 

    <Window.Resources>

        <my:NorthwindDataSet x:Key="NorthwindDataSet" />

        <CollectionViewSource x:Key="CustomersViewSource" Source="{Binding Path=Customers, Source={StaticResource NorthwindDataSet}}" />

    </Window.Resources>

    <Grid DataContext="{StaticResource CustomersViewSource}">

 

Assigning the DataContext property of the Grid will finalize the binding. Now let's go back to the Data Sources window and select the Orders item which is nested inside Customers, ensuring to select the Details view as in the following figure:

 

 

Let's drag this item and release it onto the blank space in the Window. After a few seconds we'll get the following result:

This is represented by some automated additions in the XAML code. First, a new CollectionViewSource pointing to orders related to coustomers is added:

 

        <CollectionViewSource x:Key="CustomersOrdersViewSource" Source="{Binding Path=CustomersOrders, Source={StaticResource CustomersViewSource}}" />

 

Second, a new Grid is added. Here the DataContext property is assigned with the new CollectionViewSource, that defines a list of properties for each Order object, in a typical Details fashion: 

 

        <Grid DataContext="{StaticResource CustomersOrdersViewSource}" Grid.Column="1" Name="Grid1" Grid.Row="0">

            <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}"

                     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}"

 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}"

 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" HorizontalAlignment="Left" Margin="3" Name="OrderDateDatePicker" SelectedDate="{Binding Path=OrderDate}"

 VerticalAlignment="Center" />

            <Label Content="Required Date:" Grid.Column="0" Grid.Row="4" HorizontalAlignment="Left" Margin="3" VerticalAlignment="Center" />

            <DatePicker Grid.Column="1" Grid.Row="4" HorizontalAlignment="Left" Margin="3" Name="RequiredDateDatePicker" SelectedDate="{Binding Path=RequiredDate}"

 VerticalAlignment="Center" />

            <Label Content="Shipped Date:" Grid.Column="0" Grid.Row="5" HorizontalAlignment="Left" Margin="3" VerticalAlignment="Center" />

            <DatePicker Grid.Column="1" Grid.Row="5" HorizontalAlignment="Left" Margin="3" Name="ShippedDateDatePicker" SelectedDate="{Binding Path=ShippedDate}"

 VerticalAlignment="Center" />

            <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}"

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}"

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}"

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}"

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}"

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}"

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}"

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}"

VerticalAlignment="Center" Width="120" />

        </Grid>

 

It's worth mentioning that each property of an Order is represented by a Label/TextBox group or by a Label/DatePicker group, and this is because Visual Studio is able to associate the most appropriate control to the UI, according to the data type that must be represented. All data controls are populated via databinding (Binding Path=).

 

We can now switch to the Visual Basic code. The following is the code generated by Visual Studio 2010 Beta 1:

 

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

 

        Dim NorthwindDataSet As ItemsBrowsingDemoWithDataSet.NorthwindDataSet = CType(Me.FindResource("NorthwindDataSet"),

                                ItemsBrowsingDemoWithDataSet.NorthwindDataSet)

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

        Dim NorthwindDataSetCustomersTableAdapter As ItemsBrowsingDemoWithDataSet.NorthwindDataSetTableAdapters.CustomersTableAdapter =

            New ItemsBrowsingDemoWithDataSet.NorthwindDataSetTableAdapters.CustomersTableAdapter()

        NorthwindDataSetCustomersTableAdapter.Fill(NorthwindDataSet.Customers)

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

            System.Windows.Data.CollectionViewSource)

        CustomersViewSource.View.MoveCurrentToFirst()

        'Load data into the table Orders. You can modify this code as needed.

        Dim NorthwindDataSetOrdersTableAdapter As ItemsBrowsingDemoWithDataSet.NorthwindDataSetTableAdapters.OrdersTableAdapter = New

            ItemsBrowsingDemoWithDataSet.NorthwindDataSetTableAdapters.OrdersTableAdapter()

        NorthwindDataSetOrdersTableAdapter.Fill(NorthwindDataSet.Orders)

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

            System.Windows.Data.CollectionViewSource)

        CustomersOrdersViewSource.View.MoveCurrentToFirst()

    End Sub

 

In a few words, the above code retrieves the instances of the DataSet and of CollectionViewSources. Moreover, the code populates TableAdapters related to Customers and Orders. The "real" data-binding is performed on the XAML side.

This code is quite useful but it has one important limitation: it doesn't give us the ability to move between orders, which means the ability of moving between the objects that constitute the collections populating CollectionViewSources. So this must be accomplished manually.

First of all, we have to move to class level the declarations of DataSet, CollectionViewSource and TableAdapter:

 

    'Moving to class level

    Private NorthwindDataSet As ItemsBrowsingDemoWithDataSet.NorthwindDataSet

    Private CustomersViewSource As System.Windows.Data.CollectionViewSource

    Private CustomersOrdersViewSource As System.Windows.Data.CollectionViewSource

    Private NorthwindDataSetOrdersTableAdapter As ItemsBrowsingDemoWithDataSet.NorthwindDataSetTableAdapters.OrdersTableAdapter

 

In the code below we're going to replace the instance declaration of the above objects with an assignment:

 

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

 

        NorthwindDataSet = CType(Me.FindResource("NorthwindDataSet"), ItemsBrowsingDemoWithDataSet.NorthwindDataSet)

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

        Dim NorthwindDataSetCustomersTableAdapter As ItemsBrowsingDemoWithDataSet.NorthwindDataSetTableAdapters.CustomersTableAdapter =

            New ItemsBrowsingDemoWithDataSet.NorthwindDataSetTableAdapters.CustomersTableAdapter()

        NorthwindDataSetCustomersTableAdapter.Fill(NorthwindDataSet.Customers)

 

        'Modifico la dichiarazione della CollectionViewSource

        CustomersViewSource = CType(Me.FindResource("CustomersViewSource"), System.Windows.Data.CollectionViewSource)

        CustomersViewSource.View.MoveCurrentToFirst()

 

        'Modifico la dichiarazione del TableAdapter.

        NorthwindDataSetOrdersTableAdapter = New ItemsBrowsingDemoWithDataSet.NorthwindDataSetTableAdapters.OrdersTableAdapter()

        NorthwindDataSetOrdersTableAdapter.Fill(NorthwindDataSet.Orders)

 

        'Modifico la dichiarazione della CollectionViewSource

        CustomersOrdersViewSource = CType(Me.FindResource("CustomersOrdersViewSource"), System.Windows.Data.CollectionViewSource)

        CustomersOrdersViewSource.View.MoveCurrentToFirst()

    End Sub

 

Once completed this, let's go back to the XAML code editor so that we can define three new buttons: two for moving between items and one for saving changes:

 

        <StackPanel Orientation="Horizontal" Grid.Column="1" Grid.Row="1">

            <Button Margin="5" Content="&lt;Previous" Name="PrevButton" Width="100" Height="30"/>

            <Button Margin="5" Content="Next>" Name="NextButton" Width="100" Height="30"/>

            <Button Margin="5" Content="Save changes" Name="SaveButton" Width="100" Height="30"/>

        </StackPanel>

 

Then we can take advantage of Views from the CollectionViewSources objects for going forward and back. Let's consider the following code that accomplishes this:

 

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

        If Me.CustomersOrdersViewSource.View.CurrentPosition < CType(CustomersOrdersViewSource.View, CollectionView).Count - 1 Then

            Me.CustomersOrdersViewSource.View.MoveCurrentToNext()

        End If

    End Sub

 

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

        If Me.CustomersOrdersViewSource.View.CurrentPosition > 0 Then

            Me.CustomersOrdersViewSource.View.MoveCurrentToPrevious()

        End If

    End Sub

 

While the following code will save changes:

 

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

        Try

            Me.NorthwindDataSetOrdersTableAdapter.Update(Me.NorthwindDataSet.Orders)

            MessageBox.Show("Done!")

        Catch ex As Exception

            MessageBox.Show("An error occurred while attempting to save changes")

        End Try

    End Sub

 

We can now try and run our application that should look like the following figure:

Obviously this code could be improved in several ways, for example using techniques such as the Model-View-ViewModel that will help us dividing the commands from the interface. Anyway, I think that this is a good example for learning something about the Views, which also expose methods for adding and removing objects. For example, you might want to consider adding a new window to the project so that you could be able to add new orders via data-binding: in this case, you'll use methods of the Views.

 

In next blog post we'll complete the series talking about the same scenario that will be reproduced using an Entity Data Model as the data source. I'll also attach the complete source code for the series.

 

Alessandro

Print | posted on venerdì 19 giugno 2009 14: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 5 and 4 and type the answer here:

Powered by:
Powered By Subtext Powered By ASP.NET