Tuesday, August 17, 2010

On Demand Loading of Assembly in Silverlight

Hi guys I am posting a new article on, on demand loading of assembly in Silverlight applications. Nowadays when you develop application in Silverlight you may not need to load the entire assembly into client side. Instead of that you can download whenever it needs in your application by separating it into separate XAP or DLL files. By doing this the initial downloading size of XAP can be reduced and finally improvement in initial loading of your application. Another very nice feature of this approach is you can invoke function of other DLL or XAP files. Even your can set/get value of property belong to that assembly. In Silverlight this feature is very useful because the library has to be downloaded in to the client machine before start executing.

In this section I am going to explore following things:

1. Reflection (One amazing feature)

2. Downloading Library using Web client

3. Assembly

4. Get all types from assembly

Here I am trying to load a user control one XAP file to another container XAP file. For this I have two Silverlight projects.

1. Controlapp (target user control)

2. Slvdynamicloadingreflection (container application)

Here is the implementation of target user control;

Lets create two folders

1. Model

2. ViewModel

The Model will be having the model class, Following is the code for my model class;

public class Person : INotifyPropertyChanged
{
    private string _FirstName;
    public string FirstName
    {
        get
        {
            return _FirstName;
        }
        set
        {
            _FirstName = value;
            NotifyPropertyChanged("FirstName");
        }
    }
    private string _LastName;
    public string LastName
    {
        get
        {
            return _LastName;
        }
        set
        {
            _LastName = value;
            NotifyPropertyChanged("LastName");
        }
    }
    #region INotifyPropertyChanged Members
 
    public event PropertyChangedEventHandler PropertyChanged;
    private void NotifyPropertyChanged(String info)
    {
        if (PropertyChanged != null)
        {
            PropertyChanged(this, new PropertyChangedEventArgs(info));
        }
    }
    #endregion
}

In above code snippet I have a class called Person with two properties FirstName and LastName, basic person information;


Once you created the model lets create the ViewModelPerson.cs




public class ViewModelPerson : INotifyPropertyChanged
{
    private Person _Person;
    public Person Person
    {
        get
        {
            return _Person;
        }
        set
        {
            _Person = value;
            NotifyPropertyChanged("Person");
        }
    }
    public ViewModelPerson()
    {
        Person = new Person
        {
            FirstName = "",
            LastName = ""
        };
    }
 
    public void Save()
    {
        MessageBox.Show("Method Invoked");
    }
 
    #region INotifyPropertyChanged Members
    public event PropertyChangedEventHandler PropertyChanged;
    private void NotifyPropertyChanged(String info)
    {
        if (PropertyChanged != null)
        {
            PropertyChanged(this, new PropertyChangedEventArgs(info));
        }
    }
    #endregion
}

In the MainPage.xaml add the following code


In viewmodel I have a function save to save the user information. For the time being am not adding any implementation for save function. Our primary aim is to invoke this function from our container application.


So initialize the ViewModel Class in mainpage.xaml.cs file.




public partial class MainPage : UserControl
{
    public ViewModelPerson VMPerson { get; set; }
    public MainPage()
    {
        InitializeComponent();
        VMPerson = new ViewModelPerson();
        this.DataContext = VMPerson.Person;
    }
 
    public void SaveData(object[] objArray)
    {
        VMPerson.Save();
    }
}

Here is the implementation for container application.


Once you done with the user control to be loaded. You have to copy the controlapp.XAP file to web project, that is our third project.


untitled


Now we will start working on the container application.


In Mainpage.caml.cs file contructor download the controlapp.XAP file using the WebClient.


Following is the code snippet;




public MainPage()
{
    InitializeComponent();
    WebClient wClient = new WebClient();
    wClient.OpenReadCompleted += (sender, e) =>
    {
        if ((e.Error == null) && (e.Cancelled == false))
        {
            StreamResourceInfo sRIXap = new StreamResourceInfo(e.Result, null);
            StreamResourceInfo gRSStream = Application.GetResourceStream(sRIXap, new Uri("controlapp.dll", UriKind.Relative));
            AssemblyPart aPart = new AssemblyPart();
            _assembly = aPart.Load(gRSStream.Stream);
            _objectControlApp = _assembly.CreateInstance("controlapp.MainPage") as UserControl;
            OnDemandUserControl.Children.Add(_objectControlApp);
        }
    };
    wClient.OpenReadAsync(new Uri("http://localhost:1105/" + "ClientBin/controlapp.xap"));
}

Use the webslient’s OpenReadAsync method to download the XAP file from the server. In the completed method you have to load the assembly in a assembly variable. Create instance of the class (MainPage) using CreateInstance method of assembly. And finally add the control to stackpanel “OnDemandUserControl”.


So now you got the idea of how to load a external xap/dll in to your application. The next step is to understand how you can access or call a function in that dll / xap file. Lets make it working.


I have added a button in my container application. Here is the code for the button click event handler;




Type[] types = _assembly.GetTypes();
foreach (Type t in types)
{
    MethodInfo[] mInfos = t.GetMethods();
    foreach (MethodInfo minf in mInfos)
    {
        if (minf.Name == "SaveData")
        {
            minf.Invoke(_objectControlApp, null);
        }
    }
}



Above code is to find the function named SaveData and execute it.


minf.Invoke(_objectControlApp, null);


The Invoke function will accept the object you created and the parameters as object array. I do not have any parameter to be supplied, so I am passing null.


Even though this is a very small article, you can use this idea to all of your project. I have found this is a very useful technique every Dot Net programmers should  know.


You guys can download the code here


Thanks Everybody,


HAPPY CODING!!!


Anilal Sambasivan

Thursday, August 5, 2010

Silverlight Image Gallery Using MVVM and SQL Server Database

sample

Hi guys, here I am posting a new article for creating a Image Gallery Using MVVM and SQL Server Database.

Create new Silverlight project in Visual studio (I am using silverlight 3.0 with Visual studio 2008 with SP1). My project name is MVVM_WCF_Silverlight_ImageUpload.

Server Side Implementation

Create a table named image in one of your SQL Server database using the following script:

   1: CREATE TABLE [dbo].[image](
   2:     [imagesid] [int] IDENTITY(1,1) NOT NULL,
   3:     [imagecontent] [image] NULL,
   4:     [imagename] [varchar](50) NULL,
   5:  CONSTRAINT [PK_image] PRIMARY KEY CLUSTERED 
   6: (
   7:     [imagesid] ASC
   8: )WITH (PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS  = ON, ALLOW_PAGE_LOCKS  = ON) ON [PRIMARY]
   9: ) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]

Create three folders in MVVM_WCF_Silverlight_ImageUpload project


  1. Data
  2. Services
  3. DTO

Add a LINQ to SQL Classes named ImageDataClasses.dbml in your data folder.

Go to Server explorer and connect to the database containing table image.

Once it is connected, drag and drop the image table to the ImageDataClasses.dbml work space.

Add a WCF service file to your Services folder, I have given WCFImageService.svc as name.

Go to your web.config file and change binding="wsHttpBinding" to binding="basicHttpBinding"

I have defined a custom basicHttpBinding for my project add this code inside binding, following is the code for that:



   1: <basicHttpBinding>
   2:     <binding name="imageBinding" maxReceivedMessageSize="2000000" maxBufferSize="2000000">
   3:         <readerQuotas maxArrayLength="2000000" maxStringContentLength="2000000"/>
   4:     </binding>
   5: </basicHttpBinding>

Change the binding configuration as follows in your endpoint tag



   1: <endpoint address="" binding="basicHttpBinding" bindingConfiguration="imageBinding" contract="MVVM_WCF_Silverlight_ImageUpload.Web.Services.IWCFImageService">

I have defined a Data Type Object class called Picture.



   1: public class Picture
   2: {
   3:     public int ImageID { get; set; }
   4:     public string ImageTitle { get; set; }
   5:     public byte[] ImageStream { get; set; }
   6: }

Following is the code for my service project:

WCFImageService.cs



   1: public class WCFImageService : IWCFImageService
   2: {
   3:     public bool Upload(Picture imageFile)
   4:     {
   5:         using (ImageDataClassesDataContext db = new ImageDataClassesDataContext())
   6:         {
   7:             db.images.InsertOnSubmit(new image { imagecontent = imageFile.ImageStream, imagename = imageFile.ImageTitle });
   8:             try
   9:             {
  10:                 db.SubmitChanges();
  11:             }
  12:             catch (Exception ex)
  13:             {
  14:                 db.SubmitChanges();
  15:             }
  16:             return true;
  17:         }
  18:     }
  19:  
  20:     public List<Picture> ListImages()
  21:     {
  22:         using (ImageDataClassesDataContext db = new ImageDataClassesDataContext())
  23:         {
  24:             List<image> selectedImages = db.images.ToList<image>();
  25:             List<Picture> pictures = new List<Picture>();
  26:             if (selectedImages.Count > 0)
  27:                 foreach (image img in selectedImages)
  28:                     pictures.Add(new Picture { ImageID = img.imagesid, ImageTitle = img.imagename, ImageStream = img.imagecontent.ToArray() });
  29:             return pictures;
  30:         }
  31:     }
  32:  
  33:     public Picture Download(int imageId)
  34:     {
  35:         using (ImageDataClassesDataContext db = new ImageDataClassesDataContext())
  36:         {
  37:             image selectedImage = db.images.Where(r => r.imagesid == imageId) as image;
  38:             return new Picture { ImageID = selectedImage.imagesid, ImageTitle = selectedImage.imagename, ImageStream = selectedImage.imagecontent.ToArray() };
  39:         }
  40:     }
  41: }

WCFImageService.svc.cs



   1: [ServiceContract]
   2: public interface IWCFImageService
   3: {
   4:     [OperationContract]
   5:     bool Upload(Picture imageFile);
   6:  
   7:     [OperationContract]
   8:     List<Picture> ListImages();
   9:  
  10:     [OperationContract]
  11:     Picture Download(int imageId);
  12: }

Client Side Implementation


If you are done with the server side add the service reference in the silverlight project.

Right click on add reference => Add Service Reference => Click on Discover, Once the service is listed, select the service and give a name for your reference. I have given WCFImageService.

As I am using MVVM pattern in my project, Lets create 3 folders in Silverlight project


  1. Converters
  2. Model
  3. ViewModel
  4. View

In Converters you will be having one value converter classes for converting the bytes to bitmap image.

Following is the code for ImageConverter.cs



   1: public class ImageConverter : IValueConverter
   2: {
   3:    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
   4:    {
   5:        BitmapImage bi = new BitmapImage();//create bitmap image
   6:        if (value != null)
   7:        {
   8:            if (value != null)
   9:                bi.SetSource(new MemoryStream((Byte[])value));
  10:  
  11:            return bi;
  12:        }
  13:        return bi;
  14:    }
  15:  
  16:    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
  17:    {
  18:        throw new NotImplementedException("PropertyViewer does not use ConvertBack.");
  19:    }
  20: }

In model create a model class file called Picture.cs. Following is the code for my model class:



   1: public class Picture : INotifyPropertyChanged
   2: {
   3:     #region Private Variables
   4:     private string _imageTitle;
   5:     private byte[] _imageStream;
   6:     #endregion
   7:  
   8:     #region Public Properties
   9:     public int ImageID { get; set; }
  10:     public string ImageTitle
  11:     {
  12:         get
  13:         {
  14:             return _imageTitle;
  15:         }
  16:         set
  17:         {
  18:             _imageTitle = value;
  19:             NotifyPropertyChanged("ImageTitle");
  20:         }
  21:     }
  22:     public byte[] ImageStream
  23:     {
  24:         get
  25:         {
  26:             return _imageStream;
  27:         }
  28:         set
  29:         {
  30:             _imageStream = value;
  31:             NotifyPropertyChanged("ImageStream");
  32:         }
  33:     }
  34:     public ICommand Click { get; set; }
  35:     #endregion
  36:  
  37:     #region INotifyPropertyChanged Members
  38:  
  39:     public event PropertyChangedEventHandler PropertyChanged;
  40:     private void NotifyPropertyChanged(String propertyName)
  41:     {
  42:         if (PropertyChanged != null)
  43:         {
  44:             PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
  45:         }
  46:     }
  47:  
  48:     #endregion
  49: }

In View Model folder create a class called ViewModelRTV.cs, here you can implement your logic for populating the tree from the database. Following is the code for the view model class:



   1: public class ViewModelImageUpload : INotifyPropertyChanged
   2: {
   3:     private Picture _pictureFile;
   4:     public Picture PictureFile
   5:     {
   6:         get
   7:         {
   8:             return _pictureFile;
   9:         }
  10:         set
  11:         {
  12:             _pictureFile = value;
  13:             NotifyPropertyChanged("PictureFile");
  14:         }
  15:     }
  16:  
  17:     private Picture _selectedItem;
  18:     public Picture SelectedItem
  19:     {
  20:         get
  21:         {
  22:             return _selectedItem;
  23:         }
  24:         set
  25:         {
  26:             _selectedItem = value;
  27:             NotifyPropertyChanged("SelectedItem");
  28:         }
  29:     }
  30:  
  31:     private ObservableCollection<Picture> _pictureList;
  32:     public ObservableCollection<Picture> PictureList
  33:     {
  34:         get
  35:         {
  36:             return _pictureList;
  37:         }
  38:         set
  39:         {
  40:             _pictureList = value;
  41:             NotifyPropertyChanged("PictureList");
  42:         }
  43:     }
  44:  
  45:     public ICommand CommandClick { get; set; }
  46:     public ICommand Click { get; set; }
  47:  
  48:     public ViewModelImageUpload()
  49:     {
  50:         PictureFile = new Picture
  51:         {
  52:             ImageTitle = "Image Not Selected",
  53:             ImageStream = null
  54:         };
  55:         PictureList = new ObservableCollection<Picture>();
  56:         WCFImageService.WCFImageServiceClient client = new WCFImageService.WCFImageServiceClient();
  57:         client.ListImagesCompleted += client_ListImagesCompleted;
  58:         client.ListImagesAsync();
  59:         CommandClick = new DelegateCommand<object>(argument =>
  60:                        {
  61:                            switch (argument.ToString())
  62:                            {
  63:                                case "Browse":
  64:                                    BrowseFile();
  65:                                    break;
  66:                                case "Upload":
  67:                                    UploadFile();
  68:                                    break;
  69:                                default:
  70:                                    break;
  71:                            }
  72:                        });
  73:     }
  74:  
  75:     void client_ListImagesCompleted(object sender, MVVM_WCF_Silverlight_ImageUpload.WCFImageService.ListImagesCompletedEventArgs e)
  76:     {
  77:         PictureList = new ObservableCollection<Picture>();
  78:         if (e.Result != null)
  79:         {
  80:             ObservableCollection<WCFImageService.Picture> lstPics = e.Result as ObservableCollection<WCFImageService.Picture>;
  81:             foreach (WCFImageService.Picture pic in lstPics)
  82:                 PictureList.Add(new Picture
  83:                     {
  84:                         ImageID = pic.ImageID,
  85:                         ImageTitle = pic.ImageTitle,
  86:                         ImageStream = pic.ImageStream,
  87:                         Click = new DelegateCommand<object>(obj =>
  88:                                 {
  89:                                     if (obj != null)
  90:                                         SelectedItem = obj as Picture;
  91:                                 })
  92:                     });
  93:             if (PictureList != null && PictureList.Count > 0)
  94:                 SelectedItem = PictureList[0];
  95:         }
  96:     }
  97:  
  98:  
  99:  
 100:  
 101:     private void BrowseFile()
 102:     {
 103:         OpenFileDialog openFileDialog = new OpenFileDialog { Filter = "JPEG files|*.jpg" };
 104:         if (openFileDialog.ShowDialog() == true)
 105:         {
 106:             using (Stream stream = (Stream)openFileDialog.File.OpenRead())
 107:             {
 108:                 byte[] bytes = new byte[stream.Length];
 109:                 stream.Read(bytes, 0, (int)stream.Length);
 110:                 PictureFile.ImageTitle = openFileDialog.File.Name;
 111:                 PictureFile.ImageStream = bytes;
 112:             }
 113:         }
 114:     }
 115:  
 116:     private void UploadFile()
 117:     {
 118:         WCFImageService.WCFImageServiceClient client = new WCFImageService.WCFImageServiceClient();
 119:         client.UploadCompleted += client_UploadCompleted;
 120:         client.UploadAsync(new WCFImageService.Picture { ImageTitle = PictureFile.ImageTitle, ImageStream = PictureFile.ImageStream });
 121:     }
 122:  
 123:     void client_UploadCompleted(object sender, WCFImageService.UploadCompletedEventArgs e)
 124:     {
 125:         if (e.Error == null)
 126:         {
 127:             WCFImageService.WCFImageServiceClient client = new WCFImageService.WCFImageServiceClient();
 128:             client.ListImagesCompleted += client_ListImagesCompleted;
 129:             client.ListImagesAsync();
 130:             MessageBox.Show("File Uploaded!!!");
 131:         }
 132:     }
 133:  
 134:     #region INotifyPropertyChanged Members
 135:  
 136:     public event PropertyChangedEventHandler PropertyChanged;
 137:     private void NotifyPropertyChanged(String propertyName)
 138:     {
 139:         if (PropertyChanged != null)
 140:         {
 141:             PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
 142:         }
 143:     }
 144:     #endregion
 145: }

In View create a Silverlight user control named UCImage.xaml, here you will the define the UI for your application in XAML. Following is the xaml code for the View:



   1: <UserControl x:Class="MVVM_WCF_Silverlight_ImageUpload.View.UCImage"
   2:     xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
   3:     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
   4:     xmlns:commands="clr-namespace:Microsoft.Practices.Composite.Presentation.Commands;assembly=Microsoft.Practices.Composite.Presentation"
   5:     xmlns:converter="clr-namespace:MVVM_WCF_Silverlight_ImageUpload.Converters"
   6:     Width="730" Height="370">
   7:     <UserControl.Resources>
   8:         <converter:ImageConverter x:Key="ImageConverter" />
   9:     </UserControl.Resources>
  10:     <Grid x:Name="LayoutRoot" Background="White">
  11:         <Border Padding="5" Background="#FFD6D6D6" CornerRadius="5" OpacityMask="Black" BorderThickness="1">
  12:             <StackPanel Orientation="Horizontal">
  13:                 <Grid>
  14:                     <Grid.ColumnDefinitions>
  15:                         <ColumnDefinition></ColumnDefinition>
  16:                         <ColumnDefinition></ColumnDefinition>
  17:                     </Grid.ColumnDefinitions>
  18:                     <StackPanel Grid.Column="0">
  19:                         
  20:                             <ListBox ItemsSource="{Binding Path=PictureList, Mode=TwoWay}" 
  21:                      SelectedItem="{Binding Path=SelectedItem, Mode=TwoWay}"
  22:                      ScrollViewer.VerticalScrollBarVisibility="Auto"
  23:                                      MaxHeight="310"
  24: >
  25:                                 <ListBox.ItemTemplate>
  26:                                     <DataTemplate>
  27:                                         <Button commands:Click.Command="{Binding Click}" 
  28:                                 commands:Click.CommandParameter="{Binding}"
  29:                                 Background="Transparent" 
  30:                                 BorderThickness="0">
  31:                                             <Border Padding="5" Background="#FF0D253D" CornerRadius="5" OpacityMask="Black" BorderThickness="1" Width="170" >
  32:                                                 <Border.Effect>
  33:                                                     <DropShadowEffect BlurRadius="7" Opacity="0.5" ShadowDepth="3"/>
  34:                                                 </Border.Effect>
  35:                                                 <StackPanel Orientation="Horizontal">
  36:                                                     <Border Padding="5" Background="#FF8AB8E6" CornerRadius="5" OpacityMask="Black" BorderThickness="1">
  37:                                                         <Image Width="50" Height="40" 
  38:                        Source="{Binding Path=ImageStream, Mode=TwoWay, Converter={StaticResource ImageConverter}}" 
  39:                        Stretch="Uniform" 
  40:                        HorizontalAlignment="Center" ></Image>
  41:                                                     </Border>
  42:                                                     <TextBlock Text="{Binding Path=ImageTitle, Mode=TwoWay}" 
  43:                                                                HorizontalAlignment="Center" 
  44:                                                                VerticalAlignment="Center" 
  45:                                                                Foreground="White" 
  46:                                                                FontWeight="Bold" 
  47:                                                                FontSize="10.667"></TextBlock>
  48:                                                 </StackPanel>
  49:                                             </Border>
  50:                                         </Button>
  51:                                     </DataTemplate>
  52:                                 </ListBox.ItemTemplate>
  53:                             </ListBox>
  54:                         
  55:                     </StackPanel>
  56:                     <StackPanel Orientation="Vertical" Grid.Column="1">
  57:                         <StackPanel Orientation="Vertical">
  58:                             <Border Padding="5" Background="#FFFFFF" CornerRadius="5" OpacityMask="Black" BorderThickness="1" BorderBrush="#FF5D5D5D">
  59:                                 <Image Width="500" Height="300" 
  60:                        Source="{Binding Path=SelectedItem.ImageStream, Mode=TwoWay, Converter={StaticResource ImageConverter}}" 
  61:                        Stretch="Uniform" 
  62:                        HorizontalAlignment="Center" ></Image>
  63:                             </Border>
  64:                             <TextBlock Text="{Binding Path=SelectedItem.ImageTitle, Mode=TwoWay}" 
  65:                                                                HorizontalAlignment="Center" 
  66:                                                                Foreground="#000000" 
  67:                                                                FontWeight="Bold" 
  68:                                                                FontSize="10.667">
  69:                             </TextBlock>
  70:                         </StackPanel>
  71:                         <StackPanel Orientation="Horizontal">
  72:                             <Button Content="Browse File" 
  73:                         commands:Click.Command="{Binding CommandClick}" 
  74:                         commands:Click.CommandParameter="Browse" 
  75:                         Width="100" Margin="5,5,5,5"></Button>
  76:                             <Button Content="Upload File" 
  77:                         commands:Click.Command="{Binding CommandClick}" 
  78:                         commands:Click.CommandParameter="Upload" 
  79:                         Width="100" Margin="5,5,5,5"></Button>
  80:                         </StackPanel>
  81:                     </StackPanel>
  82:                 </Grid>
  83:             </StackPanel>
  84:         </Border>
  85:     </Grid>
  86: </UserControl>

Include the UCImage.xaml in MainPage.xaml. Following is the code for MainPage.xaml:



   1: <UserControl x:Class="MVVM_WCF_Silverlight_ImageUpload.MainPage"
   2:     xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
   3:     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
   4:     xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
   5:     xmlns:local="clr-namespace:MVVM_WCF_Silverlight_ImageUpload.View"
   6:     mc:Ignorable="d" d:DesignWidth="640" d:DesignHeight="480">
   7:   <Grid x:Name="LayoutRoot">
   8:         <local:UCImage></local:UCImage>
   9:     </Grid>
  10: </UserControl>

Initialize your view model and set the data context of view in MainPage.xaml.cs. Following is the code for MainPage.xaml.cs:



   1: ViewModelImageUpload _vmiu;
   2: public MainPage()
   3: {
   4:     _vmiu = new ViewModelImageUpload();
   5:     InitializeComponent();
   6:     this.DataContext = _vmiu;
   7: }

Press F5………. You are done!!!!


Click here to find the complete source code for this article.


If you find any problem, please feel free to ask questions!!!


Thanks Guys!!!