Wednesday, December 31, 2008

Paging,Sorting in datagrid

Paging and sorting is the most common feature of datagrid. As i am very much used to with ObjectDataSource, i don't have to do anything for implementing Paging and sorting.
Just set the datasource of the gridview to the ObjectDataSource and
set the AllowPaging and AllowSorting property of the grid view to true and
for each column put some SortExpression and
some pagesize for the gridview.
And thats all for implementing the paging and sorting. ObjectDataSource will take care of the remainings for you.
But some situalition arises where i must not use ObjectDataSource, actuaaly not using ObjectDataSource is easier from coding perspective though we have to implement the paging and sorting explicitly. So i used this code, a overloaded version of bindGrid() function to handle the situaltion

protected override void OnInit(EventArgs e)
{
base.OnInit(e);
gvStockCode.Sorting += new GridViewSortEventHandler(gvStockCode_Sorting);
gvStockCode.PageIndexChanging += new GridViewPageEventHandler(gvStockCode_PageIndexChanging);
}

void gvStockCode_PageIndexChanging(object sender, GridViewPageEventArgs e)
{
gvStockCode.PageIndex = e.NewPageIndex;
bindGrid(); //Call bindgrid without any parameter so that previous sorting is maintained
}

void gvStockCode_Sorting(object sender, GridViewSortEventArgs e)
{
gvStockCode.PageIndex = 0; //Don't change page index if you want to show the old page the user was
if (ViewState["SortDirection"] != null && ViewState["SortExpression"] != null)
{

SortDirection direction = (SortDirection)ViewState["SortDirection"];
string sortExpr = ViewState["SortExpression"].ToString();
if (sortExpr.ToLower() == e.SortExpression.ToLower())
{
direction = direction == SortDirection.Ascending ? SortDirection.Descending : SortDirection.Ascending;
}
bindGrid(e.SortExpression, direction);
}
else
{
bindGrid(e.SortExpression, e.SortDirection);
}
}
protected void Page_Load(object sender, EventArgs e)
{
If(!IsPostBack)
bindGrid();
}
void bindGrid()
{
SortDirection direction = SortDirection.Ascending; //Default Sort Direction
string sortExpr = ""; //Default sort expression, you can put any expression if wanted to be default for grid
if (ViewState["SortDirection"] != null && ViewState["SortExpression"] != null)
{
direction = (SortDirection)ViewState["SortDirection"];
sortExpr = ViewState["SortExpression"].ToString();
}
bindGrid(sortExpr, direction);
}
void bindGrid(string sortExpression, SortDirection sortDirection)
{
DataTable dt = BusinessLogic.FillTable();//Some function declared in BL

DataView dv = new DataView(dt);
if (sortExpression != string.Empty)
{
if (sortDirection == SortDirection.Ascending)
dv.Sort = sortExpression + " ASC";
else
dv.Sort = sortExpression + " DESC";
}
gvStockCode.DataSource = dv;
gvStockCode.DataBind();
ViewState["SortExpression"] = sortExpression;
ViewState["SortDirection"] = sortDirection;
}

Thursday, October 30, 2008

Trigger validator control validation from javascript

function checkForConfirmRate()
{
for(var i=0;i<Page_Validators.length;i++)
{
if(Page_Validators[i].id != '<%= rfvStockCode.ClientID %>')
{
continue;
}
ValidatorValidate(Page_Validators[i]); //Function to trigger the validation
ValidatorUpdateIsValid();//To show the validation message according to the validator display property
ValidationSummaryOnSubmit();//To show in sumary(if exists)
return Page_IsValid;
}
}

Thursday, September 18, 2008

Run aggregate on datatable column

I had one table with columns "Name" and "Department" which i need to display in a multiline textbox. like below
Name~~~~~~~~ Dept
Tarun Ghosh~~~SWD
Diptangshu Das~SSWD
So that the names and Dept are aligned properly. So i needed the Max of the string length of the column "Name" and give a right-padding to name to make all of same length with the maximum lengthed name.
Firstly i went with the primitive looping through each record and calculate the max of the row, if it is greater than the global max, set global max to current max algo...
But i was not satisfied with the approach even though no problem.
Then i got the
public Object Compute(
string expression,
string filter
)
function of DataTable. Expression par needs to be some aggregate function lik Sum, Avg,Min,Max,Count,Var. with filter expression is same as where clause in dt.Select() function.
But one problemmmmmmmm.... The expression parameter can't work on computed column. Like say you have two column qty and unit price. You need to get the max(qty*unitPrice), then first you have to add a new column to hold the value of the multiplied result and then use the compute function on that column.
So in my case i wanted to get the max of length. So i added a column as

DataColumn col = new DataColumn("Length");
col.Expression = "Len(Name)";
col.DataType = typeof(int);
dt.Columns.Add(col);
object obj= dt.Compute("Max(Length)","");
Response.Write(obj.ToString());

obj is storing the max length value
Some useful link
Link1
Link2

Wednesday, September 17, 2008

Get the date diference in the form of x Years y months z days

Somethimes we need to get the difference of two dates in SQL server to send the result to front end. The result of DateDiff SQL function will give the difference of the date for a particular part like year or month or day... If you think of just concatinating the results got from three datediff function with parameters for year,month and day, you will get the wrong result. WHY???
DateDiff(Year,'2007/31/12','2008/1/1') is 1 though the difference only 1 day.
Similarly month differnce and day difference will be shown as 1.
So the result age is "1 year 1 month 1 day". But actyually only "1 day".
Here is a function i wrote to get the exact value. This is very useful when you need to get the age on today when you have DOB stored in your database. Here you have to send the DOB as @startdate and GetDate() as @endDate

Create FUNCTION [dbo].[fn_GetDateDifference] (@startDate as DateTime,@endDate DateTime)
RETURNS varchar(50) AS
BEGIN
DECLARE @res varchar(50)
DECLARE @mon int,@year int,@day int
SELECT @year = DateDiff(year,@startDate,@endDate)
--SET @year = @day/365
SET @startDate = DateAdd(Year,@Year,@startDate)
if(@startDate > @endDate) --If the month value of Start Date is more than that of End Date
--@startDate can become more than date 2 if the values are like
--@startDate = '2007/12/15' and @endDate = '2008/02/01',
--The datediff year will be 1 though only 2 month+ difference
BEGIN
SET @year = @year - 1
SET @startDate = DateAdd(Year,-1,@startDate)
END
SELECT @mon = DateDiff(Month,@startDate,@endDate)
--SET @mon = @day/28
--Select @mon = DateDiff(Month,@startDate,@endDate)
SET @startDate = DateAdd(Month,@mon,@startDate)
if(@startDate > @endDate)
--Same as year,month can show maximum of 1 unit more if the start date "Day" val in more than end date "Day value"
BEGIN
set @startDate = DateAdd(Month,-1,@startDate)
set @mon = @mon - 1
END
SELECT @day = DateDiff(Day,@startDate,@endDate)
set @res = ''
If(@year > 0)
SELECT @res = Cast(@year as Varchar(10)) + ' Year(s) '
if( @year>0 or @mon>0)
Set @res = @res + Cast(@mon as Varchar(10)) + ' Month(s) '

set @res = @res + Cast(@day as Varchar(10)) + ' Day(s)'
--set @res = @year & ' '
--select @res = @year & ' ' & @mon & ' ' &@day
RETURN @res
END

Note: It is not a good practice to use backend sqlserver for this kind of calculations. It is preferable to return two dates to front end and then do the desired calculations.

Monday, July 14, 2008

Generate Your Dynamic Report Through Reporting Service

I have posted some articles on reporting service. Here is a sample project which will generate report dynamically. It is having an interface where you select the database in a DDL then a table or view from the selected DB and select some columns for details and group of the report. Then it will generate the report RDL and publish it to your report server. If you want to select from multiple tables with joining, you can do that also by giving your custom query.
Here are some setting which you will have to change to run the project like
string serverName = "localhost"; //The server where the report will be published.
string reportVirtualPath = "LocalReportServer"; //The virual directory path of the reportserver
string parentFolder = "QuoteReports"; //The folder where the reports will be published.
Here i am using some AppSettings val in web.config. Change those according your local settings. But in "MasterConStr" the 'Initial Catalog' value should be 'master' because this connection string is used for fetching data about DBs.
Add the web reference of the reporting service to your ptoject. You may have to change the "RSLocal.ReportService2005" AppSettings value.
Here is the link of the project http://w17.easy-share.com/1700907813.html
Here i am using sql express repoting service and my report server and database is in dame machine. If you have the Report server installed in other machine than the Database you may have some problem with the DataSource. For that pls refer to my other post Use stored credential for report created by c# code.

Tuesday, July 8, 2008

Use PageRequestManager To Trigger Javascript for Asynchronous Postback

In my last post Send Balk Data(Datatable) From Child Window To Parent
i explained how we can send bulk data from child window to parent. But if we are using Asp.Net ajax in client page i.e the postback is caused asynchronously, then we can't use the RegisterStartUpScript to call the javascript function. But instead we have to use the Microsoft's Ajax javascript framework. I did this with the use of PageRequestManager JS Class as follows

var postBackThroughAdd = false;
function addRequestManager()
{
var reqMgr=Sys.WebForms.PageRequestManager.getInstance();
reqMgr.add_beginRequest(
function()
{
//Do something before the call begins
}
)
reqMgr.add_endRequest(
function(sender,args)
{
if (args.get_error() == undefined)//Checks there is no error
{
if(postBackThroughAdd)
{
postBackThroughAdd = false;
addToQuote();
//Function to trigger the parent postback and flag set and close the page
}
}
}
)
}

Variable postBackThroughAdd is a flag variable, which is set "true" when the button for which we want to send the data to parent is clicked. The code is like

<asp:Button ID="btnAddPartNoToCatalog" runat="server" Text="Add" CssClass="formButton"OnCommand="btnAddPartNoToCatalog_Command" OnClientClick="postBackThroughAdd = true;" />

Here if any error occurs while setting the data in session through server event btnAddPartNoToCatalog_Command, i am simply throughing the error with some userfriendly message. The message will be shown in JS message box and args.get_error() will not be undefined. So the function addToquote() will not be called.
Note: addRequestManager() function should be called on bodyload so that the JS events are registered before they fires actully. With the help of PageRequestManager class you can do some other tasks like showing some message in div after some action has succeeded of failed, show/hide loading... div without using UpdateProgress but same functionality(In the beginRequest handler, set the div's "display" property to "block" and int endRequest hander display to "none").

Friday, July 4, 2008

Send Balk Data(Datatable) From Child Window To Parent

Currently in one of my .aspx page i was selecting some information from a child window which is opened from a parent window. I opened the window like
window.open('PartNoLookup.aspx','PartNo','left=150, top=60,width=800,height=600, toolbar=no, status=1,scrollbars=yes,resizable=no,dependent=yes');
Some PartNos with their information was selected by the child "PartNoLookup.aspx" page. Now i have to send the part nos to the parent page to be displayed. My parent page was NewQuote.aspx.
Now the problem arises if it was a small value then i could do it with
window.opener.document.getelementById('parentCtrlId').value='Some Value'
But as it was a bulk datatable i can't send it like that.
I stored the DataTable in Session["PartNo"] from the child page. And placed a hidden field "hidFlag" (not with runat='server'. this should be a html hiddenfield) in the Parent Page (i.e. NewQuote.aspx). And wrote some javascript with the Page.ClientScript.RegisterStartupScript like
Page.ClientScript.RegisterStartupScript(typeof(string), "", "refreshParentAndClose();", true);
And inside childpage (PartNoLookup.aspx) i added the JS function refreshParentAndClose() as
function refreshParentAndClose()
{
window.opener.document.getelementById('parentCtrlId').value='checksession'
window.opener.document.forms[0].submit();
window.close();
}
So this function will set the flag in the parent a also submit the page.
Here as the page is postback by some unconventional way, only the PageLoad,PreRender and some processing cycle events will fire in the parent. So i have to check for the flag inside tha Load() event. So i added the checking code in Page_LoadCode as
if(Request.Form["hidFlag"]!=null)
{
string flag=Request.Form["hidFlag"];
switch(flag)
{
case "checksession":
//Check the session and show inside the page.
break;
}
}

Note:Put the hidden field as Html hidden, not with runat=server coz, if you set it server variable then change by the javascript will no be visible as it will be restored from the viewstate. y JavaScript we are changing the value not the stored value inside Encripted ViewState. Also if you use server Hidden field the clientid may not be same as server id. So setting it from child page will be problemetic (but not impossible).