Tagged: tutorial

Crop & Save (Part II)

This is the second (and the last) post discussing cropping images and saving them to the SQL Server database, using C# ASP.NET and jQuery library called Jcrop. In my previous post, I have created a simple database model that can be used for this purpose, so if you haven’t checked it out, please do. Also, I won’t get into details with Jcrop, since its website is very informative and contains a good manual, as well as demos.

I’ve made a simple demo which can be downloaded here, and I’ll quickly guide you through it, so it could be easily understood. The idea is the following: user clicks on ‘Choose file’ button (which opens FileUpload control), picks an image on their computer and clicks on ‘Upload’ button. At that point, the image is uploaded to the app server and saved to a folder in the file system(in this demo, it is TempImages folder). User can then see the uploaded picture and do the cropping, after which ‘Crop’ button should be clicked. Then, a new bitmap will be created, containing only the area of the original photo selected as the crop area. The bitmap will then be saved to the database as the array of bytes.

FIrst of all, here is the aspx page:

<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="Default.aspx.cs" Inherits="JcropDemo._Default" %>

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
    <title>Jcrop Demo</title>
    <script type="text/javascript" src="//ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
    <script type="text/javascript" src="//ajax.googleapis.com/ajax/libs/jqueryui/1.10.2/jquery-ui.min.js"></script>
    <script type="text/javascript" src="js/jquery.Jcrop.min.js"></script>
    <link rel="Stylesheet" type="text/css" href="css/jquery.Jcrop.min.css" />
    <link rel="Stylesheet" type="text/css" href="css/custom.css" />
    
    <script type="text/javascript">
        jQuery(document).ready(function() {
            
            $preview = $('#preview_pane'),
            $pcnt = $('#preview_pane .preview_container'),
            $pimg = $('#preview_pane .preview_container img'),

            xsize = $pcnt.width(),
            ysize = $pcnt.height();
            
            jQuery(window).load(function() {
                jQuery($('img[bid=EditedImage]')).Jcrop({
                    onChange: doPreviewSave,
                    onSelect: doPreviewSave,
                    boxWidth: 600,
                    boxHeight: 400,
                    aspectRatio: xsize / ysize
                }, function() {

                    // Use the API to get the real image size
                    var bounds = this.getBounds();
                    boundx = bounds[0];
                    boundy = bounds[1];
                    // Store the API in the jcrop_api variable
                    jcrop_api = $('img[bid=EditedImage]');          //this;            

                    // Move the preview into the jcrop container for css positioning
                    $preview.appendTo(jcrop_api.ui.holder);

                });
            });

            function doPreviewSave(c) {
                jQuery($('[id$=HIDDENX]')).val(c.x);
                jQuery($('[id$=HIDDENY]')).val(c.y);
                jQuery($('[id$=HIDDENW]')).val(c.w);
                jQuery($('[id$=HIDDENH]')).val(c.h);

                if (parseInt(c.w) > 0) {
                    var rx = xsize / c.w;
                    var ry = ysize / c.h;

                    $pimg.css({
                        width: Math.round(rx * boundx) + 'px',
                        height: Math.round(ry * boundy) + 'px',
                        marginLeft: '-' + Math.round(rx * c.x) + 'px',
                        marginTop: '-' + Math.round(ry * c.y) + 'px'
                    });
                }
            };
        });       
 
</script>
</head>
<body>
    <form id="form1" runat="server">
    <div>
        <asp:Panel ID="CurrentImagePanel" runat="server">
            <fieldset>
                <legend id="CurrentPhotoLegend" runat="server"></legend>
                    <p>
                    <asp:Image ID="UserImage" runat="server" CssClass="user_photo_large" />
                    <asp:FileUpload ID="ImageUpload" runat="server" />
                </p>
                <p>
                    <asp:Button ID="ImageUploadButton" runat="server" onclick="ImageUploadButton_Click" Text="Upload"/>
                </p>    
                <p>
                    <asp:Label ID="ImageUploadErrorLabel" runat="server"></asp:Label>
                </p>
            </fieldset>            
        </asp:Panel>
        <asp:Panel ID="EditedImagePanel" runat="server">   
            <fieldset>
                <legend><asp:Label ID="EditedPhotoLabel" runat="server"></asp:Label></legend>
                <asp:Image ID="EditedImage" runat="server" bid="EditedImage" />
                <div id="preview_pane">
                    <div class="preview_container">
                        <asp:Image ID="PreviewImage" runat="server" CssClass="jcrop-preview" />
                    </div>
                </div>          
                <p style="clear:both">
                    <asp:Button ID="CropImageButton" runat="server" OnClientClick="return checkCroppedImage();" onclick="CropImageButton_Click" CssClass="custom_button3" Text="Crop"/>
                    <asp:HiddenField ID="HIDDENX" runat="server" />
                    <asp:HiddenField ID="HIDDENY" runat="server" />
                    <asp:HiddenField ID="HIDDENW" runat="server" />
                    <asp:HiddenField ID="HIDDENH" runat="server" />
                </p>  
            </fieldset>              
        </asp:Panel>
    </div>
    </form>
</body>
</html>

So, we have to add references to jQuery and jQueryui, as well as to Jcrop js and css files. There are two panels, CurrentImagePanel, shown before the image has been selected, and EditedImagePanel, where the cropping will take place. Hidden fields HIDDENX, HIDDENY, HIDDENW and HIDDENH are used to store coordinates of the rectangle representing new (cropped) image. On window load, Jcrop’s default behavior is activated. When the selection is made, hidden fields are populated with its values (check doPreviewSave function).

Code behind:

using System;
using System.Linq;
using System.IO;
using System.Web;

namespace JcropDemo
{
    public partial class _Default : System.Web.UI.Page
    {
        private string[] _AllowedExtensions = { ".png", ".jpeg", ".jpg", ".gif" };
        private string _TempImageLocation = "TempImages\\";
        private string _ImagePath = HttpContext.Current.Request.PhysicalApplicationPath + "TempImages\\";
        private int _UserID = 1;

        protected void Page_Load(object sender, EventArgs e)
        {
            if (Session["WorkingImage"] != null)
            {
                EditedImagePanel.Visible = true;
                CurrentImagePanel.Visible = false;

                PreviewImage.ImageUrl = EditedImage.ImageUrl = _TempImageLocation + (string)Session["WorkingImage"];
            }
            else
            {
                EditedImagePanel.Visible = false;
                CurrentImagePanel.Visible = true;

                UserImage.ImageUrl = _TempImageLocation + "noimage.jpg";
            }
        }

        protected void ImageUploadButton_Click(object sender, EventArgs e)
        {
            string imageName = "";
            if (ImageUpload.HasFile)
            {
                string extension = System.IO.Path.GetExtension(ImageUpload.FileName).ToLower();
                if (_AllowedExtensions.Contains(extension))
                {
                    try
                    {
                        imageName = string.Format("{0}_{1}{2}", _UserID, DateTime.Now.ToFileTimeUtc(), extension);
                        ImageUpload.SaveAs(_ImagePath + imageName);

                        EditedImagePanel.Visible = true;
                        CurrentImagePanel.Visible = false;

                        PreviewImage.ImageUrl = EditedImage.ImageUrl = _TempImageLocation + imageName;

                        Session["WorkingImage"] = imageName;
                    }
                    catch (Exception ex)
                    {
                        ImageUploadErrorLabel.Text = "Image could not be uploaded. Details: " + ex.Message;
                    }
                }
                else
                {
                    ImageUploadErrorLabel.Text = "Wrong extension!!!";
                }
            }
        }

        protected void CropImageButton_Click(object sender, EventArgs e)
        {
            if (Session["WorkingImage"] == null)
                return;

            string imageName = Session["WorkingImage"].ToString();

            int w = Convert.ToInt32(Math.Floor(Convert.ToDouble(HIDDENW.Value)));
            int h = Convert.ToInt32(Math.Floor(Convert.ToDouble(HIDDENH.Value)));
            int x = Convert.ToInt32(Math.Floor(Convert.ToDouble(HIDDENX.Value)));
            int y = Convert.ToInt32(Math.Floor(Convert.ToDouble(HIDDENY.Value)));

            byte[] cropImage = Crop(_ImagePath + imageName, w, h, x, y);

            BLL.SaveUserProfilePhoto(_UserID, cropImage);

            File.Delete(_ImagePath + imageName);
            Session["WorkingImage"] = null;

            EditedImagePanel.Visible = false;
            CurrentImagePanel.Visible = true;
        }

        static byte[] Crop(string Img, int Width, int Height, int X, int Y)
        {
            try
            {
                using (System.Drawing.Image originalImage = System.Drawing.Image.FromFile(Img))
                {
                    using (System.Drawing.Bitmap bmp = new System.Drawing.Bitmap(Width, Height))
                    {
                        bmp.SetResolution(originalImage.HorizontalResolution, originalImage.VerticalResolution);
                        using (System.Drawing.Graphics Graphic = System.Drawing.Graphics.FromImage(bmp))
                        {
                            Graphic.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.AntiAlias;
                            Graphic.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.HighQualityBicubic;
                            Graphic.PixelOffsetMode = System.Drawing.Drawing2D.PixelOffsetMode.HighQuality;
                            Graphic.DrawImage(originalImage, new System.Drawing.Rectangle(0, 0, Width, Height), X, Y, Width, Height, System.Drawing.GraphicsUnit.Pixel);
                            MemoryStream ms = new MemoryStream();
                            bmp.Save(ms, originalImage.RawFormat);
                            return ms.GetBuffer();
                        }
                    }
                }
            }
            catch (Exception Ex)
            {
                throw (Ex);
            }
        }
    }
}

In order for this example to be as simple as possible, there is only one user that the image would be added to, hence the variable _UserID is set to 1. In the ImageUploadButton_Click event, the image is saved to a folder on the server. In CropImageButton_Click, the points of the cropped rectangle (represented in the hidden fields) are used to create array of bytes that is later saved to the database. On successful save, the original image is deleted from the file system.

I’ve put the business logic into separate file:

using System;
using System.Configuration;
using System.Linq;
using System.Web;

namespace JcropDemo
{
    public class BLL
    {
        public static Model Model
        {
            get
            {
                if (!HttpContext.Current.Items.Contains("Model"))
                    HttpContext.Current.Items.Add("Model", new Model());

                return (Model)HttpContext.Current.Items["Model"];
            }
        }

        public static void SaveUserProfilePhoto(int userID, byte[] photoContent)
        {
            User usr = Model.Entities.User.Where(x => x.UserId == userID).FirstOrDefault();
            if (usr != null)
            {
                Image img = new Image();
                img.ImageContent = photoContent;
                img.User.Add(usr);

                Model.Entities.AddToImage(img);
                Model.Entities.SaveChanges(true);
            }
        }
    }

    public class Model
    {
        private TestDbEntities _Entities = null;
        public TestDbEntities Entities
        {
            get
            {
                if (_Entities == null)
                {
                    if (ConfigurationManager.ConnectionStrings["TestDbEntities"] == null)
                    {
                        throw new NullReferenceException("Missing connection string!");
                    }
                    _Entities = new TestDbEntities(ConfigurationManager.ConnectionStrings["TestDbEntities"].ConnectionString);
                }
                return _Entities;
            }
        }
    }
}

Static method SaveUserProfilePhoto uses EF model to save the data to the SQL Server database.

So, once again: you can download the source code here, and remember to change your connection settings in Web.config!

Hope this example is useful, and I’ll be glad to get your feedback on it.

Crop & Save (Part I)

Hi all,

I’ve neglected the blog for some time because of the projects I’m currently involved in, but I’ll do my best to write more, I promise! :)

If you make code, you’ve probably at least once had the requirement to save an image in the system. For example, user can upload their profile photo so it could be displayed throughout your application. Let’s see how that could be done in C# ASP.NET, using SQL Server and Entity Framework. Also, we’ll add a cool feature of cropping photos, for which we’ll use jQuery plugin called JCrop. Since this is a fairly long example, I will divide it into couple of posts, first one discussing the database structure.

The database model for this use case is really a basic one. There are three tables: User – where the user information is stored, Image – the same for the photos/images to be uploaded, and UserImage – composite table, containing only primary keys for User and Image tables. Here is the database diagram:

DbModel

Note: User table structure is taken from a project I work on, so the fields in it reflect that app’s requirements. Some other applications may require different fields. In this example, the only field that will actually be used is UserId. I’ve added only one user to the table, having UserId set to 1. Have this in mind when going through this example!

User:

CREATE TABLE [dbo].[User](
       [UserId] [int] IDENTITY(1,1) NOT NULL,
       [Username] [nvarchar](50) NOT NULL,
       [Password] [varbinary](32) NULL,
       [FirstName] [nvarchar](50) NULL,
       [LastName] [nvarchar](50) NULL,
       [Activated] [bit] NULL,
       [Email] [nvarchar](100) NOT NULL,
       [Birthday] [datetime] NULL,
       [GenderId] [int] NULL,
       [DateCreated] [datetime] NULL,
       [DateUpdated] [datetime] NULL,
       [IsDeleted] [tinyint] NULL,
CONSTRAINT [PK_User] PRIMARY KEY CLUSTERED
(
       [UserId] ASC
)WITH (PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS  = ON, ALLOW_PAGE_LOCKS  = ON) ON [PRIMARY]
) ON [PRIMARY]
GO

Image:

CREATE TABLE [dbo].[Image](
       [ImageId] [int] IDENTITY(1,1) NOT NULL,
       [ImageContent] [varbinary](max) NOT NULL,
CONSTRAINT [PK_Image] PRIMARY KEY CLUSTERED
(
       [ImageId] ASC
)WITH (PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS  = ON, ALLOW_PAGE_LOCKS  = ON) ON [PRIMARY]
) ON [PRIMARY]
GO

UserImage:

CREATE TABLE [dbo].[UserImage](
       [UserId] [int] NOT NULL,
       [ImageId] [int] NOT NULL,
CONSTRAINT [PK_UserImage] PRIMARY KEY CLUSTERED
(
       [UserId] ASC,
       [ImageId] ASC
)WITH (PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS  = ON, ALLOW_PAGE_LOCKS  = ON) ON [PRIMARY]
) ON [PRIMARY]
GO

ALTER TABLE [dbo].[UserImage]  WITH CHECK ADD  CONSTRAINT [FK_UserImage_Image] FOREIGN KEY([ImageId])
REFERENCES [dbo].[Image] ([ImageId])
GO

ALTER TABLE [dbo].[UserImage] CHECK CONSTRAINT [FK_UserImage_Image]
GO

ALTER TABLE [dbo].[UserImage]  WITH CHECK ADD  CONSTRAINT [FK_UserImage_User] FOREIGN KEY([UserId])
REFERENCES [dbo].[User] ([UserId])
GO

ALTER TABLE [dbo].[UserImage] CHECK CONSTRAINT [FK_UserImage_User]
GO

Here is the corresponding EF model:

EFModel

Note: If you’re wondering where the UserImage table has disappeared, remember that it is an aggregate table, so in EF model it won’t be represented as entity type, but as association!

Association

In order for this example to be simple, we’ll save only one user to the database and make sure its id is set to 1:

SET IDENTITY_INSERT [User] ON

INSERT INTO [User](UserId, Username, Email)
VALUES (1, 'test', 'test@example.com')

SET IDENTITY_INSERT [User] OFF

When the database is set, we can move on to programming a simple ASP.NET application for saving the images. You can check how it is done in this post.

Getting Shamsi Date From Gregorian Date

Shamsi calendar, also known as Iranian, Persian or Jalāli Calendar is a solar calendar which is still in use in Iran and Afghanistan. If you need to convert date from Gregorian to Jalali calendar and vice versa, here is a handy Iraniar Calendar Converter. On the other hand, if you need to convert the dates in your own application, check the SQL Server function in this article.

So, let’s start. The main function is getShamsiDate, which receives Gregorian date and returns Shamsi date. I won’t get into astronomical explanations, but you can check this link to get more insight.

CREATE FUNCTION [dbo].[getShamsiDate] (@Date smalldatetime)
RETURNS nvarchar(20)
AS
BEGIN
    


DECLARE @Year int,
		@Month int,
		@Day int, 
		@ToEid int, 
		@Elapsed int, 
		@counter int,
		@retVal nvarchar(20),
		@nDay nvarchar(2),
		@nMonth nvarchar(2)

DECLARE @LeapSumOfDays TABLE 
(
	ID int,
	Val int
)

DECLARE @NonLeapSumOfDays TABLE(
	ID int,
	Val int
)

INSERT INTO @LeapSumOfDays
SELECT ROW_NUMBER() OVER (ORDER BY CAST([value] AS int)) AS ID, CAST([value] AS int) AS Val
FROM dbo.SplitStringList ('0,31,62,93,124,155,186,216,246,276,306,336,366')


INSERT INTO @NonLeapSumOfDays
SELECT ROW_NUMBER() OVER (ORDER BY CAST([value] AS int)) AS ID, CAST([value] AS int) AS Val
FROM dbo.SplitStringList ('0,31,62,93,124,155,186,216,246,276,306,336,365')


SET @Year = YEAR(@Date) - 621

IF dbo.isLeapYearShamsi(@Year - 1) = 1 AND dbo.isLeapYear(YEAR(@Date)) = 1
	SET @ToEid = 80
ELSE
	SET @ToEid = 79

IF DATEPART(dy,@Date) <= @ToEid
BEGIN
	SET @Year = @Year-1
	SET @Elapsed = 286 + DATEPART(dy,@Date)
	IF dbo.isLeapYearShamsi(@Year) = 1 AND dbo.isLeapYear(YEAR(@Date)) = 0
		SET @Elapsed = @Elapsed + 1
END
ELSE
	SET @Elapsed = DATEPART(dy,@Date) - @ToEid
     

IF dbo.isLeapYearShamsi(@Year) = 1
BEGIN
	SELECT TOP 1 @counter = ID FROM @NonLeapSumOfDays WHERE val >= @Elapsed
	SET @Month = @counter - 1
	SELECT @Day = @Elapsed - val FROM @NonLeapSumOfDays WHERE ID = @counter - 1
END
ELSE
BEGIN
	SELECT TOP 1 @counter = ID FROM @LeapSumOfDays WHERE val >= @Elapsed
	SET @Month = @counter - 1
	SELECT @Day = @Elapsed - val FROM @LeapSumOfDays WHERE ID = @counter - 1
END


IF LEN(@Day) = 1 
	SET @nDay = '0' + CAST(@Day AS nvarchar(2))
ELSE
	SET @nDay = CAST(@Day AS nvarchar(2))

IF LEN(@Month) = 1 
	SET @nMonth = '0' + CAST(@Month AS nvarchar(2))
ELSE
	SET @nMonth =  CAST(@Month AS nvarchar(2))


SET @retVal = CAST(@Year AS nvarchar(4)) + '/' + @nMonth + '/' + @nDay   

RETURN @retVal

END

There are couple of helper functions used by the main function, and their code follows.

SplitStringList function gets comma separated list of strings and returns one column table, having given string values as table records. It is useful in cases you want to get some values into the table without writing INSERT INTO statement n times.

CREATE FUNCTION [dbo].[SplitStringList]
(
	@StrList nvarchar(4000)
)
RETURNS 
@ParsedList table
(
	value nvarchar(500) COLLATE Arabic_CI_AS
)
AS
BEGIN
	DECLARE @Value nvarchar(500), @Pos int

	SET @StrList = LTRIM(RTRIM(@StrList))+ ','
	SET @Pos = CHARINDEX(',', @StrList, 1)

	IF REPLACE(@StrList, ',', '') <> ''
	BEGIN
		WHILE @Pos > 0
		BEGIN
			SET @Value = LTRIM(RTRIM(LEFT(@StrList, @Pos - 1)))
			IF @Value <> ''
			BEGIN
				INSERT INTO @ParsedList (value) 
				VALUES (CAST(@Value AS nvarchar)) --Use Appropriate conversion
			END
			SET @StrList = RIGHT(@StrList, LEN(@StrList) - @Pos)
			SET @Pos = CHARINDEX(',', @StrList, 1)

		END
	END	
	RETURN
END

Function isLeapYear checks if given year is (Gregorian)leap year. The code is really simple – it is checked if February 29th exists in the given year.

CREATE FUNCTION [dbo].[isLeapYear] (@Year SMALLINT)
RETURNS BIT
AS
BEGIN
    DECLARE @leapDate SMALLDATETIME
    DECLARE @checkDay TINYINT
 
    SET @leapDate = CONVERT(VARCHAR(4), @Year) + '0228'
    SET @checkDay = DATEPART(d, DATEADD(d, 1, @leapDate))
    IF (@checkDay = 29)
        RETURN 1

    RETURN 0  
END

isLeapYearShamsi checks if the given year is Shamsi leap year. Once more, you can check the algorithm, if you want to find more.

CREATE FUNCTION [dbo].[isLeapYearShamsi]( @Year int )
RETURNS bit
AS

BEGIN 

	DECLARE @B int,
			@isLeap bit

	SET @B = @Year % 33

	IF @B IN (1, 5, 9, 13, 17, 22, 26, 30)
		SET @isLeap = 1
	ELSE
		SET @isLeap = 0
	
	RETURN @isLeap

END

So, to get Shamsi representation of, for example, today’s date, call the function:

SELECT dbo.getShamsiDate('20130430')

And the result is:

1392/02/10

Hope this helps.

Google Charts, ASP.NET, jQuery, AJAX – Quick & Easy

What to do when there is a requirement in your application to make a graphical representation of some statistical data? Well, there are a lot of controls you can use, depending on your needs.

In this post, I’ll show you how to quickly and easily introduce Google charts to your ASP.NET application, getting something like this as a result:

GoogleCharts

Also, I’ll be using jQuery and AJAX for fetching data, although they are not necessary, of course.

First of all, let’s define the data we want to show in the chart. This is really basic example, so my class looks something like this:

public class Data
{
    public string ColumnName = "";
    public int Value = 0;

    public Data(string columnName, int value)
    {
        ColumnName = columnName;
        Value = value;
    }
}

This is really all we need to display the chart: data caption and value.

Next, here is the method for getting the data from the server:

[WebMethod]
public static List<Data> GetData()
{
    List<Data> dataList = new List<Data>();

    dataList.Add(new Data("Column 1", 100));
    dataList.Add(new Data("Column 2", 200));
    dataList.Add(new Data("Column 3", 300));
    dataList.Add(new Data("Column 4", 400));

    return dataList;       
}

This is a web method, since I’ll make AJAX call from the client to get JSON object containing data to be displayed. Bear in mind that there is no need for serializing to JSON string – just make the method return the object, or in this case – list of objects.

As for the client script, let’s take a look at the AJAX method for fetching the data from the server:

  
$(document).ready(function() {
    $.ajax({
        type: 'POST',
        dataType: 'json',
        contentType: 'application/json',
        url: 'Default.aspx/GetData',
        data: '{}',
        success:
            function(response) {
                drawVisualization(response.d);
            }
        });
    })
})

Nothing new here – just defining which server method to invoke and what to do when the data is received.

We need to define where the chart will be drawn:

  
<div id="visualization" style="width: 600px; height: 400px;"></div>

To display the Google chart, we must load three libraries: the Google JSAPI API, the Google Visualization library and the library for the chart itself.

First of all, Google JSAPI API:

  
<script type="text/javascript" src="//www.google.com/jsapi"></script>

Next, load the Google Visualization and chart libraries:

  
<script type="text/javascript">
    google.load('visualization', '1', { packages: ['corechart'] });
</script>

Note:
- ‘visualization’ – loads the google.visualization library, which defines all the core utility classes and functions.
- ’1′ – visualization version to load (’1′ is current production version).
- { packages: ['corechart'] } – here we can define the list of all Google chart libraries needed. The ‘corechart’ library contains most basic charts, including pie chart, which we use in this example.

Finally, here is the function that will actually draw the chart:

  
function drawVisualization(dataValues) {
    var data = new google.visualization.DataTable();
    data.addColumn('string', 'Column Name');
    data.addColumn('number', 'Column Value');

    for (var i = 0; i < dataValues.length; i++) {
        data.addRow([dataValues[i].ColumnName, dataValues[i].Value]);
    }

    new google.visualization.PieChart(document.getElementById('visualization')).
        draw(data, { title: "Google Charts Example" });          
}

The code is self-explanatory: the DataTable is initialized, the columns are added to it, and then the data is entered. Finally, new PieChart is initialized and its draw method is invoked.

The whole example:
cs:

 
using System;
using System.Collections.Generic;
using System.Web.Services;

public partial class _Default : System.Web.UI.Page 
{
    protected void Page_Load(object sender, EventArgs e)
    {
        
    }

    [WebMethod]
    public static List<Data> GetData()
    {
        List<Data> dataList = new List<Data>();

        dataList.Add(new Data("Column 1", 100));
        dataList.Add(new Data("Column 2", 200));
        dataList.Add(new Data("Column 3", 300));
        dataList.Add(new Data("Column 4", 400));

        return dataList;       
    }
}

public class Data
{
    public string ColumnName = "";
    public int Value = 0;

    public Data(string columnName, int value)
    {
        ColumnName = columnName;
        Value = value;
    }
}

aspx:

 
<%@ Page Language="C#" AutoEventWireup="true"  CodeFile="Default.aspx.cs" Inherits="_Default" %>

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
    <title></title>
    <script src="//ajax.googleapis.com/ajax/libs/jquery/1.8.0/jquery.min.js" type="text/javascript"></script>
    <script type="text/javascript" src="//www.google.com/jsapi"></script>
    <script type="text/javascript">
        google.load('visualization', '1', { packages: ['corechart'] });
    </script>
    <script type="text/javascript">
        $(document).ready(function() {
            $.ajax({
                type: 'POST',
                dataType: 'json',
                contentType: 'application/json',
                url: 'Default.aspx/GetData',
                data: '{}',
                success:
                    function(response) {
                        drawVisualization(response.d);
                    }

            });
        })

        function drawVisualization(dataValues) {
            var data = new google.visualization.DataTable();
            data.addColumn('string', 'Column Name');
            data.addColumn('number', 'Column Value');

            for (var i = 0; i < dataValues.length; i++) {
                data.addRow([dataValues[i].ColumnName, dataValues[i].Value]);
            }

            new google.visualization.PieChart(document.getElementById('visualization')).
                draw(data, { title: "Google Charts Example" });          
        }

        
    </script>
</head>
<body>
    <form id="form1" runat="server">
        <div id="visualization" style="width: 600px; height: 400px;"></div>
    </form>
</body>
</html>

Google charts are easy to implement and highly customizable. I will be writing more about different types of charts and how to customize them, in my future posts.

jQuery Animate & Highlight Effect

You’ve probably noticed the following feature on facebook: you get the notification that somebody liked your comment on a post; you click on that notification and the post is shown and there is a sliding effect positioning you down to your comment, highlighting it for couple of seconds. It looks pretty cool :)

I’ve thought of adding this effect to the project I’m working on. In my scenario, user types the comment in a textbox and clicks on Save button, or strikes Enter key. The comment is sent to server using ajax and on receiving response that the save was successful, the comment is added to the end of existing list. At that point, there is the animation of scrolling down to that comment and highlighting it.

I’ll present simplified version here, without the actual save on server. Instead, when the text is entered, the comments list is immediately appended. Here is both html and javascript code that you can save on your computer and try for yourself:

<html>
    <head>
        <title>Example</title>
        <link href="//ajax.googleapis.com/ajax/libs/jqueryui/1.9.0/themes/smoothness/jquery-ui.css" rel="stylesheet" type="text/css"/>
        <script type="text/javascript" src="//ajax.googleapis.com/ajax/libs/jquery/1.8.3/jquery.min.js"></script>
        <script type="text/javascript" src="//ajax.googleapis.com/ajax/libs/jqueryui/1.9.2/jquery-ui.min.js"></script>
        <script type="text/javascript">
            function addComment() {
                var comment = $('#NewCommentTextBox')[0].value;
                if (comment == '')
                    return;

                $('#commentPanel').append('<div class="comment">' + comment + '</div>');
                $('#NewCommentTextBox')[0].value = '';
                $('html,body').animate({ scrollTop: $('.comment').last().offset().top }, 1000);
                $('.comment').last().effect("highlight", {}, 2000);
            }

            function onCommentEnter(e) {
                if (e.keyCode == 13) {
                    addComment();
                    return false;
                }
            }
        </script>
    </head>
    <body>
        <input id="NewCommentTextBox" type="text" onkeypress="return onCommentEnter(event);" />
        <input id="AddNewCommentButton" type="button" value="Send" onclick="addComment();" />
        <div id="commentPanel"></div>
    </body>
</html>

The example is really simple, so I won’t go into details, only point to couple of things.

First of all, we need to add the reference to jQuery and jQuery UI libraries. They can be hosted on our servers, but I prefer letting Google to do that work, as you can see in code lines 4-6. You can learn more on why that is a good idea and why is the http: in the URLs missing in a good article here. There are several themes that can be used for highlighting effect, or you can define your own, on jQuery UI website.

On line 15, we define the animation – what to scroll to and how fast. On line 16, the highlighting is done, again telling what to highlight and for how long. The documentation can be found here and here.

This is all really easy to implement, in just couple of lines of javascript code, and the users will surely like it.