Page view counter

September 2009 - Posts

What Makes A How-Do-I Video Great ?

I have been thinking a lot about how differently each of us (inside and out of Microsoft) approaches creating How-Do-I videos.

Let’s start by agreeing that no one instructor will please everyone, and the best way to create truly excellent videos is to step away from ego and try to zero in on what it is that this community values in instructional videos.

Videos

Create A List, Check It Twice

What follows are the characteristics that I look for and pay attention to, both when I'm creating videos and when I'm watching them. 

One of the things that I hope will come out of this posting is a healthy, strong, ongoing discussion about what makes for "good" videos; that is videos that are worth your time to watch.

 

Depends on whether your meaning of “good” is good. 

We can probably all agree that good sound quality is better than poor sound quality, and even agree on whether a given video has good (or good enough) sound quality. We may not all agree on what makes for good pacing – that is,  some people prefer a faster pace, others prefer a more deliberate pace. 

When thinking about these characteristics, we also need to consider the relative importance of any given characteristic. Once again, we can probably all agree that it is very important that the video have sufficiently good audio and video quality to be watchable and understandable, but we may differ in how much value we assign to the length of the video or smooth transitions.

Thus, if we were doing a survey we might want the user to answer paired questions such as: "Do you prefer to see videos that move along more quickly or more deliberately? " and  "How important is the pacing of the video to you?

Instructional Quality

I’ll arbitrarily break instructional quality into

  • Instructional approach
  • Pacing of the material
  • Signal to noise
  • How well a single topic is encapsulated

Instructional approach.

  • Does the presenter approach the topic in a logical fashion?
  • Does the presenter provide "motivation" (that is, what you're about to learn and why you care)?
  • Is the example sufficiently clean and simple to understand the key point of the video?
  • Is the example sufficiently "mappable" to your own problem domain that you can see how to go from this example to work you might want to do?
  • Does the presenter (A.) ignore best practices, (B.) follow best practices, (C.) violate best practices.
  • Does the presenter used obscure, esoteric, techniques that lead to confusion?
  • Does the presenter skipped over interim steps, leaving you a bit confused?
  • Does the presenter dwell over-much on the obvious?
  • Does it seem that the presenter knows what he or she is talking about?

Pacing

  • Do you find the overall pacing: too slow, too fast, about right?
  • Is the presenter making the right assumptions about what you already know?
  • Is the instructor speaking: too slow, too fast, about right?
  • Is the instructor adding code to the demo: too slow, too fast, about right?

Signal-to-noise ratio

  • What percentage of the video do wish the presenter had edited out?
  • Was this video worth the time to watch it?

Encapsulation

  • Does this video teaching single topic, or does it cover number of related topics?
  • How well do the name and description of the video map to the content?
  • Does this video makes forward references to other videos?

Production Quality And Other Factors

Audio/ Video quality.

  • Is the quality of the video high enough that you can easily see what is being described?
  • Is the quality of the audio high enough that you can easily hear every word?
  • Is the video, unobtrusive (no sudden and unexplained jumps, no dropouts, etc.).
  • Is there anything in the video or audio that is distracting (background noises, instructor chewing, etc.).
  • Did the presenter take the time to make sure that the most important part of the screen is easily visible without making you dizzy with excessive zooming?
  • Does the presenter take the time to add callouts (circles arrows etc.), where that is necessary to understand what the presenters say?
  • Are transitions smooth?

Entertainment/charisma value

  • Is the video: so boring you want to claw your eyes out/ reasonably interesting/ a fair amount of fun?
  • Overall, is the instructor interesting to listen to?

Duration

  • Given the topic, was this video too long, too short, or just about right?
  • Overall be fine if the presenters videos are: too long, too short, or just about right?

Questions For You:

  • What am I missing in this list?
  • Which of these are very important?
  • Which of these are nearly irrelevant?
  • To what extent is all of this secondary to having videos on the right topics?
  • When a new version of Silverlight appears, would you rather see more videos of lower quality, or fewer videos of higher quality?
  • What have you seen in other videos that you wish we would do here?
  • What are we doing in our videos that you wish we would stop?

What?  No survey?

It's tempting at this point to create a survey based on these questions; but that would presume that I already know what all the right questions are, and more important, that would kill the discussion before it even begins.

While blog comments are not the ideal discussion framework, I invite you please to respond in any way you like; answering one or more questions; adding, deleting or otherwise editing my lists; or just commenting on the overall topic.

RIA: Data Source Control

MiniTutorialLogo
ProjectTuringLogo Turing Project Page: [Novice: 11 | Advanced: 8 ]
FAQ | Table of Contents | Definitions
What is this and where do I start?

Part 3 of 3

This is the last of a three part sub-series on using Entity Frameworks and RIA Data Services to move data from our Sql Server database to our Silverlight Application’s DataGrid.

In Part 2, we explored returning an object graph by using the Entity Frameworks and by decorating the metadata for the RIA Services with the properties we wanted to retrieve from the contained object.

Until now, however, all of our code has instantiated via RIA Services context programmatically, invoked the query function programmatically, and assign the returned collection to the items source property of the data grid programmatically. In this post, I’ll look at a declarative approach.

Using the RIA Services DomainDataSource Control

We’ll begin with the program as we left it after the previous blog post, but we’ll immediately remove the three lines in blogs.xaml.cs that manipulates the data context.

   1: public partial class Blogs : Page
   2: {
   3:     //  private readonly BlogsContext bc = new BlogsContext();
   4:   public Blogs()
   5:   {
   6:     InitializeComponent();
   7:     //    bc.Load( bc.GetBlogsQuery() );
   8:     //    this.blogsDataGrid.ItemsSource = bc.Blogs;
   9:   }

Having removed the programmatic interface to the data context, let's now open up blogs.xaml  and provide it with information about the data context by way of teh DomainDataSource  control, which we must first add to the toolbox. To do so, right-click on the Silverlight Xaml Controls tab, and then click on Choose Items.

Within the Choose Toolbox Items dialog box click on the Silverlight Components tab, and then click the browse button. Navigate to your program files ->Microsoft SDKs -> RIA Services, pick the folder for the latest version, within that:  Libraries-> Silverlight.  Double-click on System.Windows.RIA Controls.dll 

You should find that the DomainDataSource control has been added to the bottom of your list of controls; you may want to right-click and choose to sort them alphabetically.

Drag a DomainDataSource control onto the Xaml page just above the DataGrid.  By dragging it on rather than writing it yourself Visual Studio will create the namespace for you set up your using statement and your references.

The four attributes you'll typically want to set, if no others, are the Name , LoadSize, QueryName, and the AutoLoad property. We will examine each of these in turn below.

You will also want to set the domain context property which you will do it explicitly as shown in the code that follows:

   1: <riaControls:DomainDataSource x:Name="BlogsDataSource"
   2:                               LoadSize="15"
   3:                               QueryName="GetBlogs"
   4:                               AutoLoad="True">
   5:    <riaControls:DomainDataSource.DomainContext>
   6:       <ds:BlogsContext />
   7:    </riaControls:DomainDataSource.DomainContext>
   8: </riaControls:DomainDataSource>

 

The LoadSize property instructs the DomainDataSource control as to how many records to get for each query to the database.  This allows you to fine tune the performance of your application (too small load size, and you are making many calls to the database, to larg a load size and there will be a noticeable wait for the data to arrive).

The QueryName parameter references the query We created in the RIA Services class, you will remember setting GetBlogs to include bloggers. Notice that here you use the name of the method (GetBlogs ).  Rather than "GetBlogsQuery()" as you did in C#.

Setting Autoload to true tells the DomainDataSource control to load the first set of data when it is initialized.

Note that the namespace for BlogsContext is DS as opposed to RIAControls the namespace for the DomainDataSource.  DS is a namespace that refers to the Web project (you’ll need to add that to the top of the file)

Build and run the application.  The results, that is the running application, are identical to what you had before.  However now you are not creating this programmatically but rather declaratively in the Xaml.

Sorting

Is relatively straightforward to add sorting declaratively as well. You do so by adding a sort descriptor to your DomainDataSource control.  In the case shown below (Lines 9 through 11), we are setting the SortDescriptor property explicitly, and having it sort on BlogName.

 
   1: <riaControls:DomainDataSource x:Name="BlogsDataSource"
   2:                               LoadSize="20"
   3:                               QueryName="GetBlogs"
   4:                               AutoLoad="true">
   5:    <riaControls:DomainDataSource.DomainContext>
   6:       <ds:BlogsContext />
   7:    </riaControls:DomainDataSource.DomainContext>
   8:    <riaControls:DomainDataSource.SortDescriptors>
   9:       <riaData:SortDescriptor PropertyPath="BlogName"
  10:                               Direction="Ascending" />
  11:    </riaControls:DomainDataSource.SortDescriptors>
  12: </riaControls:DomainDataSource>

Sending the sort column in this way it causes the data grid to open with that column sorted.  The user, of course, can sort on any column by clicking on the column header.

Filtering

Before we leave the DomainDataSource control, let's take a quick look at filtering, which works much like sorting. Sorting the grid In this case rather than explicitly putting into our filter what it is we are filtering on, we will instead set the "Control Parameter" to point to another control on the same page that will provide the text on which to sort.

To begin, we'll add a text box into which the user can type the desired filter.

<StackPanel Orientation="Horizontal"
            HorizontalAlignment="Right"
            Margin="0,-16,0,0">
   <TextBlock VerticalAlignment="Center"
              Text="Name Contains Filter" />
   <TextBox x:Name="filterText"
            Width="75"
            FontSize="11"
            Margin="4"
            Text="Silverlight" />
</StackPanel>

 

This code can be placed directly above the DomainDataSource, and as you can see it provides a textblock with a prompt and a text box, which will gather in the desired filter. Next We'll add a FilterDescriptors element to the DomainDataSource control, and tell it to get its value from that text box:

   1: <riaControls:DomainDataSource x:Name="BlogsDataSource"
   2:                               LoadSize="20"
   3:                               QueryName="GetBlogs"
   4:                               AutoLoad="true">
   5:    <riaControls:DomainDataSource.DomainContext>
   6:       <ds:BlogsContext />
   7:    </riaControls:DomainDataSource.DomainContext>
   8:    <riaControls:DomainDataSource.SortDescriptors>
   9:       <riaData:SortDescriptor PropertyPath="BlogName"
  10:                               Direction="Ascending" />
  11:    </riaControls:DomainDataSource.SortDescriptors>
  12:    <riaControls:DomainDataSource.FilterDescriptors>
  13:       <riaData:FilterDescriptorCollection>
  14:          <riaData:FilterDescriptor PropertyPath="BlogName"
  15:                                    Operator="Contains">
  16:             <riaData:ControlParameter ControlName="filterText"
  17:                                       PropertyName="Text"
  18:                                       RefreshEventName="TextChanged" />
  19:          </riaData:FilterDescriptor>
  20:       </riaData:FilterDescriptorCollection>
  21:    </riaControls:DomainDataSource.FilterDescriptors>
  22: </riaControls:DomainDataSource>

 

The filter descriptors begin online 12 and run through line 21.  Notice on lines 14 and 15 that you are indicating which property is being filtered on and that you have your choice of operators such as contains, is, etc.  On lines 16 through 18, you indicate the name of the control that has the filter, the name of the property of the control from which to extract the filter, and the event on which you will update the filter.

N.B.: the code is showing updates each time a letter is typed in the text box.  This will cause a call to the database for every letter that's typed.  You can solve this by using the Load Delay parameter.  Of course having a low delay will make the page initially load more slowly, so you may want to add the load delay programmatically after you initialize the page.

 

Finally to give it a bit of polish, let's add a DataPager, just below the DataGrid.  Note that the binding for the DataPager is identical to that for the DataGrid, and that the DataPager retrieves 10 records at a time; we'll go up so that and change the DomainDataSource load size to 30, so that it will retrieve 3 pages-worth at a time.

<data:DataPager PageSize="10" 
  Source="{Binding Data, ElementName=BlogsDataSource}"  />


 

SortedAndFiltered

 

 
 
 
 
 
 
 
 
 
 
 
 
 

Silverlight Twitter Tips Launches

Very proud to announce the launch of #stwip -  silverlight twitter tips.

Find them by following my Tweets or by the meme

Project Turing: Beginning RIA Svcs

MiniTutorialLogo

ProjectTuringLogo

Turing Project Page: [Novice: 9 | Advanced: 6 ]   
FAQ | Table of Contents | Definitions

What is this and where do I start?


From Database to DataGrid

The next step in Project Turing is to create a first iteration of the Silverlight application that will retrieve data from our database.  Using our technology of choice: the Data Entity Framework coupled with .Net RIA Services.

.Net RIA Services will allow us to create business logic on the server and to access that same set of classes on the client.  We will retrieve the data from these business classes and display them in a data grid within a business application, that is a multipage Silverlight application.

.NET RIA Services

While a great deal has been written about RIA Services, I find when talking with Silverlight developers that there is still some confusion about where RIA Services are best used and what the key benefits are.  This really  isn't terribly surprising given how new the RIA Services platform is. Despite the incredible work done by Brad Abrams and others on the dev team  to document RIA Services,  it will take a while for many Silverlight programmers to fully integrate the platform into their designs

Project Turing offers an ideal opportunity to provide an incremental introduction to  RIA Services and an illustration of the immediate benefits of the platform.

Just before the progress took the digression into Linq, we created our basic database tables, and the initial architecture of Project Turing. Here is a snapshot of the database as it exists at this point in the project:

 DataBaseDesign
(click on image for full size)

Our goal now is to retrieve this data using the Data Entity Framework and to make it available to the DataGrid using .NET RIA Services.

What You Need To Program With RIA Services Project

For this part of Project Turing you're going you need to install the latest .Net RIA Services framework.  You can find complete instructions and all of the libraries that you need here. (There is no cost.)

You also want to create or download the Turing database, which you can install to SQL Server 2005, SQL Server 2008, or SQL express.  You can download a backup of this database and restore it to your system, from here.  Alternatively you can download a script to re-create the database from here.

Getting Started

We will build the application incrementally, starting with a simple version and adding complexity as we go.  Specifically, we will break down the steps of interacting with the database into a series of smaller tasks.

The goal in this first example will be to create a shell of a multipage application, and within that a page to which we’ll add a basic  DataGrid. To get started open Visual Studio and create a new project. In the Project Type, select Visual C#/Silverlight, and in the templates select Silverlight Business Application.  I named my project TuringRIAServices.

 CreatingBizApps
(Click image for full size)

A fairly sophisticated Silverlight application is created for you out-of-the-box.  Your solution consists of two projects, and your Xaml project has three subdirectories: Assets, Libs, and Views.

The views folder contains the initial pages already provided in the application.  These include Xaml files for logging in, a Home page, a custom error window and an about box.

MainPage.xaml acts as a shell and a dispatcher to the pages that are in the Views folder.  The Assets folder contains your first styles Xaml page, but you are free to add more (as you are to all the folders).

To begin open MainPage.xaml and let’s examine some of what has been put in place by Visual Studio.

   1: <Grid x:Name="LayoutRoot" Style="{StaticResource LayoutRootGridStyle}">
   2:   <Border x:Name="ContentBorder" Style="{StaticResource ContentBorderStyle}">
   3:           <navigation:Frame x:Name="ContentFrame" Style="{StaticResource ContentFrameStyle}" 
   4:                             Source="/Home" Navigated="ContentFrame_Navigated" NavigationFailed="ContentFrame_NavigationFailed">
   5:               <navigation:Frame.UriMapper>
   6:                 <uriMapper:UriMapper>
   7:                   <uriMapper:UriMapping Uri="" MappedUri="/Views/Home.xaml"/>
   8:                   <uriMapper:UriMapping Uri="/{pageName}" MappedUri="/Views/{pageName}.xaml"/>
   9:                 </uriMapper:UriMapper>
  10:               </navigation:Frame.UriMapper>
  11:           </navigation:Frame>
  12:   </Border>
  13:  
  14:   <Grid Style="{StaticResource NavigationOuterGridStyle}">
  15:     <Grid x:Name="NavigationGrid" Style="{StaticResource NavigationGridStyle}">
  16:  
  17:       <Border x:Name="BrandingBorder" Style="{StaticResource BrandingBorderStyle}">
  18:         <StackPanel x:Name="BrandingStackPanel" Style="{StaticResource BrandingStackPanelStyle}">
  19:  
  20:           <ContentControl Style="{StaticResource LogoIcon}"/>
  21:           <TextBlock x:Name="ApplicationNameTextBlock" Style="{StaticResource ApplicationNameStyle}" 
  22:                              Text="Application Name"/>
  23:  
  24:         </StackPanel>
  25:       </Border>
  26:  
  27:       <Border x:Name="LinksBorder" Style="{StaticResource LinksBorderStyle}">
  28:         <StackPanel x:Name="LinksStackPanel" Style="{StaticResource LinksStackPanelStyle}">
  29:  
  30:           <HyperlinkButton x:Name="Link1" Style="{StaticResource LinkStyle}" 
  31:                                    NavigateUri="/Home" TargetName="ContentFrame" Content="home"/>
  32:  
  33:           <Rectangle x:Name="Divider1" Style="{StaticResource DividerStyle}"/>
  34:  
  35:           <HyperlinkButton x:Name="Link2" Style="{StaticResource LinkStyle}" 
  36:                                    NavigateUri="/About" TargetName="ContentFrame" Content="about"/>
  37:  
  38:         </StackPanel>
  39:       </Border>
  40:  
  41:     </Grid>
  42:  
  43:     <Border x:Name="loginContainer" Style="{StaticResource LoginContainerStyle}"/>
  44:   </Grid>
  45:  
  46: </Grid>
.

Before taking this apart lets look at what is created if you build and run this without making any changes.

RunningOutOfTheBox

(Click image for full size)

You can see in the image that there is a frame around the home page, which has within it a logo, the name of the application, a button for the currently selected page, a button for the about page, and a small button for the login dialog.

This corresponds directly of course to the Xaml above.

On line 2, we  create a border, which uses the static resource predefined by Visual Studio and stored in  Styles.Xaml within the Assets folder. 

Within that border a Frame object is defined on line 3 through 11, within the navigation namespace defined at the top of the file.

xmlns:navigation="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Navigation" 

The frame has three critical properties:

  • The source.
  • The name of the method to call after successfully navigating to the page.
  • The name of the method to call in the event of failure.

Nested within the frame is a mapper, whose job is to map pages to URIs.

Note on line 8, the generic mapping that is used with the substitution parameter pagename.  This is a subtle but very powerful capability that allows you to create essentially a template of the relationship between pages in their URI and avoid duplicating this code for every page.

The second major UI area within this Xaml file begins on lines 14 and 15 with the creation of an outer and inner grid.

The content control defined on line 20 is used to display the logo and on line 21 is a standard textblock, where the text is set to Application Name.  This would be a good opportunity to change that text to "Project Turing RIA Services"

The second area is itself divided in to two areas each with its own water, and in the lower area beginning on line 27.  Line 30 shows the hyperlink button for the homepage and on line 35 is the hyperlink button for the about page.  Adding a new button only requires an additional hyperlink button.  In this section, the navigation is handled by the section about.

You may find it worthwhile and interesting to open up the style.xaml  file, and take a look at the static resources that were created by this template.

Adding A DataGrid

We will add the Silverlight control DataGrid to a new Silverlight page.  To do so, right-click on the project and choose Add --> New Item.  In the Add Item dialog box, choose Silverlight under categories and Silverlight Page in the templates pane.

Name the new page BlogsDataGrid and click Add.

At the top of this page, we will place a header as we will on each new page.  The header consistents of a  ScrollViewer, within which we will place a StackPanel, within which we will place a Toolbox with the name of the page as shown here:

Note: this design, and much in these examples, comes from the Walk-Through created by the development team and used here with their permission, for which I and my Carpal Tunnel Syndrome are very grateful.

<ScrollViewer BorderThickness="0"
              VerticalScrollBarVisibility="Auto"
              Padding="12,0,12,0"
              Margin="-12">
    <StackPanel Margin="0,12,0,12"
                Orientation="Vertical">
        <TextBlock Text="Blogs"
                   Style="{StaticResource HeaderTextStyle}" />
    </StackPanel>
</ScrollViewer>

Finally, drag a DataGrid from the toolbox into the grid.

Note: the advantage of dragging from the toolbox rather than writing the Xaml by hand is that Visual Studio will create the necessary namespace and use that as a prefix for the DataGrid

xmlns:data="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Data"

Once Visual Studio has fixed up the namespace, modify the data grid element to include the properties Name (BlogsDataGrid), MinHeight (150), and IsReadOnly (true).

<navigation:Page 
  xmlns:data="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Data"
  x:Class="TuringRiaServices.Blogs"
  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;assembly=System.Windows.Controls.Navigation"
  mc:Ignorable="d"
  d:DesignWidth="640"
  d:DesignHeight="480"
  Title="Blogs Page">
    <Grid x:Name="LayoutRoot">
        <ScrollViewer BorderThickness="0"
                      VerticalScrollBarVisibility="Auto"
                      Padding="12,0,12,0"
                      Margin="-12">
            <StackPanel Margin="0,12,0,12"
                        Orientation="Vertical">
                <TextBlock Text="Blogs"
                           Style="{StaticResource HeaderTextStyle}" />
            </StackPanel>
        </ScrollViewer>
        <data:DataGrid x:Name="blogsDataGrid"
                       MinHeight="150"
                       IsReadOnly="True"></data:DataGrid>
    </Grid>
</navigation:Page>

 

 

Your page is ready, but there is no way to navigate to it.  Return to MainPage.Xaml and let's add the logic for an additional button.

First, take a look at the solution Explorer, and if BlogsDataGrid.Xaml is not in the views folder   drag it there now.

As noted above, all we need to do here is add a hyperlink button for our new page.  This will go immediately below the hyperlink button for the about box

<HyperlinkButton x:Name="Link3"
                 Style="{StaticResource LinkStyle}"
                 NavigateUri="/Blogs"
                 TargetName="ContentFrame"
                 Content="Blogs" />

Build and run the application and be sure to click on the new button for the Blogs page. Please take note of the very elegant handling of the exception that is thrown, because we wired up for a same blogs.  But our actual page is named BlogsDataGrid.

While we're fixing that link, lets take note of the fact that Visual Studio has named each of the hyperlink buttons.  Link one, link two etc. , while it is natural and common to continue this as we have done above, I greatly prefer to name the hyperlink buttons to indicate their function.  And so I will rename Link1  HomeLinkButton; Link2  AboutLinkButton and our Link3 BlogsLinkButton. I will also fix up the NavigateURI to the full name of the page.

One defensible choice is to continue the pattern provided by Visual Studio, but it is my personal approach to rename all three on the premise that good naming practice makes for good documentation.

<UserControl x:Class="TuringRIA_Services.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:navigation="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Navigation"
xmlns:uriMapper="clr-namespace:System.Windows.Navigation;assembly=System.Windows.Controls.Navigation"
xmlns:dataControls="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Data.DataForm.Toolkit"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
d:DesignWidth="640"
d:DesignHeight="480">
<Grid x:Name="LayoutRoot"
Style="{StaticResource LayoutRootGridStyle}">
<Border x:Name="ContentBorder"
Style="{StaticResource ContentBorderStyle}">
<navigation:Frame x:Name="ContentFrame"
Style="{StaticResource ContentFrameStyle}"
Source="/Home"
Navigated="ContentFrame_Navigated"
NavigationFailed="ContentFrame_NavigationFailed">
<navigation:Frame.UriMapper>
<uriMapper:UriMapper>
<uriMapper:UriMapping Uri=""
MappedUri="/Views/Home.xaml" />
<uriMapper:UriMapping Uri="/{pageName}"
MappedUri="/Views/{pageName}.xaml" />
</uriMapper:UriMapper>
</navigation:Frame.UriMapper>
</navigation:Frame>
</Border>
<Grid Style="{StaticResource NavigationOuterGridStyle}">
<Grid x:Name="NavigationGrid"
Style="{StaticResource NavigationGridStyle}">
<Border x:Name="BrandingBorder"
Style="{StaticResource BrandingBorderStyle}">
<StackPanel x:Name="BrandingStackPanel"
Style="{StaticResource BrandingStackPanelStyle}">
<ContentControl Style="{StaticResource LogoIcon}" />
<TextBlock x:Name="ApplicationNameTextBlock"
Style="{StaticResource ApplicationNameStyle}"
Text="Project Turing RIA Services" />
</StackPanel>
</Border>
<Border x:Name="LinksBorder"
Style="{StaticResource LinksBorderStyle}">
<StackPanel x:Name="LinksStackPanel"
Style="{StaticResource LinksStackPanelStyle}">
<HyperlinkButton x:Name="HomeLinkButton"
Style="{StaticResource LinkStyle}"
NavigateUri="/Home"
TargetName="ContentFrame"
Content="home" />
<Rectangle x:Name="Divider1"
Style="{StaticResource DividerStyle}" />
<HyperlinkButton x:Name="AboutLinkButton"
Style="{StaticResource LinkStyle}"
NavigateUri="/About"
TargetName="ContentFrame"
Content="about" />
<HyperlinkButton x:Name="BlogsLinkButton"
Style="{StaticResource LinkStyle}"
NavigateUri="/BlogsDataGrid"
TargetName="ContentFrame"
Content="Blogs" />
</StackPanel>
</Border>
</Grid>
<Border x:Name="loginContainer"
Style="{StaticResource LoginContainerStyle}" />
</Grid>
</Grid>
</UserControl>

This time clicking on the blogs hyperlink does not throw an exception but rather brings you to the blogs page, where the outline of the DataGrid is drawn but of course there is as of yet no data.  Let's fix that now.

 

Adding Business Logic And Data

Our next principal task is to retrieve data from the database and present it to the DataGrid.  As noted in previous mini tutorials and postings for this project, there are a number options for retrieving data from a database.  One of the most powerful is to use the Entity Framework.

When a developer says technology is "powerful," we typically mean that it does a lot of the routine plumbing for us, and that it is highly flexible.  Certainly both are true of the Entity Framework.

A quick look at Julia Lerman's Programming Entity Framework (O'Reilly Media, 2009) is both reassuring and somewhat intimidating; her book is over 750 pages.  This stands as testimony to the enormous flexibility of the Entity Framework. 

That said, using the Entity Framework to retrieve data from a relatively straightforward relational database can be explained quite quickly. I will demonstrate enough of the Entity Framework to get a feel for the role that it plays and the level of abstraction that it provides, but in no way will this be a comprehensive survey of the Entity Framework in its entirety.

To begin, add a new item to Turing RIA Services.web.  In the Add New Item dialog select Data in the Categories pane, and choose ADO.net Entity Data Model in the templates pane, and name the new Entity Data Model TuringBlogs.  Indicate to the Entity Data Model Wizard that you wish to generate your model from an existing database.

 AddingEntityDataModel
(Click image for full size)

You'll be prompted to create or reuse a connection string for your database

 CreateDataConnection
(Click image for full size)

Note that using the second radio button "include the sensitive data in the connection string,"  will slightly simplify your code, but expose a significant security hole.

After connecting to your database, you'll be asked to choose which objects you wish to include.  Expand the tables, and select the Blogs table.

 SelectBlogsTable
(Click image for full size)

When you click finish, the entity data model will appear in the designer. To verify that everything is correct, build the solution.

Transporting the Data Class To The Client With .Net RIA Services

You're halfway home.  You now have an entity (object) that represents the data that is stored in relational tables in SQL Server.  The next step is to create business classes that will represent and manipulate that data on the server, and be equally available on the client.  That is in a nutshell, what.net RIA Services does for a living.

Once again right-click on the Turing RIA Services Web project, and add a new item. However this time, in the Categories pane, select Web and in the templates pane Domain Service Class.  Name the new item BlogsService and click Add.

 AddingBlogService
(Click image for full size)

Click Add which will open the Add New Domain Service Class dialog. Select Blogs from the list of entities (okay, in this case it's the only item in the list).  In the same dialog, be sure to check Enable Editing, which enables the CRUD functionality, and at the bottom of the dialog, click "Generate associated classes for metadata." Then click OK.

 NewDomainServiceClassDialog
(Click image for full size)

Open the class of this generates for you: BlogsService.cs and modifying the GetBlogs method as follows:

//Replace this generated code:
public IQueryable<Blogs> GetBlogs()
{
   return this.Context.Blogs;
}
 
// with this custom code:
public IQueryable<Blogs> GetBlogs()
{
  return this.Context.Blogs.OrderBy(b => b.BlogName);
}

In this first example, our customization is ordering the results by the name of the blog.

Define It on the Server, Invoke It on the Client

When you rebuild the solution not only will the classes that you've created in the server be built, but a corresponding set of proxy classes will be built in the client project.

Thus the class and its method will be available on the client side, and therefore available to our DataGrid.  At the top of the code behind page will need to add three using statements; one to bring in the web project, and the other two for RIA Services:

using TuringRIAServices.Web;          
using System.Windows.Ria.Data;
using System.Windows.Ria.ApplicationServices;

In the generated code in the client project, the BlogsContext is generated based on the BlogsService you created on the server. To see the definition, turn on show all files, and locate the file TuringRIAServices.web.g.cs Inside the previously hidden Generated_Code folder. In that file, you will find the definition for the BlogsContext.

public sealed partial class BlogsContext : DomainContext
{
    partial void OnCreated();
 
    public BlogsContext() : 
            this(new HttpDomainClient(
              new Uri("DataService.axd/TuringRIA_Services-Web-BlogsService/", System.UriKind.Relative))) {}
 
   public BlogsContext(Uri serviceUri) : 
            this(new HttpDomainClient(serviceUri)) {}
    
    public BlogsContext(DomainClient domainClient) : 
            base(domainClient)
    { this.OnCreated(); }
    
    public EntityList<Blogs> Blogs
    {
        get { return base.Entities.GetEntityList<Blogs>();  }
    }
    
    public EntityQuery<Blogs> GetBlogsQuery()
    {
        return base.CreateQuery<Blogs>("GetBlogs", null, false, true);
    }
    
    protected override EntityContainer CreateEntityContainer()
    {
        return new BlogsContextEntityContainer();
    }
    
    internal sealed class BlogsContextEntityContainer : EntityContainer
    {
        
        public BlogsContextEntityContainer()
        {
            this.CreateEntityList<Blogs>(EntityListOperations.All);
        }
    }
}
 

Returning to BlogsDataGrid.xaml.cs, instantiate a BlogsContext as a member variable,

private BlogsContext blogsContext = new BlogsContext();

Here is the complete BlogsDataGrid.xaml.cs:

using System.Windows.Controls;
using System.Windows.Navigation;
using TuringRIA_Services.Web;
using System.Windows.Ria.Data;
using System.Windows.Ria.ApplicationServices;
 
namespace TuringRIA_Services
{
   public partial class BlogsDataGrid : Page
   {
      private BlogsContext blogsContext = new BlogsContext();
 
      public BlogsDataGrid()
      {
         InitializeComponent();
         this.blogsDataGrid.ItemsSource = blogsContext.Blogs;
         blogsContext.Load( blogsContext.GetBlogsQuery() );
      }
 
      protected override void OnNavigatedTo( NavigationEventArgs e )
      {
      }
   }
}

In the constructor, you ask the BlogsContext object to retrieve its Blogs property (an entity list of Blogs) and this collection is assigned as the ItemsSource of the DataGrid. The following line instructs the Blog Context to call its Load member method, passing in the query retrieved by calling GetBlogQuery().  This will be the query that we customized earlier.

The query as you remember, retrieves all of the blogs, ordered by the name of each blog.  This collection is assigned to the DataGrid and the page is displayed when you click on the blogs button.

 DataGrid Running
(Click image for full size)

What have you learned, Dorothy?

In this first iteration of moving data from our SQL Server database to the DataGrid on our client, we have examined using the Entity Framework for creating an object orientated model of the data in the database, and using RIA Services both to provide a common set of business classes on the server and the client and to provide transportation of the data from server-side to client-side.

In the next posting on Project Turing,  I will examine more complex, data retrieval.

Download source code

Please note that due to Carpal Tunnel Syndrome this posting was dictated and then transcribed by computer.  I apologize for any resulting absurdities.



Novice PreviousLinqToSql Next: Multiple tables and RIA Services
Advanced PreviousLinqToSql Next: Multiple tables and RIA Services

Mini-Review of Code Rush

These days I'm very much in the market for anything will cut down on manual typing.  Thus I'm dictating more of what I write (using speech recognition), and today I began to explore CodeRush: a highly extensible Visual Studio plug-in

Being very smart marketers, DevExpress offers a free version: CodeRush Express, which is a somewhat stripped down, but fully useful product, and they offer a 30 day trial of the full CodeRush product. The trial, however, allows you to install both the trial of CodeRush and the free CodeRush Express.  Of course, trying out CodeRush is like driving the turbo enhanced model of the car you want.  Once you do, it’s mighty hard to go back to four unassisted cylinders, and I suspect it will be mighty hard to step down to CodeRush Express.  But stay tuned.

 

Some of what CodeRush does is not immediately intuitive (at least not late at night), but  the documentation is excellent, and includes some surprisingly useful animations.  My biggest complaint is that while they tell you how to do many things, they don't always tell you why you might want to (hey, does that sound familiar?)  A classic example of this is that you can have "Smart paste" where, for example, you copy “a < b” and when you paste you get “a = b.” This to me was not obviously useful, but I tried it out and the light bulb lit:

if ( theValue < minValue )
{
theValue = minValue;
}
 
To create this I copied the condition, and pasted it as the action, and that was very sweet.

You can of course create your own Smart paste constructs; which I suspect I’ll do quite a bit.

What You See Is What You Will Get

What moved me to write this brief review before fully exploring this product, however, was the following wicked cool experience.  In a bit of sample code I wrote:

public class foo
{
int age;
public foo()
{

}
}

I then used the template expansion for initializing the member variable

InitializeMemberVariable

 

With one click, this initialized the member variable “age” to zero.

MemberInitialized

[highlighting added]

That was really nice, I could see doing that.

I have chosen the option of having a value next to each method indicating its relative complexity (the pale red 5 to the right of the method name) and I’ve asked it to display the architecture of the program by drawing faint lines between connected braces.

 

CodeRush then indicated that there was more that could be done with the member variable age (you may be way ahead of me)

EncapsulateField

 

The three blue dots under the member varaible “age”  Eare CodeRush's equivalent to a smart tag (this like everything else is configurable) and notice that the re-factor dialog offers two CodeRush refactor options as well as three Visual Studio modifications.

As you would guess, with one click you are able to encapsulate the member variable into a field.  What I find particularly nice is that before committing to the change,  CodeRush shows you what it's going to do:

 

BeforeSettingTheProperty

 

Note that I've done no drawing on this image; everything shown here appears inside the Visual Studio editor, complete with the arrows indicating that the member age will be replaced by the property, etc. 

The graphical preview is enormously useful for making sure that the changes your expect are what you'll get, and also for learning about new technology (e.g., switching back and forth between anonymous methods and lambda expressions) and for instructing others.

Hints

Immediately upon accepting this change, a blue "hint" indicator appeared, letting me know that CodeRush felt the code could still be improved.  Hovering on the hint opened a box that told me what the issue was, and offered a one click solution

 

IssueIndicator

 

Once again, the change was graphically displayed before I committed to it:

ProposedAutoProperty

Clicking on the link implements:

AfterAcceptingAutoProp

It was all much faster to do then to explain. It was also an entirely pleasant experience, without any opportunity for confusion.

 

It's early days, but this software looks like a keeper.

Silverlight and Sunday night football

My fascination with Silverlight

Is not strongly determined by the video aspects, though they can be the most immediately compelling.  That said, it is certainly an indicator of the impacted Silverlight is having, that Silverlight was chosen for the online Olympics experience, the online presidential inauguration, and now for the entire season of Sunday night football.

It was announced today that  The NFL and NBC will be delivering the entire Sunday Night Football season utilizing Silverlight 3.0 and IIS Smooth Streaming and that the first game of the opening season will be broadcast tonight with the Tennessee Titans vs. Pittsburg Steelers game starting at 5:30pm PST and can be viewed online for free:

There's no way to escape that this is a pretty big deal, and it includes a host of extraordinary features including:

 

· A full screen video player that is capable of delivering 720p HD video.

· A main HD video feed, plus 4 user selectable alternate synchronized camera feeds that allows users to switch camera angles themselves.

· Adaptive smooth streaming of live HD video, which enables the video player to automatically switch bitrates on the fly depending on networking/CPU conditions.

· DVR support of the live video, including Pause, Instant Replay, Slow Motion, Skip Forward/Back

· Play-by-play data (touchdowns, fumbles, etc) inserted as tooltip chapter markers on the scrubber at the bottom allowing you to quickly seek to key moments.

· Highlights of major plays created within minutes of the play.

· Live stats in real-time

· live chat with the TV broadcasters

Posted by jesseliberty | 2 comment(s)
Filed under: ,

Oops

While we are transitioning to the new design, there are some minor problems with my blog, and so for now I will be dual posting to here (http://SilverlightGeek.me) and to http://geekswithblogs.net/SilverlightGeek 

 

Thank you for your patience.

Posted by jesseliberty | with no comments
Filed under:

Project Turing: Multiple Tables & RIA Services

MiniTutorialLogo

ProjectTuringLogo

Turing Project Page: [Novice: 10 | Advanced: 7 ]   
FAQ | Table of Contents | Definitions

What is this and where do I start?

In the previous listing in the Project Turing series, I examined extracting data from a single table and displaying it in the DataGrid.  This time we’ll modify the program by including data from related data (the Blog and its author) retrieved from two (joined) tables.

Let’s start by opening the project as it was after the previous posting (source available here). Delete the Data Entity Frameworks and the RIA Services projects by removing the four associated files from the project.

 DeleteProjects

Displaying Data From Two Tables In The DataGrid

Return to Blogs.xaml, replace the DataGrid with the following code

   1: <data:DataGrid x:Name="BlogsDataGrid"
   2:                MinHeight="150"
   3:                IsReadOnly="True"
   4:                AutoGenerateColumns="False" >
   5:    <data:DataGrid.Columns>
   6:       <data:DataGridTextColumn x:Name="BlogName"
   7:                                Binding="{Binding BlogName}"
   8:                                Header="Blog Name" />
   9:       <data:DataGridTextColumn x:Name="BlogURL"
  10:                                Binding="{Binding BlogURL}"
  11:                                Header="URL" />
  12:       <data:DataGridTextColumn x:Name="First"
  13:                                Binding="{Binding First}"
  14:                                Header="First Name" />
  15:       <data:DataGridTextColumn x:Name="Last"
  16:                                Binding="{Binding Last}"
  17:                                Header="Last Name" />
  18:       <data:DataGridTextColumn x:Name="Email"
  19:                                Binding="{Binding Email}"
  20:                                Header="Email" />
  21:       <data:DataGridTextColumn x:Name="Phone"
  22:                                Binding="{Binding Phone}"
  23:                                Header="Phone" />
  24:    </data:DataGrid.Columns>
  25: </data:DataGrid>
  26:   

Obtaining The Object Graph

The two most common ways to obtain the data from joined tables is to delegate that responsibility either to the database, by creating the view, or by using the ability of the Data Entity Framework to return the entire object graph.

Remember that our database design creates a one to many relationship between Bloggers (one) and their Blogs (many). This is reflected in the database diagram, and will be reflected in our Data Entity Model by having each Blog object contain a Blogger object.

 

As you did previously, right-click on the web project choose add new, select the data, select ADO.net Entity Data Model and mean the new model Blogs.emdx.

As you did previously, indicate that you want to build your model from the database, choose the appropriate connection string, click okay and in the Entity Data Model Wizard expand and choose both the Blogs and the Bloggers table. Name the Blogs model and click on the finish button.  

It is instructive to compare side-by-side the SQL Server diagram of the tables and the Data Entity Model you just created, as shown in the next illustration with the Data Entity objects on the bottom

RelationalAndObject

In this case, because of the simplicity of the design, there is an isomorphic mapping from the relational database tables to the data objects. It is important to note that this need not be true, and that a given data object may be composed from numerous tables, and may contain a subset or superset or a composite of the values stored in the database.

In this case, the most important thing brought forward into the data object model is the one to many relationship between Bloggers and Blogs. This is manifested in the Blogs class by the navigation property Bloggers, which serves as a reference to all of the Bloggers who own this Blog (by design that will always be one object as in our design while one Blogger may own many Blogs, each Blog is owned by a single Blogger)

The key point, is that when we retrieve the Blog, we can instruct the data model either to return a reference to each Blogs Blogger, or, via containment, to return each logger object within each Blog object.

In this case, so that we can display information in the data grid about both the Blog and the Bloggers, rather than thinking about joining the tables as a database programmer might, we will instruct the data Entity Frameworks to return the object graph: that is to return each Blog will have its Blogger via containment.

Using attributes in the metadata

To accomplish this, first build the web project so that your new data model will be available. Next right-click on the project and choose add new, in the Categories choose Web, and in the templates area choose Domain Service Class. 

Give it the name Blog service and click the add button, and in the Add New Domains Service Class dialogue be sure to check all of the checkboxes before pressing okay.

The RIA Services framework will generate both the Blogs service and a file with its metadata. Open the metadata file where you will find classes representing the Bloggers metadata and the Blogs metadata. The last few lines of the Blogs metadata class represents the public properties the first of which is of type Bloggers. You will need to decorate that property with the include attribute to indicate which fields you want included in to the Blogs class from the Bloggers object as shown in the next snippet of code

[Include( "alias", "Email" ), Include( "FirstName", "First" )]
[Include( "LastName", "Last" ), Include( "phone", "Phone" )]
public Bloggers Bloggers;

Notice that I am including four fields: alias, FirstName, LastName, and phone, and while I'm at it I am indicating that alias should be returned as "Email," FirstName as "First," LastName as "Last," and phone with a lowercase P. as Phone with an P.

Having indicated in the metadata what fields of Bloggers to return, I now must modify the get Blogs query to ensure that Bloggers are returned as entities rather than references. To do so open the BlogsService.cs file and navigate to the GetBlogs method, which you will modify as indicated in the next listing

public IQueryable<Blogs> GetBlogs()
{
   return this.Context.Blogs.Include( "Bloggers" );
}

Be sure to rebuild the project.  Note, you may have to temporarily comment out the BlogsService lines in Blogs.xaml.cs)

Modifying Blogs.xaml

Return to Blogs.xaml.cs and make sure that the context there is named BlogsContext or change it to be so. In any case the logic is unchanged from the previous version:, we will instantiate a BlogsContext object, use that to load the results of our now modified GetBlogs query, and assign the results to the DataGrid’s ItemsSource property:

private readonly BlogsContext bc = new BlogsContext();
ublic Blogs()
 
 InitializeComponent();
 bc.Load( bc.GetBlogsQuery() );
 this.blogsDataGrid.ItemsSource = bc.Blogs;
 

Build and run the application

MultiTableRunning
(Click on image to enlarge)

This entire post was dictated and translated by computer; apologies in advance for any resulting absurdities.

Completed Code

Novice PreviousRia Services Part 1 Next: Domain Data Source Control
Advanced PreviousRia Services Part 1 Next:  Domain Data Source Control

Complexity Isn’t Simple

It is very rare for me to respond to comments in follow-on blog post, in fact I don't think I've ever done so before. However the response to my previous blog entry about Dot Net Rocks show number 476 was so overwhelming, and so many good points were made, that I couldn't resist having another go at some of these issues.

Center of Attention

N.B.: I've taken the liberty™ of paraphrasing user comments.  The original comments can be seen here.

Too Much To Keep Up With?

Nate Jackson wrote that he felt, even as a seasoned developer, that the pace at which technology is coming out of Microsoft is too fast for him to reasonably keep up with or even use to provide context for future projects.

My response is that I don't disagree that with so many new technologies it's terribly difficult to keep up. Rather, my position is that I'd rather live in a world where there are constantly improving technologies and run the risk of feeling that I'm falling behind then live in either of the alternative worlds: the one where we forgo rapid technology improvement, or the one in which we prematurely deprecate technology that developers are still using.

Rapid Deprecation?

Guerchele objected that if we continued to advance the technology without deprecating,  we wii inevitably end up with too many technologies. He rejected my idea of a “gizmo" because it does not enforce adoption of the latest technology for new projects

While I embrace the evolution of development technology, the solution cannot be to "forbid" the use of platforms we released only a few years ago. While it is in my own experience that applications that would've taken me months or even years to finish with the technology available five years ago can today be completed in months while providing a more robust and better UI, I have to also recognize that other developers do not necessarily feel the need to move from the technology with which they're most comfortable.

And it may well be that for them the opportunity cost of forgoing the potential in the new technology is outweighed by the benefit of not having to take the time to make that move,  given that they're already fully productive in a technology they know.

No, actually  that’s a lie. If the truth were known, my actual opinion is that the anticipated cost of adopting a new technology is over-estimated by those who feel this way (and under-estimated, I suppose by people like me!); and the real world gains of moving to a new technology like Silverlight are in fact far higher than might be anticipated. But I really do understand that many developers disagree with me.

Full Projects Please

rickj1 wrote that our training and material has suffered from a lack of a “full-featured step-by-step end to end real world tutorial.” I fully agree, which is why I find the touring project very satisfying, though I think it's extremely important to put each component into context and also to point out a generalized set of uses that transcend any individual project.

Note to rickj1, every part of project touring will be fully annotated and all the source code will be available as we move forward.

Others have commented, and once again I agree, that my tutorials and videos would be improved by providing more motivation for each technique; that is,  “don't just show me how to do something, tell me when I might want to do it."

A Few Words From Josh

“Nitwit! Blubber! Oddment! Tweak!" – A.D.

Josh Holmes, RIA evangelist for Microsoft, who was on the original panel, made the following points in a private correspondence(which he has permitted me to copy here):

You’re right, we can’t support the extraordinary train of technologies including everything from Access 3 to.NET 4.0….My biggest frustration with the show was that we never really defined “Complexity” and who things were complex for. I think I tried to make the point at least once that things were getting simpler to create for professional programmers but harder and harder for non-programmers (the so-called hobbyist). Even with the Getting Started posts and the like, you have to be a technologist to write an application these days with Microsoft technologies. This is one of many reasons that we are losing share to technologies such as PHP.

While I agree that we are not fully serving the needs of the so-called hobbyist -- and parenthetically if we mean by that people who program but for whom programming is not their day job, then I think we need a different term; Programming Artisan comes to mind -- I'm not sure there is anything like universal agreement that things are getting simpler for professional programmers.

There's no doubt that with the latest technology, once mastered, we can do things more quickly, better, more simply than we could just a few years ago, however you have to factor into that equation the cost of learning the new technology, and the perceived "complexity" of myriad overlapping choices.

As to the hobbyist, or even the technologist attempting to transfer to .Net,there is no doubt that the learning curve has grown, if not steeper, then at least longer. That said, we all remember how much of the plumbing we used to write, and  few of us want to back.

Josh continues,  The second part of complexity that we never covered is that the software that we build is far too complex for our users. We have all of the opportunity to do something amazing but there are few firms that do. Silverlight and WPF are enabling technologies but unless the devs and designers really push it to its limits, my grandma still can’t use her applications…

It is certainly true that we are a long way from software that is as easy to use as a toaster, but that has been true for a very long time. I would argue that it was worse in the so-called "good old days" before things got so "complex."  In fact, I would argue, we are slowly learning how to make software that is more intuitive, easier-to-use out-of-the-box, and that appeals to a broader audience. While we absolutely have a long way to go, I don't think we should lose sight of the enormous strides that have been made towards more intuitive interfaces in products both from within Microsoft and from numerous other software vendors.

The bottom line for me is that the overall experience of being a programmer requires that you know more than was needed when I started, but that's because we're being asked to build much better products. And given that, I’d far rather have today's tools than the tools I had in the 1980s.

Please note, this post was produced through dictation; my apologies for any absurdities. It turns out that carpal tunnel syndrome is a whole lot less fun than you might expect.

-jesse