Silverlight and .NET RIA Services - Step by Step - 9

by rahul 10/10/2010 2:02:00 AM

Part 1 > Setup database, create entity data model, create a simple domain service, and expose OData end points
Part 2 > Consume OData Endpoints using Internet Explorer [or any other client] for that matter
Part 3 > Consume entities exposed using RIA services with Silverlight [code behind]
Part 4 > Consume entities exposed using RIA services with Silverlight [declaratively using XAML]
Part 5 > Write a converter that shows the text Loading… while the data is loaded in the background
Part 6 > Paging in Silverlight using .NET RIA Services declaratively
Part 7 > Pass parameters to a method declared in the DomainService class
Part 8 > Use Filter descriptors to filter data directly in XAML

In this post, you will learn how to Create, Edit, and Delete records, and with that finally you will need to go to the code behind Smile

    Change your Albums.xaml to appear as follows

<navigation:Page x:Class="ChinookSample.Views.Albums" 
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:ria="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.DomainServices"
xmlns:service="clr-namespace:ChinookSample.Web.Services"
xmlns:helper="clr-namespace:ChinookSample.Helpers"
mc:Ignorable="d"
xmlns:navigation="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Navigation"
d:DesignWidth="640" d:DesignHeight="480"
Title="Albums Page" xmlns:sdk="http://schemas.microsoft.com/winfx/2006/xaml/presentation/sdk">
    <Grid x:Name="LayoutRoot">
        <Grid.Resources>
            <helper:BooleanToVisibility x:Key="BoolToVisibility" />
        </Grid.Resources>
        <ria:DomainDataSource Name="source" AutoLoad="True" LoadSize="50" QueryName="GetFilteredAlbums">
            <ria:DomainDataSource.DomainContext>
                <service:dsAlbumAndArtist />
            </ria:DomainDataSource.DomainContext>
            <ria:DomainDataSource.QueryParameters>
                <ria:Parameter ParameterName="filter" Value="{Binding ElementName=txtFilter, Path=Text}" />
            </ria:DomainDataSource.QueryParameters>
        </ria:DomainDataSource>
        <ScrollViewer Margin="2" MinHeight="500" VerticalAlignment="Top">
            <StackPanel>
                <StackPanel Orientation="Horizontal" Margin="10,0,10,0">
                    <Button x:Name="btnAdd" Click="btnAdd_Click" Content="Add" Margin="1" Width="80" />
                    <Button x:Name="btnEdit" Click="btnEdit_Click" Content="Edit" Margin="1" Width="80" />
                    <Button x:Name="btnDelete" Click="btnDelete_Click" Content="Delete" 
Margin="1" Width
="80" /> <TextBlock x:Name="lblMessage" Width="300" Margin="5,0,0,0"
VerticalAlignment="Center" Text=""></TextBlock
> <TextBlock FontSize="11" FontWeight="Bold" Foreground="Brown"
VerticalAlignment="Center">Title:</TextBlock
> <TextBox x:Name="txtFilter" Margin="5,0,0,0" Width="200"></TextBox> </StackPanel> <sdk:DataPager x:Name="AlbumsPager" Margin="10,0,10,0" PageSize="10" Source="{Binding ElementName=source, Path=Data}" /> <sdk:DataGrid Margin="10,2,10,2" Name="gridAlbums" ItemsSource="{Binding ElementName=source, Path=Data}"> </sdk:DataGrid> </StackPanel> </ScrollViewer> <TextBlock Height="49" Text="Loading..." VerticalAlignment="Center" HorizontalAlignment="Center" FontSize="32" Visibility="{Binding ElementName=source, Path=IsLoadingData, Mode=TwoWay, Converter={StaticResource BoolToVisibility}}" /> </Grid> </navigation:Page>

    Notice> The query has been changed back to GetFilteredAlbums and we are going to use Parameters to filter out data
    Notice> Elements are added to provide the necessary interface for Add, Edit, Delete.
    For cleanliness of code, I am going to use a new form that does editing as well as inserting of records.
    Deletion of records will happen on the Albums page itself. If there are multiple items selected, all of them would be removed at one shot.
    Add a new Item – [Silverlight Child Window] in Views folder called AlbumsEditor.xaml

image

     Copy and paste the following XAML into this AlbumsEditor.xaml

<controls:ChildWindow 
    xmlns:data="http://schemas.microsoft.com/winfx/2006/xaml/presentation/toolkit"  
    x:Class="ChinookSample.Views.AlbumsEditor"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
    xmlns:controls="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls"
    Width="413" Height="184" 
    Title="This will change..." Closing="ChildWindow_Closing">
    <Grid x:Name="LayoutRoot" Margin="2">
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="*" />
        </Grid.ColumnDefinitions>
        <Grid.RowDefinitions>
            <RowDefinition Height="*" />
            <RowDefinition Height="Auto" />
        </Grid.RowDefinitions>
        <Button x:Name="CancelButton" Content="Cancel" Click="CancelButton_Click" 
Width="75" Height="23" HorizontalAlignment="Right" Margin="0,12,0,0" Grid.Row="2" Grid.Column="1" /> <Button x:Name="OKButton" Content="OK" Click="OKButton_Click" Width="75"
Height="23" HorizontalAlignment="Right" Margin="0,12,79,0" Grid.Row="2" Grid.Column="1" /> <TextBlock Grid.Column="0" Text="Title" HorizontalAlignment="Right" VerticalAlignment="Center" /> <data:DataForm x:Name="AlbumDetails" AutoGenerateFields="False" AutoEdit="False" AutoCommit="False"> <data:DataForm.EditTemplate> <DataTemplate> <data:DataField Label="Title"> <TextBox Text="{Binding Title, Mode=TwoWay}"></TextBox> </data:DataField> </DataTemplate> </data:DataForm.EditTemplate> </data:DataForm> </Grid> </controls:ChildWindow>

     Notice> ChildWindow_Closing event will take care of the following…

    Form is in Edit mode -> User presses Ok -> Valid -> Close
    Form is in Edit mode -> User presses Ok -> InValid -> Do not Close
    Form is in Edit mode -> User presses Cancel -> Close [No validation done]
    Form is in New Album mode -> User presses Ok -> Validate -> Close
    Form is in New Album mode -> User presses Cancel -> Close [No validation done]

     Notice> This is not your conventional Textboxes bound to fields. This is a DataForm and as you can see, I had to add a new xmlns at the top to use it.
     Okay, XAML files ready. Let's add the code behind files so that it really works.
     AlbumsEditor.xaml.cs

using System.Windows;
using System.Windows.Controls;
using ChinookSample.Web;

namespace ChinookSample.Views
{
    public partial class AlbumsEditor : ChildWindow
    {
        public Album CurrentAlbum { get; set; }

        public bool IsEditing { get; set; }

        public AlbumsEditor()
        {
            InitializeComponent();
        }

        public void SetupForm(bool IsEditing)
        {
            if (IsEditing)
            {
                this.Title = "Edit Album";
            }
            else
            {
                this.Title = "Create New Album";
                CurrentAlbum = new Album();
            }
            this.IsEditing = IsEditing;
            AlbumDetails.CurrentItem = CurrentAlbum;
            AlbumDetails.CommandButtonsVisibility = DataFormCommandButtonsVisibility.None;
            AlbumDetails.BeginEdit();
        }

        private void OKButton_Click(object sender, RoutedEventArgs e)
        {
            AlbumDetails.CommitEdit();
            this.DialogResult = true;
        }

        private void CancelButton_Click(object sender, RoutedEventArgs e)
        {
            CurrentAlbum = null;
            AlbumDetails.CancelEdit();
            this.DialogResult = false;
        }

        private void ChildWindow_Closing(object sender, System.ComponentModel.CancelEventArgs e)
        {
            //If the form is in Add new mode...
            if (!IsEditing)
            {
                if (CurrentAlbum == null)
                {
                    e.Cancel = false;
                    return;
                }
            }

            //If the form doesn't validate and is in Editing mode...
            if (!AlbumDetails.ValidateItem())
            {
                e.Cancel = true;
            }
        }
    }
}

     Notice > SetupForm method takes care of setting the title bar and creates an empty record for addition.
     Ok and Cancel buttons take care of keeping the CurrentAlbum property in a good shape.
     ChildWindow_Closing takes care of ensuring that the ChildWindow closes on OK button ONLY when everything is valid. If anyone clicks OK button and the data is not valid you will get an error message as follows. Notice that this error message has come up automatically! Also notice how the Title text becomes Red in color below. All this is done by our friend RIA Smile

image


     Albums.xaml.cs

using System.Windows;
using System.Windows.Controls;
using ChinookSample.Web;
using ChinookSample.Web.Services;

namespace ChinookSample.Views
{
    public partial class Albums : Page
    {
        public Albums()
        {
            InitializeComponent();
        }

        private void btnAdd_Click(object sender, RoutedEventArgs e)
        {
            AlbumsEditor ae = new AlbumsEditor();
            ae.SetupForm(false);
            ae.Closed += new System.EventHandler(ae_Closed);
            ae.Show();
        }

        private void btnEdit_Click(object sender, RoutedEventArgs e)
        {
            AlbumsEditor ae = new AlbumsEditor();
            ae.CurrentAlbum = (Album)gridAlbums.SelectedItem;
            ae.SetupForm(true);
            ae.Closed += new System.EventHandler(ae_Closed);
            ae.Show();
        }

        void ae_Closed(object sender, System.EventArgs e)
        {
            AlbumsEditor ae = (AlbumsEditor)sender;
            if (ae.IsEditing)
            {
                if (ae.CurrentAlbum != null)
                {
                    dsAlbumAndArtist context = (dsAlbumAndArtist)source.DomainContext;
                    context.SubmitChanges();
                    lblMessage.Text = "Updated successfully!";
                } 
            }
            else
            {
                if (ae.CurrentAlbum != null)
                {
                    dsAlbumAndArtist context = (dsAlbumAndArtist)source.DomainContext;
                    context.Albums.Add(ae.CurrentAlbum);
                    context.SubmitChanges();
                    lblMessage.Text = "Created successfully!";
                }
            }
        }

        private void btnDelete_Click(object sender, RoutedEventArgs e)
        {
            dsAlbumAndArtist context = (dsAlbumAndArtist)source.DomainContext;
            int count = gridAlbums.SelectedItems.Count;
            if (count > 0)
            {
                while (gridAlbums.SelectedItems.Count > 0)
                {
                    context.Albums.Remove((Album)gridAlbums.SelectedItems[0]);
                };
                context.SubmitChanges();
                lblMessage.Text = "Deleted successfully!";
            }
            else
            {
                lblMessage.Text = "Please select a row to delete!";
            }
        }
    }
}

     Notice> All the real work of saving the new record, or updating the existing record is taken care of here.
     Notice> Deletion of records are taken care of here! Carefully notice the While loop Smile I have used it simply because I wanted to give functionality of deleting multiple records at once. Using SelectedItems in a foreach loop will cause problems since every time you delete a record you would inherently change the SelectedItems collection of gridAlbums which won't be allowed. With While loop the necessarily recalculates the SelectedItems collection and deletes the first one in each iteration.
     One last change in your dsAlbumAndArtist.cs. This is just to ensure that insertion happens and it doesn't throw any validation errors because of missing fields. In this case, ArtistId was a foreign key, and currently I am just hard coding it to 1. Later you can add interface in the Child Window to get the ID and push it in. Once you update the following code, recompile your solution.

public void InsertAlbum(Album album)
{
    if ((album.EntityState != EntityState.Detached))
    {
        this.ObjectContext.ObjectStateManager.ChangeObjectState(album, EntityState.Added);
    }
    else
    {
        //We are hard coding the Artist ID for now. In Chinook database ID 1 is set to AC/DC
        album.ArtistId = 1;
        this.ObjectContext.Albums.AddObject(album);
    }
}


     So, that's pretty much it. Hard work done, and now you can browse to your web page.
     By now, you have seen CRUD in full action and I hope this series is helping you out.
     In the next post, you will find out more about validation. Stay tuned.

Until next time, Wave
Rahul


Quote of the day:
The men who really believe in themselves are all in lunatic asylums. - G. K. Chesterton

Tags: ,

Silverlight | Visual Studio 2010

blog comments powered by Disqus

Disclaimer

The opinions expressed herein are our own personal opinions and do not represent our employer's view in any way.
© Copyright 2014, Rahul Soni