Professional, Software

Pax-Reflector Plugin

Technorati Tags: ,

Logical progression for PAX was build some kind of UI around it. I thought of writing my UI but then I realized the building  a UI for assembly parts and letting people look at the code inside it is not a small task and there is already a tool for it i.e. .Net Reflector. There is even nice Add-in model that lets you extend reflector functionality. Here are few things I found that were helpful for me to create this add-in.

This is add-in is pretty simple. It adds a menu item to File menu. Open Xap menu launches a OpenFileDialog that lets user pick a Xap file that they would like to see.

Fileopen

When user picks the file, it opens the xap in tree view structure. User can then expand them to see individual files inside it.

treeview expandedtreeview

User can click on some of the files to see the content. E.g. for Xaml File text is shown. For image, image is shown and for Media files, media is played.

xamlview mediaview

Here is the source code for the project.

Standard
Professional, Software

Pax

Silverlight app file (.XAP) is essentially a zip file. For testing purposes, many times we needed to look inside the XAP file from customers to see what was causing an issue.

It is possible to day to see what exactly is inside the .Xap file today but it involves few  steps….

  1. Extract all the contents out of Zip file
  2. you should be having bunch of resource files (.mpg, jpg, .wmv etc) and assemblies (.dll) and Application manifest (app.xaml)
  3. Then most of the pages etc are embedded in the assembly so now you can use one of the tools (such as ILDASM) to extract resources out of there to see what is embedded in side assembly

In short it was kind of painful process. I wanted to write some utility assembly that exposes all this information out of Xap in a easier way. These utility APIs could be used to build UI around it.

[Before we get into details of PAX, big Thanks to Eduardo for helping me cleanup the code and fix some of the issues]

There are two DLLs….

ArchiveModel

This DLL contains classes and APIs that read the Zip (Deflate) format that is used for creating .Xap files for silverlight. It has following classes

image

Archive

This class represents a zip file. It exposes the Files collection that are inside zip as well as ReadFiles method that reads individual files (stored in Files Collection).

ArchiveFile

This class represents individual files inside Zip file. Every ArchiveFile has ArchiveFileHeader as well as method ReadFile to read the file. ReadFile method is actuall what decompresses the content using DeflateStream class.

ArchiveFileHeader

Each file in the zip format has a header. Header contains lot of information that is used to read the actual content of the file. Header exposes properties such as Compressed/Uncompressed size, last modified date, filename etc

ByteReader

This is a utility class that converts the array of Bytes into types that are needed for PAX.

XapModel

This DLL that contains classes and APIs that introduces the concept of XAP. e.g. Every XAP has a manifest file, it can contain assemblies and resources etc. It has following classes

image

image

XapArchive

This class represents individual XAP file. It exposes Files collection that contains individual files inside Xap. It also exposes property for manifest.

File and its Sub classes

This class represents generic file inside a xap. File is a base class for different type of files (assemblyfile, manifestfile, xamlfile etc). File can be inside xap or inside assembly. Currently only ManifestFile class implements validation logic using Schemas but in general every file class could have some validation logic. That is the reason why VideoFile/AudioFile classes exists but are empty today.

FileProperties

This class represents the metadata about a file in xap such as  compressed size, uncompressed size etc

Use of Pax

Here is a very simple way I can use the PAX to list the properties of files inside a given xap.

using System;
using XapModel;
using System.IO;

namespace PaxConsoleTest
{
    class Program
    {
        static XapArchive _xap;

        static void Main(string[] args)
        {
            using (FileStream fs = new FileStream("TestPax.xap", FileMode.Open))
            {
                _xap = new XapArchive(fs);
                WriteFileProperties(_xap.Manifest);
                foreach (XapModel.FileTypes.File file in _xap.Files)
                {
                    WriteFileProperties(file);
                    if (file is XapModel.FileTypes.AssemblyFile)
                    {
                        foreach (XapModel.FileTypes.File embeddedfile in 
((XapModel.FileTypes.AssemblyFile)file).Resources) { WriteFileProperties(embeddedfile); } } } } } private static void WriteFileProperties(XapModel.FileTypes.File file) { Console.WriteLine(); Console.WriteLine("************************************"); Console.WriteLine( "File Name: " + file.FileProperties.FileName + " Last Modified: " + file.FileProperties.LastModified); Console.WriteLine( "Compressed Size: " + file.FileProperties.CompressedSize + " Uncompressed Size: " + file.FileProperties.UncompressedSize); Console.Write("************************************"); Console.WriteLine(); } } }

which produces output such as

************************************

File Name: AppManifest.xaml         Last Modified: 10/22/2008 10:06:14 PM

Compressed Size: 300         Uncompressed Size: 337

************************************

************************************

File Name: TestPax.dll         Last Modified: 10/22/2008 10:06:14 PM

Compressed Size: 21189226         Uncompressed Size: 21720064

************************************

************************************

File Name: TestPax.g.resources/page.xaml         Last Modified: 1/1/0001 12:00:00 AM

Compressed Size: 0         Uncompressed Size: 286

************************************

************************************

File Name: TestPax.g.resources/asmembedded/asmembeddedvideowmv.wmv         Last Modified: 1/1/0001

2:00:00 AM

Compressed Size: 0         Uncompressed Size: 4045744

************************************

************************************

File Name: TestPax.g.resources/asmembedded/asmembdimgjpeg.jpeg         Last Modified: 1/1/0001 12:

:00 AM

Compressed Size: 0         Uncompressed Size: 3495

Technorati Tags: ,,,

Here is the link to code to code for Pax

Standard
Professional, Software

Silverlight 2 RTW…

Today we release Silverlight 2. Whole bunch of people are blogging about whole bunch of features and I don’t think I need to cover it any further. Personally for me, this project has been very special and I thought I would share a picture of myself and Sujal taken during shipping.

  1. First picture was taken on 10/3/2008 at around 4:00 AM while we were working on a train. This was few days we before locked down.
  2. Second picture was taken after we had locked down for excrow and we both had time to get a haircut.
  3. Third picture was taken yesterday after we shipped!!

beforeship aftership

threeamigos

Standard
Professional, Software

OnItemsChanged

ItemsControl class in Silverlight Beta2 has added  protected virtual method OnItemsChanged. This method is invoked when Items Property changes. This could be used to add a custom behavior when items are added or removed. Here is the quick sample.

I wanted Listbox where when a selected item is removed, it selects the next item in the list.

CustomListBox

 

   1: public class MyListBox: ListBox
   2:     {
   3:         protected override void OnItemsChanged(System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
   4:         {
   5:             base.OnItemsChanged(e);
   6:             if (e.Action == System.Collections.Specialized.NotifyCollectionChangedAction.Remove)
   7:             {
   8:                 if (e.OldStartingIndex < Items.Count)
   9:                     SelectedItem = Items[e.OldStartingIndex];
  10:                 else
  11:                     SelectedItem = Items[Items.Count - 1];
  12:             }
  13:         }
  14:     }

Here is the sample that compares the behavior of default listbox and customlist box.

Standard
Professional, Software

Beta2 DataBinding

Here is a quick sample to demonstrate some of the new functionality expose in Silverlight Beta2.

  1. Validation support: This is exposed as an BindingValidationError event on FrameworkElement and fires when the data source throws an exception. Currently silverlight only support Validation on exception
  2. Fallback support for IValueConverter: When ValueConverter comes across a value that can not be converted it could return DependencyProperty.UnsetValue which reverts the target DependencyProperty to default

In this sample, there is a simple form that binds data (Person object) to a form that has First Name, Last Name and Age. Age is bound to background of form as well as a TextBox that lets user enter the value for Age.

  1. If age is set to be negative then data object throws an exception. This is used to set the background of TextBox to indicate error
  2. Age is also used to set the background color of the form using ValueConverter and background is green if age is greater than 25. If age is less than 25 but not 0 then background is set to be gray. If the age is set to be 0 then value converter returns DependencyProperty.UnsetValue.

Person

This is the data source and implements INotifyPropertyChanged to support databinding.

   1: public class Person: INotifyPropertyChanged
   2: {
   3:     private string _firstname;
   4:     private string _lastname;
   5:     private int _age;
   6:  
   7:     public event PropertyChangedEventHandler PropertyChanged;
   8:  
   9:     public string FirstName
  10:     {
  11:         get { return _firstname; }
  12:         set 
  13:         { 
  14:             _firstname = value;
  15:             NotifyPropertyChanged("FirstName");
  16:         }
  17:     }
  18:  
  19:     public string LastName
  20:     {
  21:         get { return _lastname; }
  22:         set 
  23:         { 
  24:             _lastname = value;
  25:             NotifyPropertyChanged("LastName");
  26:             
  27:         }
  28:     }
  29:  
  30:     public int Age
  31:     {
  32:         get { return _age; }
  33:         set 
  34:         {
  35:             int temp = value;
  36:             if(temp < 0)
  37:                throw new Exception();
  38:             _age = value;
  39:             NotifyPropertyChanged("Age");
  40:         }
  41:     }
  42:  
  43:    
  44:  
  45:  
  46:     //fires Property Change event. 
  47:     private void NotifyPropertyChanged(String info)
  48:     {
  49:         if (PropertyChanged != null)
  50:         {
  51:             PropertyChanged(this, new PropertyChangedEventArgs(info));
  52:         }
  53:     }
  54: }

AgeToColorConverter

This class implements IValueConverter to convert age to color property

   1: public class AgeToColorConverter : IValueConverter
   2: {
   3:  
   4:     public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
   5:     {
   6:         if ((int)value > 0)
   7:         {
   8:             if ((int)value > 25)
   9:                 return new SolidColorBrush(Colors.Green);
  10:             else 
  11:                 return new SolidColorBrush(Colors.Orange);
  12:         }
  13:         else
  14:             return DependencyProperty.UnsetValue;
  15:     }
  16:  
  17:     public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
  18:     {
  19:         return null;
  20:     }
  21: }

Page.Xaml

   1: <UserControl x:Class="DataBindingBeta2.Page"
   2:     xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
   3:     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
   4:     xmlns:local="clr-namespace:DataBindingBeta2;assembly=DataBindingBeta2" 
   5:     >
   6:     <UserControl.Resources>
   7:         <local:AgeToColorConverter x:Key="goldconverter"/>
   8:     </UserControl.Resources>
   9:     <Grid x:Name="LayoutRoot" Background="{Binding Path=Age, Mode=TwoWay, Converter={StaticResource goldconverter}}" Height="500" Width="500">
  10:         <Grid.RowDefinitions>
  11:             <RowDefinition/>
  12:             <RowDefinition/>
  13:             <RowDefinition/>
  14:         </Grid.RowDefinitions>
  15:         <Grid.ColumnDefinitions>
  16:             <ColumnDefinition/>
  17:             <ColumnDefinition/>
  18:         </Grid.ColumnDefinitions>
  19:         <TextBlock Text="First Name" Grid.Row="0" Grid.Column="0" VerticalAlignment="Center"/>
  20:         <TextBlock Text="Last Name" Grid.Row="1" Grid.Column="0" VerticalAlignment="Center"/>
  21:         <TextBlock Text="Age" Grid.Row="2" Grid.Column="0" VerticalAlignment="Center"/>
  22:         <TextBox Grid.Row="0" Grid.Column="1" VerticalAlignment="Center" x:Name="txtFirstName">
  23:             <TextBox.Text>
  24:                 <Binding Mode="TwoWay" Path="FirstName"/>
  25:             </TextBox.Text>
  26:         </TextBox>
  27:         <TextBox Grid.Row="1" Grid.Column="1" VerticalAlignment="Center" Text="{Binding Path=LastName, Mode=TwoWay}" x:Name="txtLastName"/>
  28:         <TextBox Grid.Row="2" Grid.Column="1" VerticalAlignment="Center" Text="{Binding Path=Age, Mode=TwoWay, ValidatesOnExceptions=true, NotifyOnValidationError=true}" BindingValidationError="TextBox_BindingValidationError" x:Name="txtAge" />
  29:     </Grid>
  30: </UserControl>

Interesting parts of this XAML is

<TextBox Grid.Row="2" Grid.Column="1" VerticalAlignment="Center" x:Name="txtAge" 
Text="{Binding Path=Age, Mode=TwoWay, ValidatesOnExceptions=true, 
NotifyOnValidationError=true}" BindingValidationError="TextBox_BindingValidationError" />
 

Page.Xaml.cs

   1: public partial class Page : UserControl
   2: {
   3:     public Page()
   4:     {
   5:         InitializeComponent();
   6:         Person p = new Person();
   7:         p.FirstName = "Vivek";
   8:         p.LastName = "Dalvi";
   9:         p.Age = 100;
  10:         DataContext = p;
  11:     }
  12:  
  13:    
  14:  
  15:     
  16:     private void TextBox_BindingValidationError(object sender, ValidationErrorEventArgs e)
  17:     {
  18:         if (e.Action == ValidationErrorEventAction.Added)
  19:         {
  20:             TextBox t = sender as TextBox;
  21:             t.Background = new SolidColorBrush(Colors.Red);
  22:         }
  23:         if (e.Action == ValidationErrorEventAction.Removed)
  24:         {
  25:             TextBox t = sender as TextBox;
  26:             t.Background = new SolidColorBrush(Colors.White);
  27:         }
  28:     }
  29:  
  30:    
  31: }

Here is the sample project.

Standard