images Workshop: Creating a Master/Details Screen

Let's create a very simple master/details screen to allow the user to edit product data.

  1. Add a new view to your project, named EditProductsView.xaml.
  2. Split the LayoutRoot Grid vertically into two rows.
    <navigation:Page x:Class="AdventureWorks.Views.EditProductsView"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
             xmlns:navigation="clr-namespace:System.Windows.Controls;    images
                                assembly=System.Windows.Controls.Navigation"
             mc:Ignorable="d"
             d:DesignWidth="640" d:DesignHeight="480"
             Title="Edit Products">

        <Grid x:Name="LayoutRoot">
            <Grid.RowDefinitions>
                <RowDefinition Height="*" />
                <RowDefinition Height="*" />
            </Grid.RowDefinitions>
        </Grid>
    </navigation:Page>
  3. Define the following namespace prefixes on the root element in the file:
    xmlns:riaControls="clr-namespace:System.Windows.Controls;    images
                        assembly=System.Windows.Controls.DomainServices"
    xmlns:sdk="http://schemas.microsoft.com/winfx/2006/xaml/presentation/sdk"
    xmlns:toolkit=
      "http://schemas.microsoft.com/winfx/2006/xaml/presentation/toolkit"
    xmlns:my="clr-namespace:AdventureWorks.Web"
    xmlns:my1="clr-namespace:AdventureWorks.Web.Services"
  4. For simplicity's sake, we'll use the DomainDataSource control to handle loading the data from the server in this workshop. Add the DomainDataSource control to your view, as content of the LayoutRoot Grid control, and hook it up to the GetProducts domain operation exposed by the ProductService domain service that you created in Chapter 4.
    <riaControls:DomainDataSource AutoLoad="True" Height="0" Width="0"
                      d:DesignData="{d:DesignInstance my:Product, CreateList=true}"
                      Name="productDDS" QueryName="GetProductsQuery">
        <riaControls:DomainDataSource.DomainContext>
            <my1:ProductContext />
        </riaControls:DomainDataSource.DomainContext>
    </riaControls:DomainDataSource>
  5. Add a DataGrid control to the top row of the LayoutRoot Grid, and bind its ItemsSource property to the DomainDataSource control.
    <sdk:DataGrid ItemsSource="{Binding ElementName=productDDS, Path=Data}" />
  6. Now, we need to create the data entry form. You've seen a couple of ways to generate this, including using the Data Sources window, using the XAML Power Toys, and using the DataForm control. However, for the purpose of this workshop we'll create it manually. We'll add a Grid to the bottom row of the LayoutRoot Grid, which we'll use to lay out our data entry form. The following XAML demonstrates a simple data entry form that allows you to edit five properties on a Product entity. It includes three text boxes (one which formats how the value should be displayed using the Binding object's StringFormat property), and two DatePicker controls. It also includes a ValidationSummary control to display the details of any data validation errors, hence the need for all the bindings to have their NotifyOnValidationError set to True so that the ValidationSummary control can be made aware of their validation errors.
    <Grid Grid.Row="1" DataContext="{Binding ElementName=productDDS, Path=Data}">
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto" />
            <RowDefinition Height="Auto" />
            <RowDefinition Height="Auto" />
            <RowDefinition Height="Auto" />
            <RowDefinition Height="Auto" />
            <RowDefinition Height="*" />
        </Grid.RowDefinitions>
                
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="100" />
            <ColumnDefinition Width="250" />
        </Grid.ColumnDefinitions>

        <sdk:Label Grid.Row="0" Content="Name:"
                    Target="{Binding ElementName=NameField}" />
        <sdk:Label Grid.Row="1" Content="Product Number:"
                    Target="{Binding ElementName=ProductNumberField}" />
        <sdk:Label Grid.Row="2" Content="List Price:"
                    Target="{Binding ElementName=ListPriceField}" />
        <sdk:Label Grid.Row="3" Content="Sell Start Date:"
                    Target="{Binding ElementName=SellStartDate}" />
        <sdk:Label Grid.Row="4" Content="Sell End Date:"
                   Target="{Binding ElementName=SellEndDate}" />

        <TextBox Name="NameField" Grid.Column="1" Grid.Row="0" Margin="2"
                 Text="{Binding Name, Mode=TwoWay,
                                NotifyOnValidationError=True}" />

        <TextBox Name="ProductNumberField" Grid.Column="1" Grid.Row="1" Margin="2"
                 Text="{Binding ProductNumber, Mode=TwoWay,
                                NotifyOnValidationError=True}" />

        <TextBox Name="ListPriceField"
                 Grid.Column="1" Grid.Row="2" Margin="2"
                 Text="{Binding ListPrice, Mode=TwoWay, StringFormat=C,
                                NotifyOnValidationError=True}" />

        <sdk:DatePicker Name="SellStartDateField"
                        Grid.Column="1" Grid.Row="3" Margin="2"
                        SelectedDate="{Binding SellStartDate, Mode=TwoWay,
                                               NotifyOnValidationError=True}" />

        <sdk:DatePicker Name="SellEndDateField"
                        Grid.Column="1" Grid.Row="4" Margin="2"
                        SelectedDate="{Binding SellEndDate, Mode=TwoWay,
                                               NotifyOnValidationError=True}" />
                
        <sdk:ValidationSummary Grid.Row="5" Grid.ColumnSpan="2"
                               VerticalAlignment="Bottom" />
    </Grid>

    images Note As you can see, we're binding the top-level Grid control's DataContext property to the collection returned by the DomainDataSource control. The DomainDataSource control's Data property returns a collection view (a DomainDataSourceView), which maintains a CurrentItem property. Controls that expect to be bound to a single object instead of a collection see that they are bound to a collection view, automatically drill down to this property, and bind to the object that it exposes. The DataGrid control is also bound to this same collection view, and when a row is selected in the DataGrid, it will update the collection view's CurrentItem property accordingly. Therefore, when you select an item in the DataGrid, the details for that item will appear in the data entry form. This synchronization feature provided by collection views is very neat and makes it very easy to implement master/details views.

  7. Now run your application. When you select an item in the DataGrid control, the details of that item will appear in the data entry form. When you enter some invalid data (such as clearing the value of the Name field), the control will change its appearance to indicate that the data is invalid, and a validation error will appear in the ValidationSummary control.

images Note The next step is to provide a way to save the changes back to the server, which you can implement by binding a Button control to the DomainDataSource control's SubmitChangesCommand property, as described in Chapter 5.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset