Tuesday, October 28, 2008

Silverlight 2 : Download Contents on Demand

First of all, wishes to all my Blog readers..”Happy Diwali and Wonderful upcoming New Year”

This week was full of fun and joy..well Windows 7 is getting ready to public today !! My Best wishes to Windows Team for their Hard Work throughout.

Well,coming back to Silverlight 2, we saw several demos on media and images, This post will help you to not only set Source of Media and images dynamically but also you can do this with cross domain, Its not very difficult but can turn into nightmare if not done systematically.

Aim of this article to reduce XAP in size by not to adding each and everything as content,Media server I am not sure each and everyone will go and buy, So another good option is to host them on IIS with creating virtual directory with videos and pictures. I am not sure about performance comparison between IIS and Media Server though, It just another tip and trick to make things simple to understand,develop and maintain.

Step 1 : Download Contents

For calling contents like Videos and images following code will work :

namespace SLDownLoadProgress
{
    public partial class Page : UserControl
    {
        public Page()
        {
            InitializeComponent();
            this.Loaded += new RoutedEventHandler(Page_Loaded);
        }

        void Page_Loaded(object sender, RoutedEventArgs e)
        {
            WebClient ws = new WebClient();
            ws.OpenReadCompleted += new OpenReadCompletedEventHandler(ws_OpenReadCompleted);
            ws.OpenReadAsync(new Uri("http://localhost:1425/Videos/numbers.wmv"));
            ws.DownloadProgressChanged += new DownloadProgressChangedEventHandler(ws_DownloadProgressChanged);

        }

       void ws_DownloadProgressChanged(object sender, DownloadProgressChangedEventArgs e)
        {
            try
            {
                MyProg.Value = (double)e.ProgressPercentage;
                Mystk.Visibility = Visibility.Collapsed;
            }
            catch (Exception ex)
            {
                HtmlPage.Window.Alert(ex.InnerException.ToString()); 
            }           
        }

        void ws_OpenReadCompleted(object sender, OpenReadCompletedEventArgs e)
        {
            try
            {
                StreamResourceInfo srcinfo = new StreamResourceInfo(e.Result as Stream, null);
                MyVid.SetSource(srcinfo.Stream);
                MyVid.Play();
            }
            catch (Exception ex)
            {
                HtmlPage.Window.Alert(ex.InnerException.ToString());
            }  
        }
    }
}

Observe carefully following things :

  • I have fixed Port here as 1425, Its good thing to do when you are trying it locally, Developer need to Understand Absolute and Relative path well, “AG_E_NETWORK_ERROR” which is common error, remember that it is always due to irrelevant Path.
  • I am handling two events over here as OpenReadCompleted()[ This is for actual logic implementation] and another one is DownloadProgressChanged() [ This is for keeping track of amount of download]
  • I am using StreamResourceInfo for streaming and for Images you might need to Cast images while downloading content with help of BitMapImage instance.

As far as Cross-domain downloading is concern or let it be within same IIS instance with different virtual directories, Since Silverlight is client side technology though it will not allow you direct access to data or drive any way, so to address this issue you need a XML file named as “clientaccesspolicy.xml” ,content of this file will be like :

<?xml version="1.0" encoding="utf-8" ?>
<access-policy>
  <cross-domain-access>
    <policy>
      <allow-from http-request-headers="*">
        <domain uri="*"/>
      </allow-from>
      <grant-to>
        <resource path="/" include-subpaths="true"/>
      </grant-to>
    </policy>
  </cross-domain-access>
</access-policy>

Some of my friends who blog on silverlight had mention that this file is not needed,practically I don’t belive this, so I will suggest you to have this in your both application, The application which is demanding and application which is providing the contents. This is very useful also in cases like transfers between http and https, so its good practice to have this file.

Step 2 : Show Download Progress by Silverlight 2 ProgressBar controls and other techniques like showing “Loading…” images.

XAML Code : [Here you have 2 options, either show progress by progress bar or show Loading Image.

<Grid x:Name="LayoutRoot" Background="White">
    <StackPanel x:Name="Mystk">
        <TextBlock x:Name="Mytxt" Text="Downloading..." Canvas.Left="100"/>
       <ProgressBar x:Name="MyProg" Height="25" Minimum="0" Maximum="100" />
        <!--<Image x:Name="Loader" Source="http://localhost:1425/Images/loader.jpg" Height="100"/>-->
    </StackPanel>
    <StackPanel>
        <MediaElement x:Name="MyVid"/>
    </StackPanel>
</Grid>

As I said above you can go for image also, I have commented that particular code, if you want you can put it on.

Managed C# code for this :

Again for showing the delay you can either create instance of StoryBoard and implement timer kind of functionality or you can use one thread in sleep mode with some specific delay.

Here download completed part will take care of Progress, You can see both the techniques in code below [Well, you need to decide and separate out each as per your requirement specification]

public partial class Page : UserControl
    {
        Storyboard timer = new Storyboard();

        public Page()
        {
            InitializeComponent();
            this.Loaded += new RoutedEventHandler(Page_Loaded);
        }

        void Page_Loaded(object sender, RoutedEventArgs e)
        {
            WebClient ws = new WebClient();
            ws.OpenReadCompleted += new OpenReadCompletedEventHandler(ws_OpenReadCompleted);
            ws.OpenReadAsync(new Uri("http://localhost:1425/Videos/numbers.wmv"));
            ws.DownloadProgressChanged += new DownloadProgressChangedEventHandler(ws_DownloadProgressChanged);

            timer.Duration = TimeSpan.FromSeconds(50);
            timer.Completed += new EventHandler(timer_Completed);
            timer.Begin();

        }

        void timer_Completed(object sender, EventArgs e)
        {
            if (MyProg.Value <= MyProg.Maximum)
            {
                MyProg.Value += 1;
                timer.Begin();
            }
        }

        void ws_DownloadProgressChanged(object sender, DownloadProgressChangedEventArgs e)
        {
            try
            {
                MyProg.Value = (double)e.ProgressPercentage;
                Mystk.Visibility = Visibility.Collapsed;
            }
            catch (Exception ex)
            {
                HtmlPage.Window.Alert(ex.InnerException.ToString()); 
            }           
        }

        void ws_OpenReadCompleted(object sender, OpenReadCompletedEventArgs e)
        {
            try
            {
                StreamResourceInfo srcinfo = new StreamResourceInfo(e.Result as Stream, null);
                MyVid.SetSource(srcinfo.Stream);
                MyVid.Play();
            }
            catch (Exception ex)
            {
                HtmlPage.Window.Alert(ex.InnerException.ToString());
            }  
        }
    }

If you feel this is tricky and difficult,life can be made easy by putting this silverlight control in update panel and implement Update Progress to show that its “Loading..” with some image.

For such technique you can refer my old article :

http://pendsevikram.blogspot.com/2008/03/implementing-ajax-with-updateprogress.html

I hope this will ease your job and helpful for you to manage contents of silverlight on demand especially when they are large in size.

Vikram.

No comments: