Friday, February 20, 2009

Paging in Datalist and Repeater

Datalist and Repeater doesn't provide any paging by default like gridview or datagrid. So we avoid using datagrid/repeater as much as possible. But there comes situation where using datalist or repeater is easier to maintain the design for the page. Like some product is displayed like row by row with 4 producs in a single w. There its tough to achieve (but not impossible) this design output wit datagrid/gridview. So we choose datalist making its RepeatColumns="4" RepeatDirection="Horizontal".
But paging???
So i created a control named as COUSPaging (Cous is my project name). Which contains two link buttons as "Next" and "Previous" and a textbox for goto page as below



In my control i used a public event "PageIndexChanging" which is of type GridViewPageEventHandler, so that it sends the same parameter as GridView. The event is handled from the Parent page containg the DataList.
The Paging.aspx Page Code:
<table>
<tr>
<td>
<asp:LinkButton ID="linkPrevious" CssClass="button" runat="server" Text="Previous" CommandName="Previous" OnClick="linkPrevious_Click"></asp:LinkButton>
</td>
<td>
<asp:Literal ID="litPageCount" runat="server"></asp:Literal>
<span id="spanGotpPage" runat="server">Goto Page:
<asp:TextBox Width="15" ID="txtPageNo" CssClass="inputfld" runat="server" MaxLength="3"></asp:TextBox>
<asp:Button ID="btnGo" runat="server" Text="Go" CssClass="button" OnClick="btnGo_Click" />
</span>
</td>
<td>
<asp:LinkButton ID="linkNext" CssClass="button" runat="server" Text="Next" CommandName="Next" OnClick="linkNext_Click"></asp:LinkButton>
</td>
</tr>
</table>

.Cs Page Coding:

void Controls_Paging_PreRender(object sender, EventArgs e)
{
litPageCount.Text = string.Format("{0} of {1} Pages ", SelectedPageIndex + 1, TotalNoOfPages);
if (SelectedPageIndex == TotalNoOfPages - 1)
{
linkNext.Enabled = false;
}
else
{
linkNext.Enabled = true;
}
if (SelectedPageIndex == 0)
{
linkPrevious.Enabled = false;
}
else
{
linkPrevious.Enabled = true;
}
txtPageNo.Text = (SelectedPageIndex + 1).ToString();
}
protected void btnGo_Click(object sender, EventArgs e)
{
int newPageIndex = 0;
try
{
newPageIndex = Convert.ToInt32(txtPageNo.Text) - 1;
if (newPageIndex > TotalNoOfPages - 1)
{
newPageIndex = TotalNoOfPages - 1;
}
if (newPageIndex < 0)
{
newPageIndex = 0;
}
SelectedPageIndex = newPageIndex;
PageIndexChanging(btnGo, new GridViewPageEventArgs(newPageIndex));
}
catch { }
}
public event GridViewPageEventHandler PageIndexChanging;
public int PageSize
{
set
{
ViewState["PageSize"] = value;
}
get
{
if (ViewState["PageSize"] is int)
return (int)ViewState["PageSize"];
else return 0;
}
}

public int CurrentPageIndex
{
set
{
ViewState["CurrentPageIndex"] = value;
}
private get
{
if (ViewState["CurrentPageIndex"] is int)
return (int)ViewState["CurrentPageIndex"];
else return 0;
}
}
public int StartIndex
{
get
{
return SelectedPageIndex * PageSize;
}
}

public int TotalNoOfPages
{
get
{
return (int)Math.Ceiling(TotalNoOfRows / (float)PageSize);
}
}
public long TotalNoOfRows
{
set
{
ViewState["TotalNoOfRows"] = value;
}
get
{
if (ViewState["TotalNoOfRows"] is long)
return (long)ViewState["TotalNoOfRows"];
else return 0;
}
}
public int SelectedPageIndex
{
set
{
ViewState["SelectedPageIndex"] = value;
}
get
{
if (ViewState["SelectedPageIndex"] is int)
return (int)ViewState["SelectedPageIndex"];
else return 0;
}
}
protected void linkNext_Click(object sender, EventArgs e)
{
if (SelectedPageIndex < TotalNoOfPages)
{
SelectedPageIndex = SelectedPageIndex + 1;
PageIndexChanging(sender, new GridViewPageEventArgs(SelectedPageIndex));
}
}
protected void linkPrevious_Click(object sender, EventArgs e)
{
if (SelectedPageIndex > 0)
{
SelectedPageIndex = SelectedPageIndex - 1;
PageIndexChanging(sender, new GridViewPageEventArgs(SelectedPageIndex));
}
}

The page where the control is used

Here the page size is initialized. From the cs page call the bind datalist function as

void bindDataList()
{
long totalNoOfRows;
ImageTblMasterTable dtImage = ImageTblMaster.SelectAll(-1, 1, pagingDL.StartIndex, pagingDL.PageSize, out totalNoOfRows);
pagingDL.TotalNoOfRows = totalNoOfRows;
dlImage.DataSource = dtImage;
dlImage.DataBind();
}

Here i am using custom paging. So i am sending the page start index and page size which is converted to ennIndex before the call to SP. Here is code from SP

Select @TotalNoOfRows = Count(*) FROM [dbo].[Image_tbl_Master]

Select * from
(
SELECT ROW_NUMBER() OVER (ORDER BY ImageId) as RowNumber, m.* FROM [dbo].[Image_tbl_Master] as m
)t Where
RowNumber >= @StartIndex
AND
RowNumber <= @EndIndex As i am using SQL Server 2005 i get the facility of ROW_Number() but in SQL Server 2000 or before, there is no row number. So you have to change the query like Select * from ( Select (Select Count(*) from Image_tbl_Master where ImageId<=img.ImageId )as RowNumber, img.* from Image_tbl_Master as img )t Where RowNumber >= @StartIndex
AND
RowNumber <= @EndIndex

Here row number is calculated on the fly through a subquery. See the where clause "ImageId<=img.ImageId ", it is responsible for the sorting. As i was sorting with ImageId i have used it like that.

Tuesday, January 13, 2009

Add / Insert a row to a gridview

In many cases we come across the situation where we have to add a row to a gridview. We can create a new grid view row through the GridViewRow class. But there is no property/method to a grid to add the row to the gridview. Not even the Gridview.Rows property has any methor to add/insert a row.
In a situation i had to add a footer to show the total of the fields displayed in the gridview. I knew that i could set the value in footer through RowDataBound event of GV by inspection like

if(e.Row.RowType==DataControlRowType.Footer)
{

}

here e is the GridViewRowEventArgs parameter to the event.
But i was looking for something new. I found i can create the row through GridViewRow which takes the rowindex, rowtype ... parameters.
Then i found i can cast the parent of any row of gridview to a table but cannot cast the gridview directly to table. So here is how i implemented...
::::::::::::::::::::::::::::::::::::::::::::::::::::::

gvTaxPlanning.DataSource = dv;
gvTaxPlanning.DataBind();
if (gvTaxPlanning.Rows.Count > 0)
{
GridViewRow gvr = new GridViewRow(gvTaxPlanning.Rows.Count, gvTaxPlanning.Rows.Count, DataControlRowType.Footer, DataControlRowState.Normal);
TableCell tc = new TableCell();
tc.Text = "Total";
gvr.Cells.Add(tc);
tc = new TableCell();
tc.Text = (tEQTax + tMFTax + tULTax + tBulTax + tBankTax + tAssetTax).ToString(GlobalSettings.FloatNumberFormatter);
gvr.Cells.Add(tc);
gvr.Font.Bold = true;
gvr.BackColor = System.Drawing.ColorTranslator.FromHtml("#000084");
gvr.ForeColor = System.Drawing.ColorTranslator.FromHtml("#FFFFFF");
Table tab = (Table)gvTaxPlanning.Rows[0].Parent;
tab.Rows.Add(gvr);
}

I thought that it will need the
gridView.ShowFooter = true; to show the new row i have added as it was footer and by default grid doesn't show the footer.
But not... it ws showing the footer whithout setting it to true...
And one more thing... The RowCreated event is not fired when you add this new row.

Read More http://blog.falafel.com/2007/04/12/DynamicallyAddRowsToAGridView.aspx

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.