Master pages, part V : progressive download of pages with a heavy footprint

by gleblanc 16. February 2009 01:29

In previous posts, we showed how to progressively load (in the background) the different pages of a Silverlight application, drastically reducing the wait time for the user. Here, we go a step further : some pages have a heavy footprint (ie their xap files are big), generally because they need support from one or more dll's (these dll's are then incorporated by Visual Studio into the xap file). 

Here, we are using the Chart control from the Toolkit (http://www.codeplex.com/Silverlight ) in the first page. To do so, we need to add a reference to Microsoft.Windows.Controls.DataVisualization.dll, which is a 280 KB file. Using the progressive download technique, our first page is only 12 KB and is thus displayed immediately after request. 

Background images are paintings from Georgia O'Keeffe, an American artist. 


Project / source code

As usual, we create a Silverlight project (SilverlightApplication5). The default page contains the master page, but not its background image, that will dynamically loaded (ie by program, later on) :

 <Grid>
  <Image x:Name="imgBG" Stretch="UniformToFill"  />
  <Grid x:Name="LayoutRoot" Background="LightGray" >
  .....  here the xaml for master page
  </Grid>
 </Grid>   

The master page contains a grid cell (named PageContainer) that will act as a place-holder for Page1, Page2 and Page3 (one at a time). 

Within the same project (the only one right now), we create Page1 (Add, New Item, Silverlight UserControl). The background image and the Chart control are not included (we just mention a grid cell as place-holder, named PlaceHolderForChart). The master page and Page1 are compiled into the same xap file (SilverlightApplication5.xap, whose size is only 12 KB).

In the solution, we add a new project (right click on the Solution line, Add, New Project), first for Page2 (project named Proj4Page2). Then for Page3 (Proj4Page3). Page2 and Page3 are build as usual. After compilation, Proj4Page2.xap and Proj4Page3.xap are created (10 KB each) in the ClientBin folder.   

To keep data available to and from any page, we create another project (named Proj4Data) in the solution. We keep data as public static fields in Proj4Data.Page.xaml.cs. Upon confirmation in Page2 and Page3 (button Confirm), legends and slider values are saved in fields of Proj4Data (for instance Proj4Data.Page.LegendPage2, of type string).

Now the most interesting part for this post : we create a new project named Proj4Chart. We add a reference to Microsoft.Windows.Controls.DataVisualization.dll (a 280 KB file downloaded with the toolkit). We insert a Chart control in Proj4Chart.xaml :

 <UserControl x:Class="Proj4Chart.Page"
   xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation
   xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml
   xmlns:chart="clr-namespace:Microsoft.Windows.Controls.DataVisualization.Charting;
                assembly=Microsoft.Windows.Controls.DataVisualization"
  >
  <Grid x:Name="LayoutRoot" >
   <StackPanel Orientation="Vertical"  >
    <chart:Chart x:Name="chart" LegendTitle="Legend" BorderThickness="0"
                Width="250" Height="225" >
     <chart:Chart.Series>
      <chart:PieSeries DependentValueBinding="{Binding Path=Value}"
                       IndependentValueBinding="{Binding Path=Key}"
                       BorderThickness="0" />
     </chart:Chart.Series>
    </chart:Chart>
   </StackPanel>
  </Grid>
 </UserControl>

We add a using line in Proj4Chart.xaml.cs, as well as a function (DrawChart) :

 .....
 using Microsoft.Windows.Controls.DataVisualization.Charting;  namespace Proj4Chart
 {
  public partial class Page : UserControl
  {
   public Page()
   {
    InitializeComponent();
    .....    // initialisation of a delegate to DrawChart
   }
   public void DrawChart()
   {
    PieSeries serie = chart.Series[0] as PieSeries;
    serie.ItemsSource = new KeyValuePair<string, int>[] {
      new KeyValuePair<string, int>(Proj4Data.Page.LegendPage2,
                                    Proj4Data.Page.SliderValuePage2),
      new KeyValuePair<string, int>(Proj4Data.Page.LegendPage3,
                                    Proj4Data.Page.SliderValuePage3)
     };
   }
  }
 }

We will explain the initialisation of the delegate to DrawChart at the end of this post.

In the projects named SilverlightApplication5, Proj4Page2, Proj4Page3 and Proj4Chart, we add a reference (right click on the project line, Add Reference, Projects) to Proj4Data (Proj4Data.xap is a 5 KB file). 

In the constructor of the master page (SilverlightApplication5.xaml.cs), we initiate the progressive download (in the background) of (in sequence, in this order) : background image for master page, Proj4Page2.xap, Proj4Page3.xap, Proj4Chart, background image for Page1, background image for Page2 and background image for Page3. These two xap files and these four jpg files must be copied in the ClientBin folder.  

We explained in a previous post how to extract a UserControl out of Proj4Page2.xap and Proj4Page3.xap.

Proj4Chart.xap is a bit more complex since it contains an additional (and huge) file : Microsoft.Windows.Controls.DataVisualization.dll. For confirmation, rename Proj4Chart.xap as Proj4Chart.zip and unzip it. In AppManifest.xaml, you will find an additional line : 
 <AssemblyPart x:Name="Microsoft.Windows.Controls.DataVisualization"
               Source="Microsoft.Windows.Controls.DataVisualization.dll" />

The work for Proj4Chart is not so different (see previous posts) : 

 void client_OpenReadCompleted(object sender, OpenReadCompletedEventArgs e)
 {
  .....
  StreamResourceInfo sri, sri4dll;
  AssemblyPart asmPart, asmPartChart;
  Assembly asm;
  UserControl uc;
  switch (nStep)
  {
   case 1:             // end of load for background image of masterpage
     .....
   case 2:             // end of load of Page2.xap
     .....
   case 3:             // end of load of Page3.xap
     .....
     nStep++;
     // load Proj4Chart.xap (includes Microsoft.Windows.Controls.DataVisualization.dll)
     client.OpenReadAsync(new Uri("Proj4Chart.xap", UriKind.Relative));
     break;
   case 4:             // end of load of Proj4Chart
     sri = new StreamResourceInfo(e.Result, null);
     sri4dll = Application.GetResourceStream(sri,
                  new Uri("Microsoft.Windows.Controls.DataVisualization.dll",
                          UriKind.Relative));
     asmPartChart = new AssemblyPart();
     asmPartChart.Load(sri4dll.Stream);
     sri4dll = Application.GetResourceStream(sri,
                 new Uri("Proj4Chart.dll", UriKind.Relative));
     asmPart = new AssemblyPart();
     asm = asmPart.Load(sri4dll.Stream);
     uc = asm.CreateInstance("Proj4Chart.Page") as UserControl;
     Proj4Data.Page.chart = uc;
     page1.PlaceHolderForChart.Children.Add(uc);
     nStep++;
     // load background image for Page1
     .....
     break;
   case 5:             // end of load for background image for page1
     .....
   case 6:             // end of load of background image for page2
     .....
   case 7:             // end of load of background image for page3
     .....
  }
 }

The xap file is first loaded into sri, of type StreamResourceInfo. From sri, we extract first DataVisualization.dll, that we load in a newly created object (asmPartChart) of type AssemblyPart. It's all, folks! For Proj4Chart.dll (that contains xaml and C# compiled code), nothing is changed from the previous post.

To end this post : how do we call the DrawChart function in Proj4Chart.Page.xam.cs from any page ? In Proj4Data.Page.xaml.cs (that contains shared variables), we declare a delegate :

 public delegate void DrawChartDel();
 public static DrawChartDel fDrawChart;   

A delegate is the C# (type-safe) equivalent of C function pointer, ie a variable that holds the address of a function. It's possible to call the function thru the function pointer. In the first line, we define a type : a variable of type DrawChartDel is (in C parliance) a variable that points to a function that accepts no argument and returns nothing (void). In the second line, we declare such a variable, here named fDrawChart.

Up to now, fDrawChart points to nothing. fDrawChart is initialized in the constructor in Proj4Chart.Page.xaml.cs :

 using Microsoft.Windows.Controls.DataVisualization.Charting; namespace Proj4Chart
 {
  public partial class Page : UserControl
  {
   public Page()
   {
    InitializeComponent();
    Proj4Data.Page.fDrawChart = new Proj4Data.Page.DrawChartDel(DrawChart);
   }
   public void DrawChart()
   {
    .....
   }
  }
 }

To call DrawChart from Page2, we just need to write :

 if (Proj4Data.Page.fDrawChart != null) Proj4Data.Page.fDrawChart(); 

In the forthcoming post, a similar example with a web service request. See you soon. A+, as we now say in the new sms-biaised French language. 

Tags:

Powered by BlogEngine.NET 1.5.0.7
Theme by Mads Kristensen

About the author

Gerard Leblanc is the author of several books (in french) on C++, C#, .NET and Silverlight (Eyrolles, Paris as publisher). See www.gleblanc.eu as the companion web site for these books (included sample programs).
He is Microsoft MVP for Silverlight.

MVP logo