Blog Posts

Wednesday, December 22, 2010

WaveMaker for Cloud Applications in Java

My friend who was a Java Developer was very much interested in Cloud Development, while searching for the Cloud Application development tools/API.  Thought of forwarding to him - 


More on -
http://www.wavemaker.com/


http://en.wikipedia.org/wiki/Wavemaker


Few lines from wikipedia - 

WaveMaker (formerly known as ActiveGrid) is an open source software development platform that automates much of the process for creating Java web and cloud applications. WaveMaker provides a visual rapid application development platform and is available as a free open source software download or as a hosted cloud development environment (akaPlatform as a Service) running on Amazon EC2.[1]


From WaveMaker -



WaveMaker is the only open and easy-to-use development platform for web and cloud applications. With WaveMaker's visual, drag and drop tools, any developer can start building enterprise Java applications with minimal training. WaveMaker creates standard Java applications, boosting developer productivity and quality without compromising flexibility.
WaveMaker applications are cloud-ready and include built-in support for multi-tenancy and elastic scaling. WaveMaker is a rapidly-growing company backed by a 15,000-strong developer community. WaveMaker customers like the Center For Disease Control, Macy's and KANA have built rich internet applications with minimal learning curve and up to 98% less code.

Sunday, December 19, 2010

Grouping in LINQ

Grouping in LINQ provides flexibility and reliability over for loops while manipulating DataTable data.  As the LINQ uses the power of Generics, we can write code that avoid issues related to type conversion.  

I am taking a simple example explain the grouping LINQ, I am loading data from xml file (it is similar to getting data from SQL server).  I have to display the data by monthly and yearly.  My xml file has the content refer end of the post.

If I need to show the data by monthly in a grid (grdSales) on my form - 



DataSet ds = new DataSet();
ds.ReadXml("Sales.xml");


var sales = from r in ds.Tables[0].AsEnumerable()
group r by new { month = r["Month"].ToString(), year = r["Year"].ToString() } into grp                


orderby grp.Key.year, grp.Key.month
     select new SalesDetails
     {
        Year = grp.Key.year,
        Month = grp.Key.month,
        Sales = grp.Sum(s => Convert.ToInt32(s["Price"].ToString()))
     };


List<SalesDetails> lstSales = sales.ToList();

//adding total row
lstSales.Add(
sales       
      group r
(from r i n by sales into grpTotal
ails
      {
         Year =
      select new SalesDe t"Total",          Month = "",
=> s.Sales)
      }).ToList()[0]
     );
 
         Sales = grpTotal.Sum(
sgrdSales.DataSource = lstSales;



Sum() is the method from Enumerable class which is implements the the IEnumerable interface. refer the http://msdn.microsoft.com/en-us/library/system.linq.enumerable.aspx for other method of the class like Min, Max and Average.  These methods are overloaded to support datatypes like Int, Decimal etc.  And grp is the object of class which implements IGrouping.


Similarly to show data by year - 
DataSet ds = new DataSet();
ds.ReadXml("Sales.xml");
var sales = from r in ds.Tables[0].AsEnumerable()
    group r by r["Year"].ToString()into grp
    orderby grp.Key
    select new SalesDetails
    {
     Year = grp.Key,
     Month = "1",
     Sales = grp.Sum(s => Convert.ToInt32(s["Price"].ToString()))
    };


List lstSales = sales.ToList();
lstSales.Add(
      (from r in sales
       group r by sales into grpTotal
       select new SalesDetails
       {
        Year = "Total",
        Month = "",
        Sales = grpTotal.Sum(s => s.Sales)
      }).ToList()[0]
   );
grdSales.DataSource = lstSales;








One of the advantages of LINQ is, you can also have a List as one of the columns of your rows. As the following code populates a column with a List of month sales for the particular year.  






public struct MonthlySales
        {
           public string MonthName;
           public int MonthSale;
        }



var sales = from r in ds.Tables[0].AsEnumerable()
           group r by r["Year"].ToString() into grp
           orderby grp.Key
           select new SalesDetails
           {
              Year = grp.Key,
              monthSales =
                 (from rm in ds.Tables[0].AsEnumerable()
                   where rm["Year"].ToString() == grp.Key
                   select new MonthlySales
                   {
                     MonthName = rm["Month"].ToString(),
                     MonthSale = Convert.ToInt32(rm["Price"].ToString())
                   }).ToList(),
             Sales = grp.Sum(s => Convert.ToInt32(s["Price"].ToString()))
            };

The data populates like - 








My xml data from Sales.xml, and loaded data from database also should work.


<?xml version="1.0" encoding="utf-8" ?> <Sales>   <Sale><Item>item1</Item> <Price>2000</Price>  <Month>1</Month> <Year>2010</Year></Sale>   <Sale><Item>item2</Item><Price>3400</Price><Month>5</Month><Year>2010</Year></Sale>   <Sale><Item>item1</Item><Price>2200</Price><Month>7</Month><Year>2010</Year></Sale>   <Sale><Item>item1</Item><Price>1600</Price><Month>10</Month><Year>2010</Year></Sale>   <Sale><Item>item1</Item>  <Price>1200</Price> <Month>11</Month><Year>2010</Year></Sale>   <Sale><Item>item1</Item> <Price>500</Price><Month>12</Month>  <Year>2010</Year></Sale>   <Sale><Item>item1</Item><Price>2500</Price><Month>1</Month><Year>2010</Year></Sale>   <Sale><Item>item2</Item><Price>3400</Price><Month>5</Month><Year>2010</Year> </Sale>   <Sale><Item>item1</Item> <Price>2400</Price><Month>7</Month><Year>2010</Year></Sale>   <Sale><Item>item1</Item><Price>1600</Price><Month>10</Month> <Year>2010</Year></Sale>   <Sale><Item>item1</Item><Price>1000</Price> <Month>11</Month><Year>2010</Year></Sale>   <Sale><Item>item1</Item><Price>200</Price><Month>12</Month><Year>2010</Year></Sale>   <Sale><Item>item1</Item><Price>500</Price><Month>1</Month> <Year>2011</Year></Sale>   <Sale><Item>item2</Item><Price>3100</Price><Month>3</Month><Year>2011</Year></Sale>   <Sale><Item>item1</Item> <Price>2300</Price><Month>8</Month><Year>2011</Year></Sale>   <Sale><Item>item1</Item><Price>3400</Price><Month>11</Month><Year>2011</Year></Sale>   <Sale><Item>item1</Item><Price>2200</Price><Month>8</Month><Year>2011</Year></Sale>   <Sale><Item>item1</Item><Price>1200</Price> <Month>11</Month><Year>2011</Year></Sale>   <Sale> <Item>item1</Item><Price>2990</Price><Month>8</Month> <Year>2011</Year></Sale>   <Sale><Item>item1</Item><Price>1840</Price> <Month>11</Month><Year>2011</Year></Sale>   <Sale><Item>item1</Item><Price>1200</Price><Month>11</Month><Year>2011</Year></Sale>   <Sale><Item>item1</Item> <Price>6340</Price>  <Month>12</Month> <Year>2012</Year></Sale>   <Sale> <Item>item1</Item> <Price>9000</Price><Month>8</Month><Year>2012</Year></Sale>   <Sale> <Item>item1</Item><Price>1600</Price> <Month>9</Month><Year>2012</Year> </Sale>   <Sale> <Item>item1</Item><Price>1100</Price> <Month>7</Month> <Year>2012</Year></Sale>   <Sale><Item>item1</Item><Price>2300</Price><Month>8</Month><Year>2012</Year> </Sale>   <Sale><Item>item1</Item><Price>1800</Price> <Month>9</Month><Year>2012</Year></Sale>   <Sale><Item>item1</Item> <Price>1200</Price><Month>7</Month><Year>2012</Year> </Sale> </Sales>

Monday, December 13, 2010

Cloud Computing - Auto Scaling

Cloud Computing is going to change the way we compute, store data and present in our application. 


In the words of wikipedia - (http://en.wikipedia.org/wiki/Cloud_computing)


"Cloud computing is Internet-based computing, whereby shared resources, software and information are provided to computers and other devices on-demand, as with the electricity grid".
Auto Scaling enables the application developers to enable the resources added to the site/application at production when needed as according to the hits to the site,  http://blogs.msdn.com/b/gonzalorc/archive/2010/02/07/auto-scaling-in-azure.aspx, if my site is a success and the traffic is high it will be added with extra servers and storage to my site.  And if it is a failure I may need to pay only the lost cost for the single server or minimum resources.  Thus reducing risk of investment.


Came to know these are the sites which are already using cloud computing for their applications.
  • Zynga.com
  • NetFlix.com
  • Hungama.com
  • Animoto.com
And Microsoft is planning office suite 'Office 365' on cloud http://www.microsoft.com/en-in/cloud/default.aspx

One game site was a huge success and it just got from 80 servers to 3000+ server in a single night.  

OKEY, but what if I am not getting any revenue of my site and I want to restrict to only 1 (or few to fit in my budget) server? 
Simple I can set the Auto Scalling to off on my account of cloud computing.  Later I can use auto scaling when I am ready to invest.

Friday, December 10, 2010

Performance issue while Grouping in SSRS Reports

In one of our projects the report was taking too much time to load the report. When we analysed the issue we found that we are unnecessarily loading all the records into local reporting server when we need only top level data after applying grouping.
To explain this I am taking a simple example here but in real time there may be complex reports but with little analysis we can find the way to increase performance. Let’s take orders data, and we need to get a report of sales details that in various states.


When we write the query as below to get the details and apply the grouping in the SSRS report.

SELECT OrderID, State, Price, Product FROM SalesDetails

When there is more than 100,000 records in sales and when we run the report it will first fetch the 100,000 records to the report server and then applies the grouping. Assume if the network bandwidth is 1Mbbs it will take at least 10secs assuming each record of 100 bytes.
Instead of this we can use the grouping within the select statement to get only the required data –

SELECT State, Sum(Price), Count(1) FROM SalesDetails GROUP BY State

It will return only those number of records as sales.


But in real time tables may involve joins also. For those use nested select statements like –

SELECT t.State, Sum(t.Price), Count(1) FROM
( < your complex query with joins and other conditions > ) t
GROUP BY State


Lesson is to CONSIDER BANDWIDTH ALSO WHILE DEVELOPING REPORTS.

Tuesday, May 25, 2010

Best fit of text length while printing PDF templates

In one of my projects we are using PDF templates for the generating the PDF files. Like we create a template first with the textboxes on it and later we can use the template with filling the dynamic data and saving the resulting file. The issue was with the dynamic data not fitting in to the textboxes when the data from database is in Capital Letters. As we were not using the mono spaced fonts like courier. Mono spaced font takes equal number of points while printing on screen or paper say 8X12 pixels for all characters it may be ‘I’ or ‘W’. But non mono spaced font will take different width for different characters to save space, like ‘I’(may be say 3 pixels) will take less width on paper or screen than ‘W’(say 12pixels). And also capital letters take more width than small case. Got it?

There are more than 10 textboxes on my PDF for a paragraph like content to show on the PDF to look like lines of running text. Initially I set the line length to some 100 characters for each line in my code like if the text to show is 400 characters I was displaying in 4 lines. My issue was it is giving space on right side of the PDF textbox when text is in small letters. Then I increased number of characters to show from 100 to 120. Then the issue was with the uppercase text it was concatenated as the textbox width on the PDF form is limited and cannot increase.

The solution is to calculate the best fit of text length at runtime instead hard coding to 100 or 120. When the text is in uppercase the length can be calculated to 90 or so. And when the text is in lower case the length can be 120 characters.

Case Study: We are using iTextSharp (an open source project from SourceForge), there is a mothod GetEffectiveStringWidth() in the PdfContentByte class. This function will calculate how many points will the text take on the PDF at runtime based on the supplied font.

Before –





GetNextLine() is my own method (read at the end if you are interested) which returns the next line to display for the supplied length to maintain the text into readable way by keeping the words to-gether as when using
Substring may split single word into lines, like 'letter' can be splitted to 'let' at the end of first line and 'ter' in second line.


PdfReader pdfformreader=new PdfReader(strPdfTempPath);
PdfStamper pdfformstamper=new PdfStamper(pdfformreader,new FileStream(strPdfNewPath,FileMode.Create));

PdfContentByte cb = pdfformstamper.GetOverContent(2);
cb.SetFontAndSize(FontFactory.GetFont("Arial").GetCalculatedBaseFont(false), 8f);
//i am printing the text strRemarksSummary in to 12 lines
for(int ind = 0 ; ind < 12 ; ind++)
{
   string strNextRemarks = PermitPdfForm.GetNextLine( ref strRemarksSummary, 125); // fixed length of 125 
   if( strNextRemarks.Equals("EndOfContent"))
   {
       break;
   }
   else
   {
       pdfformfields.SetField( "txtRemarks" + (ind+1) , strNextRemarks);
   }
}
After, instead of hard coding to 125 use
-



string strNextRemarks = PermitPdfForm.GetNextLine( ref strRemarksSummary, //125);
                        PermitPdfForm.GetEffectiveTextLength(
                        ref strRemarksSummary, cb,
cb.PdfDocument.PageSize.Width - 70)); // textbox width on the pdf

public static int GetEffectiveTextLength(
            ref string str, PdfContentByte cb, float intTextBoxWidth)
{
    int intCharIndex = 0;
    while( true)
    {
        string strTemp = str.Substring(0,intCharIndex);
        float fltLength = cb.GetEffectiveStringWidth(strTemp,false);
        if ( fltLength > intTextBoxWidth)
        {
            return intCharIndex - 1;
        }
        else
        {
            intCharIndex++;
            if( intCharIndex >= str.Length)
                return str.Length;
        }
        //set to maximum of 1000 loop only to avoid infinite loop
        if( intCharIndex > 1000)
            return str.Length;
    }
}



public static string GetNextLine(
    ref string strContent, int intLineMaxLength)
{
    if ( strContent == null ||
        strContent.Length == 0)
    {
        return "EndOfContent";
    }

    string strReturn = "";

    if( strContent.IndexOf('\n') != -1 &&
        strContent.IndexOf('\n') < intLineMaxLength)
    {
        strReturn = strContent.Substring( 0, strContent.IndexOf('\n'));
        strContent = strContent.Substring( strContent.IndexOf('\n') + 1);
        return strReturn;
    }

    if ( strContent.Length <= intLineMaxLength)
    {
        strReturn = strContent;
        strContent = "";
    }
    else
    {
        int intLastCharIndex = intLineMaxLength;
        while( true)
        {
            if ( Char.IsWhiteSpace( strContent[intLastCharIndex]))
            {
                strReturn = strContent.Substring( 0, intLastCharIndex);
                strContent = strContent.Substring( intLastCharIndex + 1).Trim();
                break;
            }
            else
            {
                intLastCharIndex--;
                if( intLastCharIndex == 0)
                {
                    strReturn = strContent.Substring( 0, intLineMaxLength);
                    strContent = strContent.Substring( intLineMaxLength + 1).Trim();
                    break;
                }
            }
        }
    }
    return strReturn;
}

Friday, May 7, 2010

SQL Server 2005 Paging – The Holy Grail


The paging and ranking functions introduced in 2005 are old news by now, but the typical ROW_NUMBER OVER() implementation only solves part of the problem.
Nearly every application that uses paging gives some indication of how many pages (or total records) are in the total result set. The challenge is to query the total number of rows, and return only the desired records with a minimum of overhead? The holy grail solution would allow you to return one page of the results and the total number of rows with no additional I/O overhead.

In theory, ROW_NUMBER() gives you all the information you need because it assigns a sequential number to every single row in your result set. It all falls down, of course, when you only return a subset of your results that don't include the highest sequential number. The solution is to return a 2nd column of sequential numbers, in the reverse order. The total number of the records will always be the sum of the two
fields on any given row minus 1 (unless one of your sequences is zero-bound).


DECLARE @startRow INT ; SET @startrow = 50
;
WITH cols AS
(
 SELECT col1, col2,
  ROW_NUMBER() OVER(ORDER BY col1, col2) AS seq,
  ROW_NUMBER() OVER(ORDER BY col1 DESC, col2 desc) AS totrows
 FROM Table1
)

SELECT col1, col2, totrows + seq -1 as TotRows
 FROM cols
 WHERE seq BETWEEN @startRow AND @startRow + 49
 ORDERBY seq


This approach gives us our page of data and the total number of rows with zero additional overhead! (well, maybe one or two ms of CPU time, but that's it).
For more info refer - http://www.sqlservercentral.com/articles/T-SQL/66030/

Wednesday, May 5, 2010

Using Silverlight Templated Control to create controls


Introduction

In one of my Silverlight projects, the requirement is to show
slide show of the pictures online and the zooming funcionality
needs to implement. I created one reusable control in
Silverlight using the 'Silverlight Templated Control' available
in Silverlight tools. Here I want to share my code for the Album
which uses the XML file to show the images from the server from
multiple folders which are mapped to the album.



Using the code

The goal is to create a Album control that is reusable in a
Silverlight project as below -


<grid x:name="LayoutRoot" />
<xamlalbum x:name="XAlbum" albumsrc="Album.xml" borderthickness="1" borderbrush="Black"
disablealbumselection="True" slideduration="3" height="500" width="600" imgsource="Babies" />
</grid />

To get the output like this -


Developing the control


Create a new 'Silverlihgt Class Library' project to create
the control, to build a .dll file to distribute the control.



Add new control of type 'Silverlight Templated Control' to
create our control.



This will create a class file for the code-behind of the
control and the Generic.xml under Theme folder which contains
the template of our control.


The following is template code which has the next and
previous buttons and other buttons for the slideshow and folder
selection. A slider control for zooming of the control,
MouseWheel event is also added on the image to zoom.



<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:SLNuke">
<Style TargetType="local:XAMLAlbum">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="local:XAMLAlbum">
<Border Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}">
<Canvas Name="root" Width="{TemplateBinding Width}"
local:Clip.ToBounds="True"
Height="{TemplateBinding Height}" Margin="0,0,0,0" >
<Canvas.Resources>
<Storyboard x:Name="SlideShow">
<ObjectAnimationUsingKeyFrames BeginTime="00:00:00" x:Name="objectSS"
Storyboard.TargetProperty="Text"
Storyboard.TargetName="txtCurrentImg">
</ObjectAnimationUsingKeyFrames>
</Storyboard>
<Style TargetType="Button" x:Name="AlbumButtonStyle">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate>
<Border Background="White" BorderBrush="black" BorderThickness="2">
<Canvas Background="Transparent" Width="20" Height="20"
HorizontalAlignment="Center" VerticalAlignment="Center">
<TextBlock Canvas.Left="6" Canvas.Top="3" HorizontalAlignment="Center" VerticalAlignment="Center"
Foreground="black" FontFamily="Arial" FontWeight="Bold" FontSize="14"
Text="{TemplateBinding Content}"></TextBlock>
</Canvas>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style TargetType="Button" x:Name="SlideShowButtonStyle">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate>
<Border Background="White" BorderBrush="black" BorderThickness="2">
<Canvas Background="Transparent" Width="20" Height="20"
HorizontalAlignment="Center" VerticalAlignment="Center">
<Rectangle Canvas.Top="3" Canvas.Left="3" Width="14" Height="10"
Stroke="Black" StrokeThickness="1"></Rectangle>
<Line Stroke="Black" StrokeThickness="1"
X1="4" Y1="17" X2="9" Y2="13"></Line>
<Line Stroke="Black" StrokeThickness="1"
X1="16" Y1="17" X2="11" Y2="13"></Line>
</Canvas>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style TargetType="Button" x:Name="SlideShowDisabledButtonStyle">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate>
<Border Background="White" BorderBrush="black" BorderThickness="2">
<Canvas Background="Transparent" Width="20" Height="20"
HorizontalAlignment="Center" VerticalAlignment="Center">
<Rectangle Canvas.Top="3" Canvas.Left="3" Width="14" Height="10"
Stroke="Black" StrokeThickness="1"></Rectangle>
<Line Stroke="Black" StrokeThickness="1"
X1="4" Y1="17" X2="9" Y2="13"></Line>
<Line Stroke="Black" StrokeThickness="1"
X1="16" Y1="17" X2="11" Y2="13"></Line>
<Line Stroke="Red" StrokeThickness="1"
X1="6" Y1="4" X2="13" Y2="11"></Line>
<Ellipse Canvas.Top="4" Canvas.Left="6" Width="7"
Height="7" Stroke="Red"></Ellipse>
</Canvas>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Canvas.Resources>
<Image Canvas.Left="0" Canvas.Top="0" Stretch="Uniform"
Source="{TemplateBinding ImgSource}" Name="mainImg">
<Image.RenderTransform>
<ScaleTransform x:Name="ImgScaleTransform" ScaleX="1" ScaleY="1"></ScaleTransform>
</Image.RenderTransform>
</Image>
<TextBox Name="txtCurrentImg" Text="0" Opacity="0"></TextBox>
<Button Content=">" Style="{StaticResource AlbumButtonStyle}"
Name="btnNext" Opacity="0.1" Height="20" Width="20" >
</Button>
<Button Content="&lt;" Style="{StaticResource AlbumButtonStyle}"
Name="btnPrev" Opacity="0.1" Height="20" Width="20">
</Button>
<Button Style="{StaticResource SlideShowDisabledButtonStyle}"
Name="btnSlideShow" Opacity="0.1" Height="20" Width="20">
</Button>
<Slider x:Name="sldrZoom" Orientation="Vertical" Opacity="0.2"
Height="100" Width="20"
Maximum="10" Minimum="1" Value="1"></Slider>
<Popup Name="ablumList" IsOpen="False">
<ListBox Name="lstAlbums" Margin="0,0,0,0" Padding="0,0,0,0">
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock MinWidth="100" Text="{Binding}" Height="15"
Foreground="Black" FontSize="10"></TextBlock>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Popup>
<Image Name="btnAlbums" Opacity="0.2" Height="30" Width="30"></Image>
</Canvas>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ResourceDictionary>

The album source file (.xml) contains the following format,
which contains the albums (Folder) to be bound to the control
and the pictures file names, which will be copied to the
ClientBin of the project which uses the current control.


<?xml version="1.0" encoding="utf-8" ?>
<Albums>
<Album Name="Babies">
<Pic>baby1.jpg</Pic>
<Pic>baby2.jpg</Pic>
.....
</Album>
......
</Albums>

In the code-behind XAMLAlbum.cs load the above xml file by an
asynchronous call to server, which is referenced by the AlbumSrc
property to get the list of the Albums and the Picture names.
Here I am using GetTemplateChild() method to get the controls
from template at runtime



string[] arrPics;
public static readonly DependencyProperty AlbumSrcProperty =
DependencyProperty.Register("AlbumSrc", typeof(String), typeof(XAMLAlbum), null);
public string AlbumSrc
{
get { return (string)GetValue(AlbumSrcProperty); }
set { SetValue(AlbumSrcProperty, value); }
}

/// 
        /// Override the apply template handler.
        ///
        public override void OnApplyTemplate()
{
base.OnApplyTemplate();

....

SetFirstPicToImage();
}

public int CurrentImg
{
get { return (int)GetValue(CurrentImgProperty); }
set
{
SetValue(CurrentImgProperty, value);
ShowPicture();
}
}
void SetFirstPicToImage()
{
WebClient wc = new WebClient();
wc.OpenReadCompleted += wc_OpenReadCompleted;
wc.OpenReadAsync(new Uri(AlbumSrc, UriKind.Relative));
}
XDocument doc;
private void wc_OpenReadCompleted(object sender, OpenReadCompletedEventArgs e)
{
if (e.Error != null)
{     
return;
}
using (Stream s = e.Result)
{
//load the xml file
                doc = XDocument.Load(s);
//get the current folder name to show that is given by the user in the property ImgSourceProperty
                strFolder = (string)GetValue(ImgSourceProperty);
var pics = from p in doc.Descendants("Album")
where (string)p.Attribute("Name") == strFolder
select p;
var pic = from p in pics.Descendants("Pic")
select (string)p;
arrPics = new string[pic.Count()];
int ind = 0;
foreach (string str in pic.ToArray())
{
arrPics[ind] = str;
ind++;
}

// this will call method ShowPicture() through the property setter - 0 to show the first picture from album
                CurrentImg = 0;
//load album names to listbox
                if (GetTemplateChild("lstAlbums") != null)
{
ListBox lst = ((ListBox)GetTemplateChild("lstAlbums"));

var albums = from p in doc.Descendants("Album")
select (string)p.Attribute("Name");
lst.ItemsSource = albums.ToList();
lst.SelectedIndex = 0;
Canvas root = ((Canvas)GetTemplateChild("root"));

Popup albumList = ((Popup)GetTemplateChild("ablumList"));

double left = 3;
albumList.SetValue(Canvas.LeftProperty, left);
double top = root.Height - albums.Count() * 15 - 56;
albumList.SetValue(Canvas.TopProperty, top);
albumList.MouseLeftButtonUp += new MouseButtonEventHandler(albumList_MouseLeftButtonUp);
lst.SelectionChanged += new SelectionChangedEventHandler(lst_SelectionChanged);
}
}
}
void ShowPicture()
{
if (arrPics.Length == 0) return;

if (CurrentImg < 0)
{
CurrentImg = 0;
return;
}
if (CurrentImg > arrPics.Length - 1)
{
CurrentImg = arrPics.Length - 1;
return;
}

ImageSourceConverter objImgSrc = new ImageSourceConverter();
((Image)GetTemplateChild("mainImg")).SetValue(Image.SourceProperty,
objImgSrc.ConvertFromString(strFolder + "/" + arrPics[CurrentImg]));
}

Build the project to get the .dll file.


Using the control in Silverlight applications


Create a new Silverlight project and add a reference to the
control dll. The control can be added to the page like the
following.


<UserControl xmlns:my="clr-namespace:SLNuke;assembly=SLNuke"
x:Class="SLNukeTest.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d" d:DesignWidth="640" d:DesignHeight="480">
<Grid x:Name="LayoutRoot">
<my:XAMLAlbum ImgSource="Babies" Width="600" Height="500" x:Name="XAlbum"
SlideDuration="3" DisableAlbumSelection="True"
BorderBrush="Black" BorderThickness="1" AlbumSrc="Album.xml" ></my:XAMLAlbum>
</Grid>
</UserControl>


Create a file Album.xml with the above format and copy to
ClientBin of the project. And copy the pictures to the folders
with same name as the album names written in the xml and place
under ClientBin (Note that I copied the pictures of the albums
Babies and Flowers only Nature will not show as the pictures are
not available in the attached project file SLNukeTest).





Points of Interest



The control has the advantage to zoom and play a slideshow.
Zooming functionality is useful when the picture clarity is not
compromised when the image width and height are decreased on the
screen. The album can be set to only one folder and the property
DisableAlbumSelection can be set to true to make the control to
show only one folder pictures and the folder selection button
will be hidden.


Visit http://www.codeproject.com/KB/silverlight/Silverlight_Pic_Album.aspx to download source code.

Friday, April 23, 2010

Currency Masking in Silverlight Datagrid Cell


Introduction

The code shows how to implement currency masking (format like $21,900) in Silverlight DataGrid using the IValueConverter. User can edit the data in numbers with out entering the commas.

Background

Having knowledge in Silverlight data binding to DataGrid is enough to understand the code.

Using the code

Create a simple Silverlight application in Visual Studio and create a class implementing the IValueConverter in the Silverlight project created in the solution, to use the class as value converter to render the values to datagrid cells in the required format.
    ///
/// CurrencyConverter class converts the values to thousand seperated number in the grid.
/// 
public class CurrencyConverter : IValueConverter
{
/// 
/// Convert method will convert to string in a format disirable to show to user.
/// Value to format
/// Formatted string to bing to grid.
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
int? inputInt = value as int?;
if (inputInt.HasValue)
{
return "$" + inputInt.Value.ToString("N0", culture);
}
else
{
decimal? inputDecimal = value as decimal?;
if (inputDecimal.HasValue)
{
return "$"+inputDecimal.Value.ToString("N0", culture);
}
}
return String.Empty;
}

/// 
/// ConvertBack method will convert back the entered text to value
/// value entered by user.
/// Target type to convert/// Current culture
/// Int value of the revenue
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
string input = value as string;
if (input != null)
{
if (input[0] == '$')
{
input = input.Substring(1);
input = input.Replace(",", "").Replace(".", "");
Regex objRegNum = new Regex(@"^\d*[0-9](.\d*[0-9])?$");
if (!objRegNum.IsMatch(input))
input = "0";
return int.Parse(input);
}
else
{
input = "0";
}
}
return value;
}
}
Add the datagrid in your Silverlight page, that looks just like the following -


<UserControl x:Class="CurrencyMasking.MainPage"

xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"

xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"

xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"

mc:Ignorable="d" d:DesignWidth="640" d:DesignHeight="480"

xmlns:data="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Data"

xmlns:local="clr-namespace:CurrencyMasking;assembly=CurrencyMasking">

<UserControl.Resources>

<local:CurrencyConverter x:Key="NumberFormatConverter"/>

</UserControl.Resources>



<Grid x:Name="LayoutRoot">

<data:DataGrid AutoGenerateColumns="False" HorizontalAlignment="Left"

FontWeight="Bold" x:Name="grdEmp" Height="200"

RowBackground="#FF75A0D3" VerticalAlignment="Top"

CanUserResizeColumns="False">

<data:DataGrid.Columns>

<data:DataGridTextColumn Width="100" Header="Name"

Binding="{Binding Name}">

</data:DataGridTextColumn>

<data:DataGridTextColumn Width="100" Header="Salary"

Binding="{Binding Salary, Converter={StaticResource NumberFormatConverter}}">

</data:DataGridTextColumn>

</data:DataGrid.Columns>

</data:DataGrid>

</Grid>



</UserControl>



The 'NumberFormatConverter' resource is created from the class CurrencyConverter. The static resource is binded to the column to render the values in the Currency format.