Use PromptBox to get simple user input in Silverlight

When we create Silverlight application we will run into a situation where we need to get very simple input from a user apart from traditional ok or cancel. If you are after the simple ok or cancel then message box serve the purpose but if you need to get some kind of data from the user as simple as a number or a string, then we do not need to create popup window or child window in Silverlight if you are using component one controls.

Out of the box, component provides a user control called C1PromptBox which as the name suggests, prompt the user for an input and goes on it merry way without cluttering code with complicated code.

Let’s look at this with simple example;

C1PromptBox.Show(“Enter your age”, “Age prompt”, (s, r) =>
{
If (r ==MessageBoxResult.OK)
{
//age will be in s, parse the age out
}
else
{
// user pressed cancel
}
}

If you expect the last two line to execute after getting user input, then if condition has to be inside the C1PromoptBox.Show bot outside like the code shown

above. The proper way to write the code

In the above example, when the code executes it will prompt the small message box to enter the age and you parse or handle the value on the event user press ok button.

One thing we need to be mindful, the Show event is a delegate so if you have any instructions that need to execute after prompt is successful, it has to be inside the MessageBoxResult.OK condition not outside the show delegate since UI will take control back after show event is successful.

Here is a scenario;

  1. public void PromptAge()
  2. {
  3. int age = 0;
  4. C1PromptBox.Show(… get age);
  5. if (age <= 30)
  6. HandleLessThanThirtyAge();
  7. }
 

In the above example we are expecting the dialog to be shown for user to enter the age. After the user enters the age, line 05 gets control and executes HandlesLessThanThirtyAge method if the age is less than or equal to 30. But that is not how it works. When PromptAge executes, it execute prompt box and while it wait for user to enter something, the UI thread continues and executes 05 through so age will be always 0 and it never executes the method on 06. The reason for that is, the behavior on how to handle ok or cancel is delegate inside the prompt box. So for it work properly. ie, after user enters the ages, get the age and based on the age to execute the HandleLessThanThirtyAge, we need to move the if condition inside the PromptBox delegate itself as shown below

  1. public void PromptAge()
  2. {
  3. C1PromptBox.Show("msg", "prompt", (s, r) =>
  4. {
  5. if (r == MessageBoxResult.OK)
  6. {
  7. //parse age from s
  8. if (age <= 30)
  9. HandleLessThanThirtyAge()
  10. }
  11. };
  12. }
One of the feature missing in this is default value for the prompt, I requested this feature with Component One and it is schedule be part of their feature release. By providing most commonly used value as default value, we could save one key stoke for the user. Once the feature is available I will update this blog.

Creating Excel like grid in Silverlight – C1FlexGridBook

If you are developing a business application, you will run into a situation where the end users want to export the data to excel and do some work there and then import the sheet back to the application. This is very normal, the main reason the users ask for it because Silverlight does not have a grid which look and behaves like Excel. There are bunch of work around for this, but I really like the C1FlexGridBook control from Component One. This gird out of the box gives excel like look and feel with less amount of code. Some of the basic features like created tabbed sheet at the bottom of the control to look like worksheets in Excel and many more.

Lets see how to create a simple excel like grid using C1FlexGridBook. What we need to create is Component One dlls for

 image

We need Excel, FlexGridBook and FlexGridBook.

So how do we go about adding the control. So in the XAML I created simple tag for C1FlexGridBook as follows

image

I am planning to create the columns in the code behind (I am trying figure out a problem so two birds with single stone). The data that I am binding is a collection of customers like the following

image

where the data structure is

image

When the page is navigated to the pages I create a customer collection like the following

image

Now all is left is to create the sheets and bind the data to the grid in each sheet. Here is where you have to be very careful. I would strongly recommend you to create skeleton sheets but do not creating individual grid with data binding, rather, you create the grid for the first and default sheet or the sheet the user wants to see. So here are the steps

Create the sheets with AddSheet and pass in the sheet name as the first parameter as shown below.

image

We also bound out collection to grid by doing that, the current grid associated with the sheet get the data from customers. We set the autogenerate to false since I have collection that I want to appear properly. So below create the dynamic columns on the fly

image

now the question is why would I need a cell template? Since the data is boolean I want to display a check box rather than textblock.

image

That’s it. Now if you would run the code you will see a result something like the following,

image

With very little code from control perspective, we got a grid which looks like excel. If you notice, it has following excel features out of the box

1. Row and Column numberings.

2. Office theme.

3. Fixed column heading for application.

4. Default Excel like filtering.

5. At the bottom, you see the sheets and also it comes with default behavior to let the user create new sheets.

6. Excel like navigation to move between sheets with forward and backward navigation button.

7. Navigate to first or last sheet with fast navigation.

8. If you have lot of sheets and want to navigate to a specific sheet, then right lick on the left or right navigation, it shows available sheets and just click on the sheet name you navigate to that sheet.

I really enjoyed working with this control. If you like it or would like to know more about this control send me a note.

Elegant way to create combo box in C1FlexGrid (Tip 8)

You are asked to create a Silverlight application to show a grid where one or more columns are combo boxes. This is a very common scenario. It can be very easily solved by creating a data edit template on the grid column and show a combo box, with a converter. There are lots of examples for this scenario, one in code project and another one in Manish blog. When I binged, there are lot of information in stack overflow. There is a much better solution if you are using Component One.

If you have component one, then we can remove all these data template, combo box and converters with simple property called Value Converter. Lets see the problem statement. We need to create a grid, with employee name and job title.

image

When you enter title in edit mode, it show available jobs like the following

image

Nothing special here, it is the standard combo box which shows all the available jobs for user to pick from. So how do we solve this problem using the special Value Converter?

Lets start from ViewModel this time. 

Code Snippet
  1.  
  2. public List<Person> People
  3. {
  4.     get
  5.     {
  6.         List<Person> list = new List<Person>();
  7.         list.Add(new Person { ID = 1, Name = "Ajay", JobID = 1 });
  8.         list.Add(new Person { ID = 2, Name = "Jeeem", JobID = 2 });
  9.         return list;
  10.     }
  11. }

Nothing new in the view model, it is same as you would build a collection in any other program. How about View?

Code Snippet
  1. <c1:C1FlexGrid Name="_flex" ItemsSource="{Binding People}" AutoGenerateColumns="False">
  2.     <c1:C1FlexGrid.Columns>
  3.         <c1:Column Binding="{Binding Name}"/>
  4.         <c1:Column Binding="{Binding JobID, Mode=TwoWay}" Header="Title"/>
  5.     </c1:C1FlexGrid.Columns>
  6. </c1:C1FlexGrid>

Look at that, there is nothing special here either. We have a grid with two columns, first column is name and second column is Job ID. Please note that, we are not using combo box here. Ok so far we did not do anything new and I still want a combo box in second column. Lets go to the view code behind, that’s where the magic happens.

Code Snippet
  1. public Dictionary<int, string> dict = new Dictionary<int, string>();
  2.  
  3. public MainPage()
  4. {
  5.     InitializeComponent();
  6.     dict.Add(1, "Manager");
  7.     dict.Add(2, "Team Lead");
  8.     _flex.Columns[1].ValueConverter = new ColumnValueConverter(dict);
  9. }

We first create a dictionary, where we are going to keep the options available to show in the combo box. Line 6 and 7 adds two job titles to the dictionary and finally in line 8, we say, for second column use the value converter and point to new instance Column Value Converter with dictionary collection. This informs flex grid that, this column needs to a combo box and use the dictionary to show the string value of the value bound to the column.

Look at the code, it is very simple and elegant. I really like the approach more than writing data template, converters etc.,

One things, I haven’t figured out yet is to bind the value converter directly to view model property. If we could do it, we can eliminate all code from code behind. If I find a solution, I will post it here.

Component One Silvelright FlexGrid Tip 6 (Group Header customatization)

Continuing our previous example of using Data Table to create grid on the fly, we will add one more column for grouping purpose to produce an output like the following

image

This also shows how to do grouping when using DataTable in Silverlight. So lets look at the code which generates the data to populate the grid and also grouping.

private void LoadData()
{
   _dataTableWithData = new DataTable();
   _dataTableWithData.Columns.Add("ID");
   _dataTableWithData.Columns.Add("Name");
   _dataTableWithData.Columns.Add("Age");
   _dataTableWithData.Columns.Add("Group");
   for (int i = 0; i < 10; i++)
   {
      int grp = ((i>3)?1:i);
      _dataTableWithData.Rows.Add(i, "Name" + i.ToString(), i, grp);
   }
   var gd = _dataTableWithData.DefaultView.GroupDescriptions;
   gd.Add(new PropertyGroupDescription("Group"));
}

As you can see, we create group row as we would create normal group row in FlexGrid. When we run the code it creates the groups properly but I do not like the default group header information in the group row. It shows

Group: value (number item)

What if I do not want to show the number of item in the parenthesis, how would I go about not showing it? That is what we are going to see today. This is easily achieved by using a custom converter for GroupHeaderConverter. Here is the custom header converter that we need to do to show only ‘Group : GroupName’

public class GroupHeaderConverter:IValueConverter
{
  public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
  {
     var group = value as CollectionViewGroup;
     if (group != null)
        return string.Format("Group: {0}", group.Name);
     return null;
  }

  public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
  {
   throw new NotImplementedException();
  }
}

As you can see it is very simple value converter. For every group, we check and see if the row is a group row and if it is, then just return “Group: ” + group.name. One thing you need to make sure to use, the first type conversion. Make sure you you get the group information of type CollectionViewGroup. With that you get all group related information. Which is very powerful if you want to handle grouping completely different. Now that we have a group header converter, we need to let flex grid know to use this converter for the group headers by

_flex.GroupHeaderConverter = new GroupHeaderConverter();
That's all about it, now if you run the program you get the group header the way we wanted it to be.

image


Not only we saw how to customize the group header, we also saw how to do grouping in Data Table. Thanks Bernardo for the pointers.

Working with DataTable in Silverlight 4

Silverlight 4 out of the box does not support DataTable as its desktop counter part, WPF. But third party controls are here for the rescue. In this blog we will look at, what it take to create a simple grid and use Data Table that we love. As you might have guessed it, I am using Component One controls. Component One provides a namespace C1.Silverlight.Data, which have DataTable implementation that we have been using. So now that we know, we can use Data table, lets create a simple Grid and populate the data.

Create a Silverlight application and add grid control in your XAML as follow;

<Grid x:Name="LayoutRoot" Background="White">
        <c1:C1FlexGrid ItemsSource="{Binding DataTableWithData, Mode=TwoWay}" IsReadOnly="False" AutoGenerateColumns="True" Name="_flex"/>
    </Grid>

Nothing new here, my Flex Grid control bound a property in my view model. Before we go and code, we require following references in our project to run

c1.silvelright.dll

c1.Silvelright.data

System.Windows.Data

System.Windows.Controls.Data

Now lets look at the view model code;

private DataTable _dataTableWithData;

publicDataView DataTableWithData

{

     get

   
{

        if(_dataTableWithData == null)

            LoadData();

        return_dataTableWithData.DefaultView;

     }

}

_dataTableWithData property is our underlying data source. Which is nothing but DataTable, which is Component One data table type. If you notice we are not really binding data table to the grid control rather, we are binding DataView to the grid control. DataView implements the IEnumerable interface thus make it bindable to grid control. If you were like me trying out and not seeing the data in your grid, probabily your property type is incorrect.

Now lets look at the code which loads data to the data table

private void LoadData()
{
     _dataTableWithData = new DataTable();
     _dataTableWithData.Columns.Add("ID");
     _dataTableWithData.Columns.Add("Name");
     _dataTableWithData.Columns.Add("Age");
     for (int i = 0; i < 10; i++)
     {
          _dataTableWithData.Rows.Add(i.ToString(), "Name" + i.ToString(), i);
     }
}

There is nothing special here, creating and populating the data table as you would do in any other format. Now lets run and see the result;

image

 

That’s about it. To summarize

1. Make sure you use the references I mentioned above in your project.

2. Use Data Table as local variable to hold data but do not bind control to that property.

3. Use DataView type for binding it to the grid.

4. To bind data table to grid, you return ‘DefaultView’ of the data table.

Using ChildsItemPath in C1FlexGrid to create clean MVVM code.

In the latest Component One drop, they have added a new property in C1FlexGrid called ‘ChildItemsPath’. It is a cool feature. In this blog, I am going to go through a simple example of using ChildItemsPath.

First lets see what made me to get to this property. In the current project I am working on, I had to create a grid with the following requirements

1. When there are no children in a group row, grid has to show only the group row and no child rows.

2. Group rows should be editable.

3. When user selects a row in the grid, we need to do something in the view model so I need to know which record user selected.

Just to give you an idea of the data model that I am working with.

ParentID – Parent Description – Child ID – Child Description

We are going to group on Parent ID and show Parent ID and Parent Description in the group row. So like any good old programmer, I approached this problem head on by creating a model like the one above and then created a Paged Collection View with group on Parent description, bind the collection to my C1FlexGrid. With this couple of things happened.

First one, when I have a record like the following

1 – Parent1 – null – null

where Parent1 have no children, it created a group row with 1 – Parent1 and created an empty child row. That was not expected behavior. So I wrote a small hack when the grid loading, I check and see if the rows have null value then change the height of row to 0. Pretty hack but, the group row did how expand/collapse button, giving the user the impression there is data. Next comes editing group row. I was trying to figure out how to edit the group row, while think about it, I looked at the model again and then it occurred to me, I could change my collection like the following

ID – Description

|- ID – Description

|- ID – Description

Something like

   1:  public class TestType1 : INotifyPropertyChanged
   2:  {
   3:          public int ID { get; set; }
   4:          public string Description { get; set; }
   5:          public ObservableCollection<TestType1> Children { get; set; }
   6:   
   7:          public TestType1()
   8:          {
   9:              Children = new ObservableCollection<TestType1>();
  10:          }
  11:   
  12:          public event PropertyChangedEventHandler PropertyChanged;
  13:          private void NotifyPropertyChanged(string info)
  14:          {
  15:              if (PropertyChanged != null)
  16:              {
  17:                  PropertyChanged(this, new PropertyChangedEventArgs(info));
  18:              }
  19:          }
  20:  }

Anytime when you have a collection like this, you should consider using ChildItemsPath, it is the new property added to C1FlexGrid. So how do we go about using it. Lets see the XAML

   1:      <Grid x:Name="LayoutRoot" Background="White"
   2:            d:DataContext="{d:DesignInstance sampleData:DesignMainViewModel, IsDesignTimeCreatable=True}">
   3:          <c1:C1FlexGrid ItemsSource="{Binding MyCollection1}" AutoGenerateColumns="False" Name="flex" SelectionMode="Row" ChildItemsPath="Children">
   4:              <i:Interaction.Triggers>
   5:                  <i:EventTrigger EventName="SelectionChanged">
   6:                      <i:InvokeCommandAction Command="{Binding SelectionChanged}" CommandParameter="{Binding ElementName=flex, Path=SelectedItem}"/>
   7:                  </i:EventTrigger>
   8:              </i:Interaction.Triggers>
   9:              <c1:C1FlexGrid.Columns>
  10:                  <c1:Column Binding="{Binding ID, Mode=TwoWay}"/>
  11:                  <c1:Column Binding="{Binding Description, Mode=TwoWay}"/>
  12:              </c1:C1FlexGrid.Columns>
  13:          </c1:C1FlexGrid>
  14:      </Grid>

The last parameter in line (3) is the new property I was talking about. All you have to specify, which is the child collection property in the model that it has to group on.

As you can see from the line 4-8, after learning about triggers and behaviors, I no longer write code behind code to pass data to view model. If you can get the data from the gird, then use event trigger. So in our case, the third requirement was to pass the selected item to view model when the user select a row. that is what implemented in 4-8.

Line 5 – We are interested in listening SelectionChanged event.

Line 6 – When SelectionChanged event fires, Sl intercenpts and fires View Model command implementation for Selection Changed with Selected Item from the gird as the argument.

Lets look at the View Model for command implementation

   1:          public IActionCommand<object> SelectionChanged { get; set; }
   2:   
   3:          public List<TestType1> MyCollection1
   4:          {
   5:              get
   6:              {
   7:                  List<TestType1> list = CreateData();
   8:                  return list;
   9:              }
  10:          }
  11:   
  12:          private List<TestType1> CreateData()
  13:          {
  14:              List<TestType1> list = new List<TestType1>();
  15:              for (int i = 0; i < 5; i++)
  16:              {
  17:                  TestType1 parentData = new TestType1 { ID = i, Description = i.ToString() };
  18:                  for (int j = 0; j < i; j++)
  19:                  {
  20:                      TestType1 childData = new TestType1 { ID = j, Description = j.ToString() };
  21:                      parentData.Children.Add(childData);
  22:                  }
  23:                  list.Add(parentData);
  24:              }
  25:              return list;
  26:          }
  27:   
  28:          public MainViewModel()
  29:          {
  30:              SelectionChanged = new ActionCommand<object>(
  31:                  p => {
  32:                      TestType1 t = p as TestType1;
  33:                      int k = 0;
  34:                  });
  35:          }

Line 1- Command declaration

Line 3-25 – Collection generation for XAML data binding.

Line 30-34 – Action<T> implementation for Selection Changed Command.

With this code if I would run I get the following screen

image

Where the first row does not have children so it does not show expand or collapse icon. Since mode is set to two way, I am able to edit group row as well as child rows. With Event Trigger, there is no code in the view’s code behind.

That’s it.

Creating Footer Row in Silverlight using Component One C1FlexGrid

Everyone now and then you will run into a requirement for adding a footer row for a data grid. If you are not using a third party control, you could use two grids in a user control to achieve this. Top grid houses the data and bottom grid with one row which will listen to data change in the first grid and generate the summary row.

Recently the new version of Component One, you can get the footer row out of the box if you were using C1FlexGrid/C1FlexGridExcel. Lets see how can we use it to achieve adding a footer row. Lets start out the basic requirement for this example.

1. Display personal information in a Grid, need to display, Name, Age, Sex and Salary.

2. Display a footer row which includes total number of rows, average of Age and salary total.

Lets start out building Silverlight application. Since I am a big fan of design time data and I can get that out of box using Jounce, I am going to create a new Jounce Application.

Create Model first

public class Person : INotifyPropertyChanged
    {
        public string Name { get; set; }
        public int Salary { get; set; }
        public char Sex { get; set; }
        public int Age { get; set; }

        public event PropertyChangedEventHandler PropertyChanged;
        private void NotifyPropertyChanged(string info)
        {
            if (PropertyChanged != null)
            {
                PropertyChanged(this, new PropertyChangedEventArgs(info));
            }
        }
    }

 

Make sure you modify the interface for View Model to get personal information (IMainViewModel.cs)

public interface IMainViewModel
    {
        List<Person> People { get; }
    }

 

My design time data comes from the design viewmodel

public classDesignMainViewModel : IMainViewModel

   
{

        publicList<Person> People

        {

            get

           
{

                List<Person> ppl = newList<Person>();

                ppl.Add(newPerson() { Name = "a", Age = 20, Salary = 1000, Sex = ‘f’ });

                ppl.Add(newPerson() { Name = "b", Age = 21, Salary = 1100, Sex = ‘m’});

                ppl.Add(newPerson() { Name = "c", Age = 22, Salary = 1200, Sex = ‘m’});

                ppl.Add(newPerson() { Name = "d", Age = 23, Salary = 1300, Sex = ‘f’ });

                ppl.Add(newPerson() { Name = "e", Age = 24, Salary = 1400, Sex = ‘f’ });

                ppl.Add(newPerson() { Name = "f", Age = 25, Salary = 1500, Sex = ‘f’ });

                ppl.Add(newPerson() { Name = "g", Age = 26, Salary = 1600, Sex = ‘m’});

                ppl.Add(newPerson() { Name = "h", Age = 27, Salary = 1700, Sex = ‘m’});

                ppl.Add(newPerson() { Name = "i", Age = 28, Salary = 1800, Sex = ‘f’ });

                returnppl;

            }

        }

    }

my runtime view model

[ExportAsViewModel("MainViewModel")]
    public class MainViewModel : BaseViewModel, IMainViewModel
    {
        public List<Person> People
        {
            get
            {
                List<Person> ppl = new List<Person>();
                ppl.Add(new Person() { Name = "a", Age = 20, Salary = 1000, Sex = 'f' });
                ppl.Add(new Person() { Name = "b", Age = 21, Salary = 1100, Sex = 'm' });
                ppl.Add(new Person() { Name = "c", Age = 25, Salary = 2100, Sex = 'f' });
                ppl.Add(new Person() { Name = "d", Age = 27, Salary = 2100, Sex = 'f' });
                return ppl;
            }
        }
    }

Now we have data to load, lets look at the XAML

<c1:C1FlexGridExcel Name="flex" AutoGenerateColumns="False" ItemsSource="{Binding People}">
            <c1:C1FlexGridExcel.Columns>
                <c1:Column Binding="{Binding Name,Mode=OneWay}" GroupAggregate="Count"/>
                <c1:Column Binding="{Binding Age, Mode=TwoWay}" GroupAggregate="Average"/>
                <c1:Column Binding="{Binding Sex}"/>
                <c1:Column Binding="{Binding Salary, Mode=TwoWay}" GroupAggregate="Sum"/>
            </c1:C1FlexGridExcel.Columns>
        </c1:C1FlexGridExcel>   

If you notice, all I am doing is binding the column to display and also specifying what kind of aggregation we need to display in footer row. As per the requirement, we want to total number of personal so I added the group aggregate function as ‘Count’ and Average for Age and Sum for Salary. So my design time screen will look like the following

image

So far no footer row. In the current release, you can add the footer row in the code behind as follows

public MainPage()
        {
            InitializeComponent();
            var gr = new C1.Silverlight.FlexGrid.GroupRow();

           flex.ColumnFooters.Rows.Add(gr);
           }

So to add a footer row, all you have to create a group row and add that as the ColumnFooter rows. With this change if you compile and run it

image

You can apply style, color and all the goodies you would do to any group row, you can do it here as well.

**** Update 8/5/11 *******

There is a support for design time data so you can do it in XAML itself rather than code behind, which I really like. We need to add ColumnFooterRows in the XAML that’s all about it. Modified XAML would be something like the following

<c1:C1FlexGridExcel Name="flex" AutoGenerateColumns="False" ItemsSource="{Binding People}">
            <c1:C1FlexGridExcel.ColumnFooterRows>
                <c1:GroupRow/>
            </c1:C1FlexGridExcel.ColumnFooterRows>
            <c1:C1FlexGridExcel.Columns>
                <c1:Column Binding="{Binding Name,Mode=OneWay}" GroupAggregate="Count"/>
                <c1:Column Binding="{Binding Age, Mode=TwoWay}" GroupAggregate="Average"/>
                <c1:Column Binding="{Binding Sex}"/>
                <c1:Column Binding="{Binding Salary, Mode=TwoWay}" GroupAggregate="Sum"/>
            </c1:C1FlexGridExcel.Columns>
        </c1:C1FlexGridExcel>    

With this change now if you look at the design view you will see the footer row, you do not need to run the program to see it.

image

XAP Optimization – Part II Versioning

So we are all not just building one simple Silverlight project and publishing simple web pages. Most of us develop enterprise LOB application in Silverlight. When you develop that big application you will end up using same component again and again over multiple projects and also you will end up using multiple version of same controls over multiple places as you develop. When you develop major application, if you get a new version of a third party control or even your own control, you will not replace it everywhere because of the regression testing. You will end up versioning and upgrade the solution with version.

In my previous blog on XAP Optimization, I looked at the simple attempt to reduce the size of a small Silverlight application. We looked at two different approaches. From 880Kb, we were able to get it down to ~230KB. I looked at the XAP file to see what is taking that much space and it turned out, I am using Component One controls for UI and Jounce for MVVM Framework. My dll by itself, unzipped only 14k. I was ok with the size of the file since, some of my application would use one version of Component One control and some other XAP files would use different version of the Component One control so If I would strip them out and make it common zip file then I would run into version conflict. So I was happy with the result till last weekend :)

It turned out, even though we use different version of controls, as we make changes we bring the projects to latest controls, so 80% of them require version of the controls. So it makes sense to even strip the controls out of XAP to reduce the file size and also reduce repeated loading of common controls, but somehow put versioning in place.

So by default, System controls become ZIP file when we use XAP optimization and how would be able to do the same for other controls and our own libraries? The answer is very simple. If you would open up the XAP file, you would see

image

The first file is AppManifest.xaml,

<Deployment xmlns="http://schemas.microsoft.com/client/2007/deployment" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" EntryPointAssembly="XAPOptimization1" EntryPointType="XAPOptimization1.App" RuntimeVersion="4.0.50826.0">
  <Deployment.Parts>
    <AssemblyPart x:Name="XAPOptimization1" Source="XAPOptimization1.dll" />
    <AssemblyPart x:Name="C1.Silverlight.FlexGridExcel" Source="C1.Silverlight.FlexGridExcel.dll" />
    <AssemblyPart x:Name="Jounce" Source="Jounce.dll" />
    <AssemblyPart x:Name="System.Windows.Interactivity" Source="System.Windows.Interactivity.dll" />
  </Deployment.Parts>
  <Deployment.ExternalParts>
    <ExtensionPart Source="c1.silverlight.FlexGrid.4.183.zip" />
    <ExtensionPart Source="c1.silverlight.FlexGridFilter.4.183.zip" />
    <ExtensionPart Source="System.ComponentModel.Composition.zip" />
    <ExtensionPart Source="System.Windows.Data.zip" />
    <ExtensionPart Source="C1.Silverlight.183.zip" />
    <ExtensionPart Source="System.Xml.Linq.zip" />
    <ExtensionPart Source="C1.Silverlight.Zip.183.zip" />
    <ExtensionPart Source="C1.Silverlight.Pdf.183.zip" />
    <ExtensionPart Source="System.ComponentModel.Composition.Initialization.zip" />
  </Deployment.ExternalParts>
</Deployment>

If you see there are two parts of Deployments. One is Deployment.Parts which specifies all the dlls that are in the XAP file and associated name space. The second part specifies all the zip files that might be required for application to run. In my opinion the only thing that should be in XAP file will be the application dll. So the two part question is how would you get all the common dlls separated into its own ZIP file and how did you update the Application Manifest to reflect it?

Here is a very good article explaining how to Configure an assembly for use with application library caching. Please read this link since it goes more detail about the Application Manifest file and also ext map XML file.

Any dll that is common and it need to be in a separate Zip file, should have associated ‘extmap’ file. What it means is, in the above project you see that I am using System.Xml.Linq.dll, if you would find the file location using the file property and navigate to the file location, you will see following files, The file we are interested in is the dll file name with ‘extmap.xml’

imageIf you would crack open the System.Xml.Linq.extmap.xml, you will see the following

<?xml version="1.0"?>
<manifest xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  <assembly>
    <name>System.Xml.Linq</name>
    <version>2.0.5.0</version>
    <publickeytoken>31bf3856ad364e35</publickeytoken>
    <relpath>System.Xml.Linq.dll</relpath>
    <extension downloadUri="System.Xml.Linq.zip" />
  </assembly>
</manifest>

The first two line is name and version and the third line is the public key of the dll, that means anything you want to have as separate dll, have to be strong named. Next two specifies the relative path of the dll and where do you get it from. In our case, the dll is in a zip file in the same path of the XAP file.

Have a look at this blog, here the author explains how it is done and also provides a tool to create your own extmap files for your dlls.

Once we have extmap files created for the dlls that need to be separated and placed on the same path where the dll located, when compiled with XAP optimization enabled, all the ones with extmap files will be in separate ZIP files.

Now comes the question of versioning, some XAP files would use different version of same controls, so by creating extmap file we would end up overwriting the old one with new one and potentially crashing the  application. How do we solve it, again it is very simple, now most of all third party controls comes with extmap files, including Component One. Whenever we get latest version of a control or our own library, I run a small ruby script, which run through all extmap files and adds version number to the destination zip file. So instead of

<ExtensionPart Source="C1.Silverlight.zip" />

our implementation would look like the following

<ExtensionPart Source="C1.Silverlight.183.zip" />

That’s about it. So when I compile my project pointing to the new version dll, it uses the modified extmap and generate version numbered zip file thus making all the project work without any problem. I am working on a script which will then walk though all the XAP files and look at the Application Manifest and remove any unwanted zip files from the deliver location thus keeping the folder clean.

Once I have the second script done, I will post both the scripts in the blog here.

To test this, I created two Silverlight applications. Both are exactly same except the first one uses version 152 of Component One dlls and second one uses version 183. Now if I would run, each application should use appropriate ZIP and run without any problem. So I ran the fiddler and then ran the application to see the what is it downloading and is it caching.

image

First I ran XAPOptimization with version 152 and as you can see the first green box shows all the files that got downloaded to the client. Now when I tried the second version of the XAP Optimization with version 183, as you can see it did get the version 183 and also it skipped the common files that was already downloaded by the first version. For example. System.Windows.Data.zip was not downloaded since it was downloaded along with the first XAP file.

I see lot of potential in using XAP optimization when developing large application. Hope this helps someone.

One thing I forgot to mention, with versioning and keeping couple dlls in the XAP file itself, we are at ~100kb. That is a excellent saving. So next time when we make a change the the solution and none of te thrid party control changes, our download is only the XAP file size that is ~100kb not 880kb.

XAP optimization

I created a very simple example which have two grids, one is master grid and another one is total grid. Total gird, totals any field which has decimal field in it. Nothing complicated. As any developer, I was adding code if I miss any namespace I add them. Some time, I add some system references that I might not even use later as I progress but forget to remove from the solution. So with that all in mind, after building the solution and run as I expected it to run and deploy it. Some time you night get a complain that it takes for ever to load. If you ever hear or if you see that yourself when you start testing, take a look at the size of the XAP file in ClientBin folder. Chances are your XAP is big.

Going back to the sample I was talking about, with out any code changes, the XAP file size is 880KB. Next we are going to see how we can reduce the size of the file and user experience is better. I attempted that with couple of ways.

1. Using Component One XAP optimizer:

I got a license for Component One XAP optimizer and thought of give a world and see how much size reduction happens. I pass the current XAP as the input and it can generate new XAP file or override the existing one. Here is the result of the XAP optimization.

image

The new file size is 336KB, more than 50% size reduction. In this approach the files are still in single XAP file and there are no external files to download. What I attempted was a very simple solution, it seems it has lot more features that you could use for more complex projects.

2. Using Visual Studio XAP optimization using application library caching:

From SL3 and forward,  there is a way we can optimize the XAP files by removing the system and third party controls from the required elements of XAP. You can read more about how it is done in Tim’s blog. Using that approach I was able to get the XAP file size reduced to 229KB. All the required dlls are separated out to its on ZIP files.

image

[Following was incorrect. As of now, following combination broke the Silverlight application. Once I know why it is not working, I will update here.]

3. Using Component One Optimization on top Visual Studio optimization:

As you have guessed, we should be able apply C1 optimization and get a better initial performance. With the optimization applied the XAP file size reduced to 166KB.

I am really happy with the overall size reduction from 880KB to 166KB. If any of you have any other better way of doing optimization, would love to hear it.

Creating Treeview using DataGrid – (Component One)

In my previous posts I have explained how you can create TreeView look and feel in DataGrid using Cell Factory in Component One C1FlexGrid.  In that blog, I also mentioned it is round about way of doing it and there is a better way to do it. In this blog, we will look at the correct way to create TreeView look and feel in DataGrid. Before we go there, why are we attempting to create tree view in data grid? There are couple of reasons for that;

1. If your collection is large and each node have large number of children underneath it. Tree view takes time to load the children in memory when you expand for the first time. Because of that, if you have lot of nodes with lot of children under the nodes, your performance will suffer so switch to Data Grid which enjoys full virtualization. Since it has full virtualization expand and collapse in Data Grid is fast.

2. If you would like to get multi column look and feel with column heading.

Lets jump into the example, Here is what we are trying to achieve

image

We would like to get a tree view look with first column a check box and second column the description of the mode. In the above screen shot you can see, it looks like tree view with expand and collapse icons and also with proper indentation when expanded.

 

First look at the model; Nothing special here, it has IsChecked boolean which is the first column in our display and SourceDescription is the second column in the grid. In this example I defined children as observable collection. If you have large number of children under each node, I would recommend you to read this blog on when to use Observable collection and when not to use it.

public class Person:INotifyPropertyChanged
{
    public string ID { get; set; }
    public string Name { get; set; }
    public bool IsChecked { get; set; }
    public ObservableCollection<Person> Children { get; set; }
    public Person()
    {
        Children = new ObservableCollection<Person>();
        IsChecked = false;
    }

    public event PropertyChangedEventHandler PropertyChanged;
    private void NotifyPropertyChanged(string info)
    {
        if (PropertyChanged != null)
        {
            PropertyChanged(this, new PropertyChangedEventArgs(info));
        }
    }
}

Lets load the data

private void LoadPeople()
{
     _people = new List<Person>();
     for (int i = 0; i < 10; i++)
     {
         Person ppl= new Person() { ID = i.ToString(), Name = string.Format("Parent{0}",i.ToString()), IsChecked = false };
         if (i % 3 == 0)
         {
             for (int j = 0; j < 1000; j++)
             {
                 Person child = new Person() { ID = j.ToString(), Name = string.Format("Child{0}", j.ToString()) };
                 ppl.Children.Add(child);
             }
         }
        _people.Add(ppl);
     }
 }

Now lets look at the XAML

<c1:C1FlexGrid x:Name="_flex" HeadersVisibility="None" AutoGenerateColumns="False" ChildItemsPath="Children">
     <c1:C1FlexGrid.Columns>
         <c1:Column Binding="{Binding IsChecked, Mode=TwoWay}"/>
         <c1:Column Binding="{Binding Name}"/>
     </c1:C1FlexGrid.Columns>
</c1:C1FlexGrid>

As simple as that, only thing new in here is ‘ChildItemsPath’. It is the new property added to C1FlexGrid to mimic the tree view look and feel. So this big blog is all about this property.With this property, you do not require the different classes and all crazy event mechanisms.

If you do not want to have multiple columns like the one I did and want to have true one column look, use cell template to stack the items in one column and you will get the exact tree view.

By default, when you use the grid like this, it will put column and row header around the control to make it show as grid. To turn off the grid controls around it, make sure to use HeaderVisibility=”None”