Tuesday, September 27, 2011

Silverlight 5 P-Invoke: Using Kinect SDK from Silverlight

Away from all the discussions about whether Silverlight is dead or not, The Silverlight 5 RC contains the previously announced P-Invoke feature which enables you to call Win32 style APIs from a trusted Silverlight application.

There are some attempts to use Kinect from Silverlight (here) but this was before MS released the official SDK. we will use Silverlight 5 P-Invoke feature to call the Kinect SDK APIs.

P-Invoke in Silverlight works just like P-Invoke on the desktop. you use the DllImport attribute to import the APIs and you declare in your code the dependent types (Enums, structs, etc.)

  1. [DllImport("MSRKINECTNUI.DLL")]
  2.       private static extern HRESULT NuiInitialize(uint dwFlags);

I tried to keep the classes and methods in library identical to the ones in the official SDK. I implemented only a couple of the available APIs (with the help of the Coding4Fun Kinect Toolkit and Reflector).

To test the library we will create a simple application:

- Create a new Silverlight 5 application

- Add a reference to the KinectSilverlightLibrary.

- Change the properties of the project: enable running out of browser.

Enable running out of browser

- Change the Out-of-Browser settings to require elevated trust

Require elevated trust

- In the App.xaml.cs file, define a new property of type Runtime, this property represents the Kinect runtime, in the Application_Startup event handler initialize the runtime and in the Application_Exit event handler shut down the runtime

  1. Runtime kinectRuntime = new Runtime();
  2. public Runtime KinectRuntime
  3. {
  4.     get
  5.     { return kinectRuntime; }
  6. }
  7. private void Application_Startup(object sender, StartupEventArgs e)
  8. {
  9.     this.RootVisual = new MainPage();
  10.     kinectRuntime.Initialize(RuntimeOptions.UseColor | RuntimeOptions.UseDepth );
  11. }
  12. private void Application_Exit(object sender, EventArgs e)
  13. {
  14.     kinectRuntime.Uninitialize();
  15. }

- In the MainPage.xaml file add two image controls that we will use to display the video and depth streams coming from Kinect

  1. <Grid x:Name="LayoutRoot" Background="White" Loaded="LayoutRoot_Loaded">
  2.         <Grid.ColumnDefinitions>
  3.             <ColumnDefinition/>
  4.             <ColumnDefinition/>
  5.         </Grid.ColumnDefinitions>
  6.         <Image Name="image1" Stretch="Fill" Grid.Column="0" />
  7.         <Image Name="image2" Stretch="Fill" Grid.Column="1" />
  8.     </Grid>

- In the MainPage.xaml.cs add the following lines of code

  1. private void LayoutRoot_Loaded(object sender, RoutedEventArgs e)
  2.         {
  3.            Runtime runtime = (App.Current as App).KinectRuntime;
  4.            runtime.VideoStream.OpenStream(ImageType.Color, ImageResolution.Resolution640x480);
  5.           runtime.DepthStream.OpenStream(ImageType.Depth, ImageResolution.Resolution320x240);
  6.          runtime.VideoFrameReady += new EventHandler<ImageFrameReadyEventArgs>(runtime_VideoFrameReady);
  7.            runtime.DepthFrameReady += new EventHandler<ImageFrameReadyEventArgs>(runtime_DepthFrameReady);
  8.         }
  9.     
  10.         void runtime_VideoFrameReady(object sender, ImageFrameReadyEventArgs e)
  11.         {
  12.             Dispatcher.BeginInvoke(() => {
  13.                 image1.Source = e.ImageFrame.ToBitmapSource();
  14.             });
  15.         }
  16.         void runtime_DepthFrameReady(object sender, ImageFrameReadyEventArgs e)
  17.         {
  18.             Dispatcher.BeginInvoke(() =>
  19.             {
  20.                 image2.Source = e.ImageFrame.ToBitmapSource();
  21.             });
  22.  
  23.         }

When the page is loaded we open the video and depth streams and subscribe to the VideoFrameReady and DepthFrameReady events, in the event handler we retrieve the ImageFrame and convert it to a bitmap (I used the WritableBitmapEx library)

Here’s the application running

Kinect Video and Depth Streams 

You are free to continue adding the rest of the APIs to this library.

You can download the source code from here:

Tuesday, July 12, 2011

Surface 2.0 SDK Released

The long awaited Surface 2.0 SDK has been just released. to get started go to the new Surface Developer Center and download the SDK.

Once downloaded and installed, start VS2010, you will find the Surface 2.0 project templates available

Surface 2.0 Project Templates

Let’s create a sample application, I will create a simple twitter client (hopefully before scottgu creates it) let’s name it TwittSurf

Once we created the project you will have the same project structure as you have in the Surface 1.0 version, let’s create our UI

<s:SurfaceWindow x:Class="TwittSurf.SurfaceWindow1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:s="http://schemas.microsoft.com/surface/2008"
    Title="TwittSurf"
    Loaded="SurfaceWindow_Loaded" >
    <Grid>
        <s:ScatterView>
               <s:ScatterViewItem Width="400" Height="500" CanMove="True" CanScale="True" CanRotate="True" >
                  <s:SurfaceListBox x:Name="tweetsList" >
                    <s:SurfaceListBox.ItemTemplate>
                        <DataTemplate>
                            <StackPanel Margin="20" >
                                <Image HorizontalAlignment="Left" Source="{Binding ProfileImageLocation}"/>
                                <TextBlock Text="{Binding FromUserScreenName}"/>
                                <TextBlock Text="{Binding Text}"/>
                            </StackPanel>
                        </DataTemplate>
                    </s:SurfaceListBox.ItemTemplate>
                </s:SurfaceListBox>

            </s:ScatterViewItem>
        </s:ScatterView>
    </Grid>
</s:SurfaceWindow>

We will add a ScatterView control, inside it we will add a SurfaceListBox and configure its ItemDataTemplate.

In the Loaded event of the Window we will write our code, I will use the Twitterizer .net Library (download the lite version) to save some time, the code below will submit a query to twitter and display the result in the list box

private void SurfaceWindow_Loaded(object sender, RoutedEventArgs e)
        {
            TwitterResponse<TwitterSearchResultCollection> searchResult = TwitterSearch.Search("mosallem", null);
            tweetsList.ItemsSource = searchResult.ResponseObject;
        }

Before you run the application Open the Input Simulator (Start Menu-> Programs-> Microsoft Surface 2.0 SDK-> Tools) this tool is used to simulate the different types of inputs (finger, blob, tag).

Run the application, the list will appear with the search results, select the finger input from the input simulator, press and hold the left mouse button to simulate a finger contact on the Surface.

You can drag/rotate the list box around, to scale the list you need two fingers, you can do this by pressing/holding the left mouse button on one corner of the list, then pressing the right mouse button to keep the finger contact in this location, then you can add another finger contact on another corner by clicking the left mouse button and when you move the second finger the list will scale.

I believe you can also simulate more than one contact by connecting additional mice, but unfortunately that didn’t work for me.

bb

You can download the code from here

I’m very excited to have the SDK, Definitely I’m be spending more time playing with it. 

Monday, July 11, 2011

Portable Library Tools

With the introduction of Windows Phone 7.0 a new opportunity emerged for Silverlight developers, this new platform uses Silverlight (a version of Silverlight 3.0) as the main framework to build applications on the phone. When building applications for Silverlight and WP7 you will find that you can reuse some of the code across the two platforms, Linked source files were used to share code between WP7 and Silverlight projects along with the use of compilation directives.

Microsoft introduced a new way to create code that targets multiple platforms, it’s a Visual Studio Add-In called Portable Library Tools (Available here)

Portable Library Tools is a new Visual Studio add-in from Microsoft that enables you to create C# and Visual Basic libraries that run on a variety of .NET-based platforms without recompilation.

In this post we are going to build a sample application for WP7 and Silverlight that uses the Portable Class Library Tools.

We will create a CustomersExplorer application that will allow the user to display and edit the customers from the AdventureWorks database, Let’s start by creating new solution we will add a web application project called CustomersExplorer.Web, in this project we will have an Entity Framework model that exposes the customers in the AdventureWorks database (the EF model will use ADO.NET self tracking entities), we will expose the model through a WCF service that uses webHttpBinding. the service code is shown below

[ServiceContract]
    public interface ICustomerService
    {
           [OperationContract]
          List<Customer> GetCustomers();
           [OperationContract]
          OperationStatus SaveCustomer(Customer customer);

           [OperationContract]
          Customer GetCustomer(int custID);
    }
    [AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]
    public class CustomerService : ICustomerService
    {
        public List<Customer> GetCustomers()
        {
            return new CustomerRepository().GetCustomers();
        }

        public OperationStatus SaveCustomer(Customer customer)
        {
            return new CustomerRepository().SaveCustomer(customer);
        }

        public Customer GetCustomer(int custID)
        {
            return new CustomerRepository().GetCustomer(custID);
        }
    }

We will add three more projects to the solution:

1. CustomersExplorer.Core: a Portable Class Library Project that will contain the shared code to be used in the Silverlight and windows phone apps

Portable Class Library Project

2. CustomersExplorer.Silverlight: the Silverlight application

3. CustomersExplorer.WP: the windows phone 7 application

The layout of the solution should be like the following

Solution Layout

The CustomersExplorer.Core project will be used by the Silverlight and the Windows Phone applications, so we will add a reference to this project to the Silverlight and Windows Phone Projects.

Next we’ll change the settings of the CustomersExplorer.Core

project. By default when you create a new Portable Class Library project its settings is set to target the .Net 4.0, Silverlight 4.0 and Windows Phone 7.0 frameworks, we will change this to target Silverlight and Windows Phone. Go to the properties of the project and click Change button in the target frameworks section

Portable Class Library Project Target Frameworks

In the dialog, choose Windows Phone 7.0 and Silverlight 4.0

Target Frameworks

We need to add a reference to the System.Windows assembly, this assembly contains the ObservableCollection type that we need in the Silverlight and WP applications, the dll is located in this path “C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETPortable\v4.0\” 

Next we need to add a service reference for the CustomersService, for some unknown reason If you tried to add a reference to the Customers service in the Portal Class Library project, the generated proxy class will not use ObservableCollection as the collection type, so we will use the SLsvcutil tool to create the proxy class, the slsvcutil tool is located in the folder C:\Program Files (x86)\Microsoft SDKs\Silverlight\v4.0\Tools”

Run the tool with the following command line

  1. slsvcutil  /edb /namespace:"*,CustomerExplorer.Core.CustomersService" /ct:System.Collections.ObjectModel.ObservableCollection`1 /r:"C:\PROGRAM FILES (x86)\Reference Assemblies\Microsoft\Framework\Silverlight\v4.0\System.Windows.dll" /directory:C:\Users\Mohamed\Desktop\generated\

Take the generated file CustomerService.cs and add it to the CustomersExplorer.Core project, the other generated file (ServiceReferences.ClientConfig) must be copied to the 

CustomersExplorer.Silverlight and CustomersExplorer.WP projects.

In the CustomersExplorer.Core project we will create the CustomersViewModel, which exposes a property Customers of type ObservableCollection<Customer> , and a property CurrentCustomer of type Customer, two commands are exposed UpdateCustomerCommand and DeleteCustomerCommand that forward these actions to the Customers WCF service.

#region "Commands"
        public RelayCommand UpdateCustomerCommand
        {
            get;
            private set;
        }

        public RelayCommand DeleteCustomerCommand
        {
            get;
            private set;
        }
        #endregion
        
        #region "Properties"
        public const string CustomersPropertyName = "Customers";
        private ObservableCollection<Customer> _customers = null;
        public ObservableCollection<Customer> Customers
        {
            get
            {
                return _customers;
            }
            set
            {
                if (_customers == value)
                {
                    return;
                }
                 _customers = value;
                RaisePropertyChanged(CustomersPropertyName);
            }
        }


        public const string CurrentCustomerPropertyName = "CurrentCustomer";
        private Customer _currentCustomer = null;
        public Customer CurrentCustomer
        {
            get
            {
                return _currentCustomer;
            }

            set
            {
                if (_currentCustomer == value)
                {
                    _statusMessage = "";
                    return;
                }
                _currentCustomer = value;
                RaisePropertyChanged(CurrentCustomerPropertyName);
                UpdateCustomerCommand.RaiseCanExecuteChanged();
                DeleteCustomerCommand.RaiseCanExecuteChanged();
            }
        }


        public const string StatusMessagePropertyName = "StatusMessage";
        private string _statusMessage = "";
        public string StatusMessage
        {
            get
            {
                return _statusMessage;
            }

            set
            {
                if (_statusMessage == value)
                {
                    return;
                }
                _statusMessage = value;
                RaisePropertyChanged(StatusMessagePropertyName);
            }
        }
        #endregion

The Silverlight application is very simple, it has a single page with no code behind, the page creates an instance of the view model

<UserControl.Resources>
        <ViewModels:CustomersViewModel x:Key="ViewModel"/>
    </UserControl.Resources>

The main grid DataContext property is bound to the View model

<Grid x:Name="LayoutRoot" Background="White"
          DataContext="{Binding Source={StaticResource ViewModel}}" >

A combo box is used to display the customers

<ComboBox Height="23" HorizontalAlignment="Left" Margin="158,88,0,0" Name="CustomersComboBox"
                  VerticalAlignment="Top" Width="173" DisplayMemberPath="FullName"
                  ItemsSource="{Binding Path=Customers}"
                  SelectedItem="{Binding Path=CurrentCustomer, Mode=TwoWay}" />

and a bunch of Text Boxes are used to display the details of the view model CurrentCustomer property

<TextBox Height="23" HorizontalAlignment="Left" Margin="158,225,0,0" VerticalAlignment="Top" Width="219" Text="{Binding CurrentCustomer.FirstName, Mode=TwoWay}" />
        <TextBox Height="23" HorizontalAlignment="Left" Margin="158,270,0,0" VerticalAlignment="Top" Width="219" Text="{Binding CurrentCustomer.LastName, Mode=TwoWay}" />
        <TextBox Height="23" HorizontalAlignment="Left" Margin="158,316,0,0" VerticalAlignment="Top" Width="219" Text="{Binding CurrentCustomer.CompanyName, Mode=TwoWay}"/>
        <TextBox Height="23" HorizontalAlignment="Left" Margin="158,366,0,0" VerticalAlignment="Top" Width="219" Text="{Binding CurrentCustomer.EmailAddress, Mode=TwoWay}" />
        <TextBox Height="23" HorizontalAlignment="Left" Margin="158,416,0,0" VerticalAlignment="Top" Width="219" Text="{Binding CurrentCustomer.EmailAddress, Mode=TwoWay}" />

Two buttons “Delete” and “Updated” are bound to the view model DeleteCusotmer and UpdateCustomer commands

<Button Content="Update" Height="23" HorizontalAlignment="Left" Margin="51,480,0,0" Name="UpdateButton" VerticalAlignment="Top" Width="75"
                Command="{Binding Path=UpdateCustomerCommand}" />
        <Button Content="Delete" Height="23" HorizontalAlignment="Left" Margin="158,480,0,0" Name="DeleteButton" VerticalAlignment="Top" Width="75"
                Command="{Binding Path=DeleteCustomerCommand}" />

In the Windows Phone 7 project the View Model is defined in the application resources, cause we will have two pages that use the same view model. The first page has a list box that is bound to the view model Customers property

<ListBox Name="customersList" ItemsSource="{Binding Customers}"
                      SelectedItem="{Binding Path=CurrentCustomer, Mode=TwoWay}"
                     SelectionChanged="customersList_SelectionChanged" >
                <ListBox.ItemTemplate>
                    <DataTemplate >
                        <TextBlock Text="{Binding FullName}" />
                    </DataTemplate>
                </ListBox.ItemTemplate>
            </ListBox>

The second page displays the details of the CurrentCustomer

<StackPanel x:Name="ContentPanel" >
                <TextBlock Text="CustomerID:"/>
                <TextBlock Name="CustomerIDTextBlock" Text="{Binding CurrentCustomer.CustomerID}" />
                <TextBlock Text="First Name:"/>
                <TextBox Text="{Binding CurrentCustomer.FirstName, Mode=TwoWay}" />
                <TextBlock Text="Last Name:"/>
                <TextBox Text="{Binding CurrentCustomer.LastName, Mode=TwoWay}" />
                <TextBlock Text="Company Name:"/>
                <TextBox Text="{Binding CurrentCustomer.CompanyName, Mode=TwoWay}"/>
                <TextBlock Text="Email:"/>
                <TextBox Text="{Binding CurrentCustomer.EmailAddress, Mode=TwoWay}" />
                <TextBlock Text="Phone:"/>
                <TextBox Text="{Binding CurrentCustomer.Phone, Mode=TwoWay}" />
                <StackPanel Orientation="Horizontal">
                      <Button Content="Update" Name="UpdateButton"
                cmd:ButtonBaseExtensions.Command="{Binding UpdateCustomerCommand}"/>
                    <Button Content="Delete" Name="DeleteButton"
                cmd:ButtonBaseExtensions.Command="{Binding DeleteCustomerCommand}" />
                </StackPanel>

Here are some screen shots of the running applications

CustomersExplorer.Silverlight 

Customers List Customer Details

You can download the source code from here

Monday, July 04, 2011

Tuesday, June 28, 2011

MVVM

I will be delivering a session on MVVM Thursday June 30, in the DevLifeStyle community monthly meeting.

To register and view the detailed agenda, click here

Sunday, April 24, 2011

DevLifeStyle May 2011 Community Event

We are back again, join us on May 5,2011. I will be delivering a session on the new features in Silverlight5.

To register and view the detailed agenda, click here

Tuesday, March 01, 2011

Silverlight 4.0 with SharePoint 2010

On Last Friday 25th I delivered an online session part of the Second Riyadh Online Community Summit, here is a recording of the session Download

and here is the source code of the demos Download

Tuesday, February 15, 2011

Silverlight 4.0 Tutorial (12 of N): Collecting Attendees Feedback using Windows Phone 7

You can find a list of the previous tutorial posts here

Continuing our RegistrationBooth Application…

It’s very important to collect the attendees feedback to help us improve the future sessions/events we might organize, we can go ahead and implement this functionality in the RegistrationBooth application. but in this post we will take a different approach, we will implement a Windows Phone 7 application that attendees can use on their phones to submit their feedback. Luckily for us Windows Phone 7 uses Silverlight as its development platform (along with XNA), the Silverlight version on Windows Phone is not the same one as the desktop version, think of this as a compact version that fits the phone capabilities.

Before we start creating the phone application, first let’s think how we will bring the data to the phone, in the case of the Silverlight application we used WCF RIA services (DomainService) which provided rich client experience for the Silverlight application, Unfortunately this type of rich client experience is not available “yet” for windows phone applications.

When we were creating the DomainService we had the option to expose OData end point.

Exposing OData end point for the Domain Service

If life was peachy we would just add a new OData endpoint to the Domain service we have and consume this service from Windows Phone, unfortunately the OData endpoint exposed from the RIA Service lacks important feature (query options) so we will not be able to make use of an OData endpoint over the RIA service, the solution is to create a new WCF data service, we will call it RegistrationBoothDataService

Add a new WCF Data Service

Change the Data Service to use our ADO.NET entity model and in the InitializeService method enable access to all the entities, the service should look like the following

public class RegistrationBoothDataService :
    DataService<RegistrationBoothDBEntities>
{
    public static void InitializeService
        (DataServiceConfiguration config)
    {
        config.SetEntitySetAccessRule("*",
            EntitySetRights.All);
        config.SetServiceOperationAccessRule("*",
            ServiceOperationRights.All);
        config.DataServiceBehavior.MaxProtocolVersion =
            DataServiceProtocolVersion.V2;
    }
}

Now build the project and give it a try and navigate to the following URL

http://localhost:57263/RegistrationBoothDataService.svc/

You will see AtomPub feed containing the names of all the entities exposed by the DomainService, if you want to see the records of a certain entity just append entityname to the above URL, for example to see all the attendees go to the following URL

http://localhost:57263/RegistrationBoothDataService.svc/Attendees

That’s what we need to do to configure the service on the server side.

Now moving to the client (Phone) we need to add a new project (for the Windows Phone 7 development tools go to http://developer.windowsphone.com) We will choose the Windows Phone Panorama Application project type, later we will see why we choose that.

Creating a Windows Phone Panorama Application

To consume the OData service from the windows phone project we need to download the OData client library for Windows Phone 7 (http://odata.codeplex.com), when you download this library you will have the assembly System.Data.Services.Client.dll, Add Reference to this dll in the windows phone project, and you will have a command line tool DataSvcUtil.exe that we will use to generate a proxy class for the OData service, so we will run the command line tool passing the service URL

DataSvcUtil /uri:http://localhost:57263/RegistrationBoothDataService.svc/ /out:.\RegistrationBoothDataModel.cs /Version:2.0 /DataServiceCollection

Once we have this class, we will add it to the windows phone project.

So why we chose the panoramic application? or what is a panoramic application anyway? according to MSDN “panoramic applications offer a unique way to view controls, data, and services by using a long horizontal canvas that extends beyond the confines of the screen”

Panorama Application

So how we will implement our application UI as a panorama application? we will  display the time slots horizontally and the sessions vertically, so the user will see the time slot in the header and below it a list of sessions, and by scrolling horizontally the user will move to the subsequent time slots and below each one the list of sessions in that time slot.

If you inspect the panorama application project we created, you will find that VS created a sample project with organized structure, where you have view models located in the ViewModels folder and the view models contain some sample data, and the XAML pages (views) are bound to these view models, we will follow the same structure in our application.

To communicate with the data service we need to define a DataServiceContext we will define this in the Application class

//App.xaml.cs
static readonly Uri ServiceUri = new Uri("http://localhost:57263/RegistrationBoothDataService.svc/");
public static int AttendeeID { get; set; }
       
static RegistrationBoothDataService context;
public static RegistrationBoothDataService DataServiceContext
{
    get
    {
        if (context == null)
            context = new RegistrationBoothDataService(ServiceUri);
        return context;
    }
}

The first page will be the Login.xaml, for the sake of simplicity we will ask the user to enter the attendee Id only

Login Page

The code behind is pretty straightforward, in the page constructor we initialize a DataServiceCollection of Attendees

static readonly string attendeesUri = "/Attendees?$filter=Id eq {0}";
DataServiceCollection<Attendee> attendeesEntities;
public Login()
{
    InitializeComponent();
    attendeesEntities = new DataServiceCollection<Attendee>(App.DataServiceContext);
    attendeesEntities.LoadCompleted += new EventHandler<LoadCompletedEventArgs>(attendeesEntities_LoadCompleted);
    
}

When the user clicks the login button we populate this collection by a query that filters the attendees based on the supplied attendee id "/Attendees?$filter=Id eq {0}"

private void LoginButton_Click(object sender, RoutedEventArgs e)
{
    attendeesEntities.LoadAsync(new Uri(string.Format(attendeesUri,AttendeeID.Text), UriKind.Relative));
}

Once the query is loaded we check to see if there’s any attendee returned in the result, if there is an attendee we save the AttendeeID and navigate to the next page

void attendeesEntities_LoadCompleted(object sender, LoadCompletedEventArgs e)
{
    if (attendeesEntities.Count != 1)
        MessageBox.Show("Invalid id");
    else
    {
        App.AttendeeID = attendeesEntities[0].Id;
        this.NavigationService.Navigate(
    new Uri("/MainPage.xaml", UriKind.Relative));
    }
                
}

The next page MainPage.xaml will display the sessions in a panorama view as we described earlier. before we build the UI let’s first create the view model, the first view model is the TimeSlotViewModel which contains two properties the TimeSlotHeader and a collection of Sessions, the view model also implements the INotifyPropertyChanged interface

public class TimeSlotViewModel : INotifyPropertyChanged
{
    public TimeSlotViewModel(TimeSlot timeSlotEntity)
    {
        this.TimeSlotHeader = String.Format("{0:HH:mm tt} - {1:HH:mm tt}", timeSlotEntity.StartTime, timeSlotEntity.EndTime);
        this.Sessions = timeSlotEntity.Sessions;
    }

    Collection<Session> sessions;
    public Collection<Session> Sessions
    {
        get { return this.sessions; }
        private set
        {
            this.sessions = value;
            this.NotifyPropertyChanged("Sessions");
        }
    }

    string timeSlotHeader;
    public string TimeSlotHeader
    {
        get
        { return this.timeSlotHeader; }
        private set
        {
            this.timeSlotHeader = value;
            this.NotifyPropertyChanged("TimeSlotHeader");
        }
    }

    public event PropertyChangedEventHandler PropertyChanged;
    private void NotifyPropertyChanged(String propertyName)
    {
        PropertyChangedEventHandler handler = PropertyChanged;
        if (null != handler)
        {
            handler(this, new PropertyChangedEventArgs(propertyName));
        }
    }
}

Another view model we have is the MainViewModel, this is the view model that the MainPage.xaml will bind to, it has an ObservableCollection of TimeSlotViewModel objects, we populate this collection by performing a query against the DataService using the Uri “/TimeSlots?$expand=Sessions/Speakers” this query expands the TimeSlot entity to retrieve the associated Sessions and Speakers entities as well

public class MainViewModel : INotifyPropertyChanged
{
    static readonly Uri timeSlotsUri = new Uri("/TimeSlots?$expand=Sessions/Speakers", UriKind.Relative);
        
    DataServiceCollection<TimeSlot> timeSlotsEntities;
    ObservableCollection<TimeSlotViewModel> timeSlots;

    public MainViewModel()
    {
        timeSlotsEntities = new DataServiceCollection<TimeSlot>(App.DataServiceContext);
        timeSlotsEntities.LoadCompleted += new EventHandler<LoadCompletedEventArgs>(timeSlotsEntities_LoadCompleted);
        this.TimeSlots = new ObservableCollection<TimeSlotViewModel>();
    }
    public ObservableCollection<TimeSlotViewModel> TimeSlots
    {
        get { return this.timeSlots; }
        private set
        {
            this.timeSlots = value;
            this.NotifyPropertyChanged("TimeSlots");
        }
    }
    public bool IsDataLoaded
    {
        get; private set;
    }
    public void LoadData()
    {
        timeSlotsEntities.LoadAsync(timeSlotsUri);
        this.IsDataLoaded = true;
    }
    void timeSlotsEntities_LoadCompleted(object sender, LoadCompletedEventArgs e)
    {
        foreach (var timeSlot in timeSlotsEntities)
        {
            var tsViewModel = new TimeSlotViewModel(timeSlot);
            this.TimeSlots.Add(tsViewModel);
        }
    }

The MainPage.xaml contains the Panorama control whose ItemsSource property is bound to the TimeSlots property of the MainViewModel, each panorama Item contains a ListBox that displays the Sessions available in that TimeSlot

<Grid x:Name="LayoutRoot" Background="Transparent">
        <Grid.Resources>
            <local:DisplayNameConverter x:Key="displayNameConverter" />
        </Grid.Resources>
       <controls:Panorama Title="Select the Session"
                          ItemsSource="{Binding TimeSlots}">
            <controls:Panorama.Background>
                <ImageBrush ImageSource="PanoramaBackground.png"/>
            </controls:Panorama.Background>
            
            <controls:Panorama.ItemTemplate>
                <DataTemplate>
                    <controls:PanoramaItem Header="{Binding TimeSlotHeader}" >
                        <controls:PanoramaItem.HeaderTemplate>
                            <DataTemplate>
                                <TextBlock Text="{Binding}" Style="{StaticResource PhoneTextLargeStyle}"/>
                            </DataTemplate>
                        </controls:PanoramaItem.HeaderTemplate>
                        <ListBox Margin="0,0,-12,0" ItemsSource="{Binding Sessions}" SelectionChanged="OnSelectionChanged">
                    <ListBox.ItemTemplate>
                        <DataTemplate>
                           
                            <StackPanel Margin="0,0,0,17" Width="432">
                                <TextBlock Text="{Binding Title}" TextWrapping="Wrap"
                                            Style="{StaticResource PhoneTextLargeStyle}"/>
                                <ListBox ItemsSource="{Binding Speakers}">            
                                    <ListBox.ItemTemplate>
                                        <DataTemplate>
                                            <TextBlock Text="{Binding ., Converter={StaticResource displayNameConverter}}" TextWrapping="Wrap"
                                                            Style="{StaticResource PhoneTextSubtleStyle}" />
                                        </DataTemplate>
                                    </ListBox.ItemTemplate>
                                </ListBox>
                            </StackPanel>
                        </DataTemplate>
                    </ListBox.ItemTemplate>
                </ListBox>
                    </controls:PanoramaItem>
                </DataTemplate>
            </controls:Panorama.ItemTemplate>
        </controls:Panorama>
    </Grid>

The code behind the MainPage.xaml will set the DataContext of the page to an instance of the MainViewModel class, we will load the View Model data in the Load Event of the page, on the SelectionChanged event of the sessions ListBox we will navigate to the session evaluation page SessionEval.xaml

public partial class MainPage : PhoneApplicationPage
{
    MainViewModel ViewModel = new MainViewModel();
    public MainPage()
    {
        InitializeComponent();
        DataContext = ViewModel;
        this.Loaded += new RoutedEventHandler(MainPage_Loaded);
    }
    private void MainPage_Loaded(object sender, RoutedEventArgs e)
    {
        if (!ViewModel.IsDataLoaded)
        {
            ViewModel.LoadData();
        }
    }
    void OnSelectionChanged(object sender, SelectionChangedEventArgs e)
    {
        var selector = (Selector)sender;
        if (selector.SelectedIndex == -1)
            return;
        Session session = selector.SelectedItem as Session;
        this.NavigationService.Navigate(
            new Uri("/SessionEval.xaml?sessionId=" + session.Id, UriKind.Relative));
        selector.SelectedIndex = -1;
    }
}

The view model for the SessionEval.xaml page is called SessionEvalViewModel this view model has a punch of propertyies we have a DataServiceCollection of EvalCriteria entities which contains the different evaluation criteria, and we have another DataServiceCollection of SessionEvaluation entities which contains the evaluation value for each criteria per session per attendee. there is another ObservableCollection of EvalCriteriaInfo objects, this collection is the one that is exposed to the UI.

static readonly string evalCriteriaUrl = "/EvalCriterias";
static readonly string sessionEvalUrl = "/SessionEvaluations?$filter=SessionId eq {0} and AttendeeId eq {1}";//, UriKind.Relative);
DataServiceCollection<EvalCriteria> evalCriteriaEntities;
DataServiceCollection<SessionEvaluation> sessionEvalEntities;
bool bEvalsLoaded = false;
int _sessionId;
bool bCriteriaLoaded = false;
ObservableCollection<EvalCriteriaInfo> _evaluations;

public SessionEvalViewModel()
{            
        _evaluations = new ObservableCollection<EvalCriteriaInfo>();
}

#region Properties
public ObservableCollection<EvalCriteriaInfo> Evaluations
{
    get { return _evaluations; }
    set
    {
        _evaluations = value;
        NotifyPropertyChanged("Evaluations");
    }
}
public bool IsDataLoaded
{
    get;
    private set;
}
#endregion

the EvalCriteriaInfo class is a helper class that implements the INotifyPropertyChanged interface

public class EvalCriteriaInfo : INotifyPropertyChanged
{
    string _evalCriteriaTitle;
    int _evalCriteriaId;
    int _evaluationValue;
    public string EvalCriteriaTitle {
        get { return _evalCriteriaTitle; }
        set
        {
            _evalCriteriaTitle = value;
            NotifyPropertyChanged("EvalCriteriaTitle");
        }
    }
    public int EvaluationValue
    {
        get { return _evaluationValue; }
        set
        {
            _evaluationValue = value;
            NotifyPropertyChanged("EvaluationValue");
        }
    }
    public int EvalCriteriaId
    {
        get { return _evalCriteriaId; }
        set
        {
            _evalCriteriaId = value;
            NotifyPropertyChanged("EvalCriteriaId");
        }
    }
      
    public SessionEvaluation SessionEvaluationEntity;

    #region INotifyPropertyChanged Implementation
    /**/
    #endregion
}

The LoadData method of the view model, loads the EvalCriteria and SessionEvaluation collections and save them in the Application object

public void LoadData(int sessionId)
{
    _sessionId = sessionId;
    if (App.EvalCriteriaEntities == null)
    {
        evalCriteriaEntities = new DataServiceCollection<EvalCriteria>(App.DataServiceContext);
        evalCriteriaEntities.LoadCompleted += new EventHandler<LoadCompletedEventArgs>(evalCriteriaEntities_LoadCompleted);
        evalCriteriaEntities.LoadAsync(new Uri(evalCriteriaUrl, UriKind.Relative));
    }
    else
    {
        this.evalCriteriaEntities = App.EvalCriteriaEntities;
        bCriteriaLoaded = true;
    }

    if (!App.SessionEvaluationEntities.ContainsKey(this._sessionId))
    {
        sessionEvalEntities = new DataServiceCollection<SessionEvaluation>(App.DataServiceContext);
        sessionEvalEntities.LoadCompleted += new EventHandler<LoadCompletedEventArgs>(sessionEvalEntities_LoadCompleted);
        sessionEvalEntities.LoadAsync(new Uri(string.Format(sessionEvalUrl, sessionId, App.AttendeeID), UriKind.Relative));
    }
    else
    {
        this.sessionEvalEntities = App.SessionEvaluationEntities[this._sessionId];
        bEvalsLoaded = true;
    }
    this.IsDataLoaded = true;
    populate();
}
void evalCriteriaEntities_LoadCompleted(object sender, LoadCompletedEventArgs e)
{
    bCriteriaLoaded = true;
    App.EvalCriteriaEntities = evalCriteriaEntities;
    populate();
}
void sessionEvalEntities_LoadCompleted(object sender, LoadCompletedEventArgs e)
{
    bEvalsLoaded = true;
    App.SessionEvaluationEntities.Add(_sessionId,sessionEvalEntities);
    populate();
}
void populate()
{
    if (!bEvalsLoaded || !bCriteriaLoaded)
        return;
            
    foreach (var evalCriterion in evalCriteriaEntities)
    {
        var evalInfo = new EvalCriteriaInfo();
        evalInfo.EvalCriteriaId = evalCriterion.EvalCriterionId;
        evalInfo.EvalCriteriaTitle = evalCriterion.EvalCriterionText;
                                
        foreach (var sessionEval in sessionEvalEntities)
        {
            if (sessionEval.EvalCriterionId == evalCriterion.EvalCriterionId)
            {
                evalInfo.EvaluationValue = sessionEval.Evaluation-1;
                evalInfo.SessionEvaluationEntity  = sessionEval;
            }
        }
                
        Evaluations.Add(evalInfo);  
    }
    NotifyPropertyChanged("Evaluations");
}

The Save method will update the corresponding SessionEvaluation entity if there’s an existing one, or it will add a new SessionEvaluation entity, the changes are pushed back to the Data Service by calling the DataServiceContext BeginSaveChanges method

public void Save()
        {
            foreach (var eval in this.Evaluations)
            {
                if (eval.SessionEvaluationEntity != null)
                {
                    eval.SessionEvaluationEntity.Evaluation = eval.EvaluationValue + 1;
                    App.DataServiceContext.UpdateObject(eval.SessionEvaluationEntity);
                }
                else
                {
                    SessionEvaluation sessionEval = new SessionEvaluation();
                    sessionEval.AttendeeId = App.AttendeeID;
                    sessionEval.EvalCriterionId = eval.EvalCriteriaId;
                    sessionEval.Evaluation = eval.EvaluationValue+1;
                    sessionEval.SessionId = _sessionId;
                    App.DataServiceContext.AddObject("SessionEvaluations", sessionEval);
                }
            }
            App.DataServiceContext.BeginSaveChanges(OnSaveCompleted, null);
        }

The code behind in the SessionEval page will set the DataContext of the page to an instance of the SessionEvalViewModel class, we will load the View Model data in the NavigateTo event handler the page. When we click the Save button we call the Save method of the view model

public partial class SessionEval : PhoneApplicationPage
{
    SessionEvalViewModel viewModel=new SessionEvalViewModel();
    public SessionEval()
    {
        InitializeComponent();
        DataContext = viewModel;
    }
    protected override void OnNavigatedTo(NavigationEventArgs e)
    {
        string strIndex = this.NavigationContext.QueryString["sessionId"];
        int sessionId = int.Parse(strIndex);
        if (!viewModel.IsDataLoaded)
        {
            viewModel.LoadData(sessionId);
        }
    }
    void OnSave(object sender, EventArgs e)
    {
        this.viewModel.Save();
    }
}

The SessionEval.xaml page contains a ListBox that is bound to the Evaluations collection, each list item will have a group of radio buttons that display the evaluation values.

<ListBox Margin="0,0,-12,0" x:Name="lstEvaluations"
            ItemsSource="{Binding Evaluations,Mode=TwoWay}" >
    <ListBox.ItemTemplate>
        <DataTemplate>

            <StackPanel Margin="0,0,0,17" Width="432">
                <TextBlock Text="{Binding EvalCriteriaTitle}" TextWrapping="Wrap"
                                Style="{StaticResource PhoneTextLargeStyle}"/>
                <ListBox SelectedIndex="{Binding EvaluationValue, Mode=TwoWay}"
                            ItemContainerStyle="{StaticResource RadioButtonListItemStyle}">
                    <ListBoxItem Content="1"/>
                    <ListBoxItem Content="2"/>
                    <ListBoxItem Content="3"/>
                    <ListBoxItem Content="4"/>
                </ListBox>
            </StackPanel>
        </DataTemplate>
    </ListBox.ItemTemplate>
</ListBox>

You can build and run the project to see the application in action

Main page displaying the Sessions in Panorama control

 

Session evaluation page

You can download the source code from here