Archive for the ‘Uncategorized’ Category
Custom ASP.NET Data Pager Control
Monday, April 28th, 2008Hi, i like many others have been frustrated by the limitations of the DataPager control introducted in v3.5 of the .NET framework. The main problem is that it is tied in with the DataSource controls. Whilst i did manage to come up with a solution using the OnPagePropertiesChanging event of the ListView i still couldn’t find a solution to display my pager without using LINQ to SQL or without the Object/Linq DataSource control or without setting the DataSource to the entire record set which is very inefficient. Therefore I set about coming up with my own solution and have come up with:
namespace Flixon.Web.UI.WebControls
{
public class PagerCommandEventArgs : CommandEventArgs
{
public int StartRowIndex { get; set; }
public int MaximumRows { get; set; }public PagerCommandEventArgs(int startRowIndex, int maximumRows, CommandEventArgs originalArgs) : base(originalArgs)
{
this.StartRowIndex = startRowIndex;
this.MaximumRows = maximumRows;
}
}public class Pager : WebControl, INamingContainer
{
public new int Page
{
get
{
if (!string.IsNullOrEmpty(HttpContext.Current.Request.QueryString[this.QueryStringField]))
return Convert.ToInt32(HttpContext.Current.Request.QueryString[this.QueryStringField]);
else
return (int)(this.ViewState["Page"] ?? 1);
}
set { this.ViewState["Page"] = value; }
}public int PageSize
{
get { return (int)(this.ViewState["PageSize"] ?? 15); }
set { this.ViewState["PageSize"] = value; }
}public int TotalRowCount
{
get { return (int)(this.ViewState["TotalRowCount"] ?? 0); }
set { this.ViewState["TotalRowCount"] = value; }
}public int StartRowIndex
{
get { return (this.Page - 1) * this.PageSize; }
}public int TotalPageCount
{
get { return (int)Math.Ceiling((double)this.TotalRowCount / (double)this.PageSize); }
}public string Url
{
get { return (string)(this.ViewState["Url"] ?? string.Empty); }
set { this.ViewState["Url"] = value; }
}public string QueryStringField
{
get { return (string)(this.ViewState["QueryStringField"] ?? string.Empty); }
set { this.ViewState["QueryStringField"] = value; }
}public bool ShowPreviousNextLinks
{
get { return (bool)(this.ViewState["ShowPreviousNextLinks"] ?? true); }
set { this.ViewState["ShowPreviousNextLinks"] = value; }
}private static readonly object EventPagerCommand = new object();
public event EventHandler<PagerCommandEventArgs> PagerCommand
{
add
{
this.Events.AddHandler(EventPagerCommand, value);
}
remove
{
this.Events.RemoveHandler(EventPagerCommand, value);
}
}protected void OnPagerCommand(PagerCommandEventArgs e)
{
EventHandler<PagerCommandEventArgs> handler = (EventHandler<PagerCommandEventArgs>)base.Events[EventPagerCommand];if (handler != null)
handler(this, e);
}protected override void CreateChildControls()
{
if (!string.IsNullOrEmpty(this.QueryStringField))
this.CreatePageForQueryString();
else
this.CreatePagerForCommand();
}private void CreatePageForQueryString()
{
if (this.TotalRowCount > this.PageSize)
this.Controls.Add(new LiteralControl("Page:"));// Add the previous links
if (this.Page > 1 && this.ShowPreviousNextLinks)
{
this.Controls.Add(new LiteralControl(" "));
this.Controls.Add(this.CreateLink("«", 1));
this.Controls.Add(new LiteralControl(" "));
this.Controls.Add(this.CreateLink("‹", this.Page - 1));
}// Loop through the pages
if (this.TotalRowCount > this.PageSize)
{
for (int i = (this.Page <= 2 ? 1 : this.Page - 2); i <= (this.Page >= this.TotalPageCount - 2 ? this.TotalPageCount : this.Page + 2); i++)
{
if (this.Page == i)
this.Controls.Add(new LiteralControl(" <u>" + i.ToString() + "</u>"));
else
{
this.Controls.Add(new LiteralControl(" "));
this.Controls.Add(this.CreateLink(i.ToString(), i));
}
}// Add a link to the last page
if (this.Page < this.TotalPageCount - 2)
{
this.Controls.Add(new LiteralControl(" ... "));
this.Controls.Add(this.CreateLink(this.TotalPageCount.ToString(), this.TotalPageCount));
}
}// Add the next links
if (this.Page < this.TotalPageCount && this.ShowPreviousNextLinks)
{
this.Controls.Add(new LiteralControl(" "));
this.Controls.Add(this.CreateLink("›", this.Page + 1));
this.Controls.Add(new LiteralControl(" "));
this.Controls.Add(this.CreateLink("»", this.TotalPageCount));
}
}private void CreatePagerForCommand()
{
if (this.TotalRowCount > this.PageSize)
this.Controls.Add(new LiteralControl("Page:"));// Add the previous links
if (this.Page > 1 && this.ShowPreviousNextLinks)
{
this.Controls.Add(new LiteralControl(" "));
this.Controls.Add(this.CreateCommand("First", "«", 1));
this.Controls.Add(new LiteralControl(" "));
this.Controls.Add(this.CreateCommand("Previous", "‹", this.Page - 1));
}// Loop through the pages
if (this.TotalRowCount > this.PageSize)
{
for (int i = (this.Page <= 2 ? 1 : this.Page - 2); i <= (this.Page >= this.TotalPageCount - 2 ? this.TotalPageCount : this.Page + 2); i++)
{
if (this.Page == i)
this.Controls.Add(new LiteralControl(" <u>" + i.ToString() + "</u>"));
else
{
this.Controls.Add(new LiteralControl(" "));
this.Controls.Add(this.CreateCommand("PageChange", i.ToString(), i));
}
}
// Add a link to the last page
if (this.Page < this.TotalPageCount - 2)
{
this.Controls.Add(new LiteralControl(" ... "));
this.Controls.Add(this.CreateCommand("Last", this.TotalPageCount.ToString(), this.TotalPageCount));
}
}
// Add the next links
if (this.Page < this.TotalPageCount && this.ShowPreviousNextLinks)
{
this.Controls.Add(new LiteralControl(" "));
this.Controls.Add(this.CreateCommand("Next", "›", this.Page + 1));
this.Controls.Add(new LiteralControl(" "));
this.Controls.Add(this.CreateCommand("Last", "»", this.TotalPageCount));
}
}
private HyperLink CreateLink(string text, int page)
{
HyperLink control = new HyperLink();
control.Text = text;
control.NavigateUrl = string.Format(this.Url, page);
return control;
}
private LinkButton CreateCommand(string commandName, string text, int page)
{
LinkButton control = new LinkButton();
control.Text = text;
control.CommandName = commandName;
control.CommandArgument = page.ToString();
control.Command += new CommandEventHandler(HandleEvent);
return control;
}
private void HandleEvent(Object sender, CommandEventArgs e)
{
this.Page = Convert.ToInt32(e.CommandArgument);
OnPagerCommand(new PagerCommandEventArgs(this.StartRowIndex, this.PageSize, e));
this.ChildControlsCreated = false;
}
}
}
I’m not going to bore you with exactly what it does but instead show you a couple of examples of how it can be used.
Without a DataSource control
With the new control this is now pretty easy because the TotalRowCount property is not read only. All you have to do is set this property once you have bound your data, ie (GetData is a method which passes in the StartRowIndex and the MaximumRows and returns only the rows you want to display for that page and GetDataCount returns the total number of rows):
protected void Page_Load(object sender, EventArgs e)
{
if (!this.IsPostBack)
{
this.BindData(0);
pager.TotalRowCount = this.GetDataCount();
}
}protected void BindData(int startRowIndex)
{
lvwItems.DataSource = this.GetData(startRowIndex, pager.PageSize);
lvwItems.DataBind();
}protected void pager_PagerCommand(Object sender, Flixon.Web.UI.WebControls.PagerCommandEventArgs e)
{
this.BindData(e.StartRowIndex);
}
Then in your aspx file you reference the control in the standard way and put:
<flixon:Pager ID="pager" runat="server" PageSize="15" OnPagerCommand="pager_PagerCommand" />
Without a DataSoruce control (but using the query string to make it seo friendly)
You could modify the above to:
<flixon:Pager ID="pager" runat="server" PageSize="15" QueryStringField="Page" Url="/Default.aspx?Page={0}" />
The PagerCommand event is not necessary if we are using the query string therefore change the code behind file accordingly:
protected void Page_Load(object sender, EventArgs e)
{
if (!this.IsPostBack)
{
lvwItems.DataSource = this.GetData(pager.StartRowIndex, pager.PageSize);
lvwItems.DataBind();
pager.TotalRowCount = this.GetDataCount();
}
}
Hope this helps.
Creating PDF Documents in ASP.Net
Wednesday, February 27th, 2008This article will quickly show you how to create pdf documents in ASP.Net. To get started go to http://itextsharp.sourceforge.net/ and download the iTextSharp library. Now add the library to your web site. We can now begin creating a pdf document.
Step 1. Add a new web form to your website.
Step 2. Place the following at the top of your code behind file:
using iTextSharp.text;
using iTextSharp.text.pdf;
Step 3. Finally place the following in the Page_Load event handler:
// Start pdf writer
MemoryStream ms = new MemoryStream();
Document document = new Document();
PdfWriter.GetInstance(document, ms);
// Open the document for writing
document.Open();// Add a paragraph
Paragraph paragraph = new Paragraph("Hello World");
document.Add(paragraph);
// Close the document
document.Close();
// Output the file
Response.ContentType = "application/pdf";
Response.Clear();
Response.OutputStream.Write(ms.GetBuffer(), 0, ms.GetBuffer().Length);
Response.End();
Obviously this is a very basic example but the iTextSharp is not too well documented for ASP.Net (c#) since it was innitially targetted for Java users.
Below is an example of adding an image to your pdf document (replace the path occordingly):
// Add an image
Image logo = Image.GetInstance(Server.MapPath("~/Images/Image.jpg"));
logo.ScaleAbsolute(100, 16);
document.Add(logo);
The next example shows how you can add a new font and some text which uses that font:
// Arial font
BaseFont arial = BaseFont.CreateFont(Server.MapPath("~/Fonts/Arial.ttf"), BaseFont.WINANSI, BaseFont.EMBEDDED);
// Add a paragraph
Paragraph paragraph = new Paragraph();
paragraph.Add(new Chunk("Text", new Font(arial, 14, Font.NORMAL, new Color(0, 125, 195))));
website.Add(new Chunk("More Text", new Font(arial, 14, Font.NORMAL)));
document.Add(paragraph);
Tables are also very simple to do. To add a table you create an instance of the Table class passing the number of columns you wish your table to have and then add it to the document as you have done above:
Table table = new Table(2); // 2 columns
table.AddCell(new Cell("Row 1: Column: 1"));
table.AddCell(new Cell("Row 1: Column: 2"));
table.AddCell(new Cell("Row 2: Column: 1"));
table.AddCell(new Cell("Row 2: Column: 2"));
document.Add(table);
The final example shows how you can add a new page:
// Add a new page
document.NewPage();
I hope this helps you grasp the basics of creating pdf documents in asp.net.
ASP.NET 2 Way Databinding Suggestions/Improvements
Saturday, November 3rd, 2007Hi, i’ve searched the internet to see if others have documented this problem but i could not find anything so i thought i would post it here. The problem i have is with 2 way databinding.
First off I must say that I am a big fan of 2 way databinding introduced in asp.net 2.0 but i feel it can be improved. The main issue i have is that when you using Bind(”name”) the name must be the same both ways. Imagine i have a DateTime field in my database and i have a calendar to handle the date. Now i have three drop down lists to handle the time (one each for hour, minute and second). I would set the SelectedValue for the hour to <%# Bind(”Date”, “{0:HH}”) %> but the name in my update method is DateHour. The only way i can achieve this is to use Eval and then pass the value on the OnInserting/Updating event handler with the appropriate name. My solution to this would be to have an additional databinding method called EvalBind (as an example) which you can pass 2 (or 3 if formatting) parameters. The first would be for the Eval part and the second the Bind part. Using the example above i could then say <%# EvalBind(”Date”, “DateHour”, “{0:HH}”) %>.
However i still have one further issue with the databinding syntax. I am unable to easily add a default value to a control with 2 way databinding without using the code behind file. Therefore i have come up with a better solution which would be to add an additional property to the controls called Bind (again as an example) and then i could say <asp:DropDownList ID=”ddlDateHour” runat=”server” SelectedValue=’<%# Eval(”Date”, “{0:HH}”) %>’ Bind=”DateHour”>…</asp:DropDownList> on my edit page and on my insert page i could set the SelectedValue (or Text for a TextBox) to a default value.
I know my examples are not brilliant but i have come across these problems many times. Also both solutions above are backwards compatible.
Pivot Tables with Dynamic Columns
Monday, October 29th, 2007Hi, Recently i ran into a situation where i had to pivot/rotate a table (change rows to columns). This makes it very easy to apply filtering and sorting on data which might have seen impossible to do before. The site i used to help me is found at:
http://www.simple-talk.com/community/blogs/andras/archive/2007/09/14/37265.aspx
I found this a great resource and encourage anyone interested in this field to give it a read. One particular case i found for doing this is when dealing with dynamic forms. Imagine i have the following tables:
Attributes:
- AttributeID
- AttributeName
Documents:
- DocumentID
- Title
DocumentValues
- AttributeID
- DocumentID
- Value
The attributes are basically additional columns to the documents table that i can generate dynamically. The values for a document’s attributes are then stored in the DocumentValues table.
I can then construct the following sql query (sql server 2005) which will inter-change the rows to columns and give me the benefits mentioned above:
DECLARE @Cols VARCHAR(2000)SELECT @Cols = COALESCE(@Cols + ',[' + AttributeName + ']', '[' + AttributeName + ']')
FROM Attributes
ORDER BY AttributeName
SET @Query = N'SELECT Documents.*, ' + @Cols + ' FROM
(SELECT DocumentValues.DocumentID, DocumentValues.Value, Attributes.AttributeName FROM Attributes INNER JOIN DocumentValues ON Attributes.AttributeID = DocumentValues.AttributeID) p
PIVOT (
MAX([Value])
FOR AttributeName IN (' + @Cols + ')
) AS DocumentValues INNER JOIN
Documents ON DocumentValues.DocumentID = Documents.DocumentID'
Hope this helps.
Object reference not set to an instance of an object
Friday, October 12th, 2007Hi, this post is more a reminder to myself but for a long time i kept getting the error “Object reference not set to an instance of an object” whilst running my asp.net websites even though the object was not null. I searched google and found my answer at http://forums.aspfree.com/net-development-11/common-causes-of-object-reference-not-set-to-an-instance-45799.html. You simply need to run (using the command prompt):
aspnet_regiis -i
Hope this helps.
Adding an empty data template to the repeater control
Wednesday, September 26th, 2007Hi, i was looking around for an article on how to extend the repeater control to add an empty data template similar to the GridView control. Here is a link to the the article i found. However i discovered that it rendered the header and footer, so i set about creating my own one and come up with the following:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Text;
using System.Web.UI;
using System.Web.UI.WebControls;
namespace ControlLibrary
{
public class Repeater : System.Web.UI.WebControls.Repeater
{
private ITemplate _emptyDataTemplate;[Browsable(false)]
[PersistenceMode(PersistenceMode.InnerProperty)]
public ITemplate EmptyDataTemplate
{
get { return _emptyDataTemplate; }
set { _emptyDataTemplate = value; }
}protected override void Render(HtmlTextWriter output)
{
// If there is no data then we don't wish to render anything (including the header and the footer)
if (this.Items.Count == 0)
{
// If an empty data template has been provided then display it
if (this.EmptyDataTemplate != null)
{
PlaceHolder phdTemplate = new PlaceHolder();
this.EmptyDataTemplate.InstantiateIn(phdTemplate);
phdTemplate.RenderControl(output);
}
}
else
base.Render(output);
}
}
}
Now all you have to do is say:
<%@ Register TagPrefix=”Flixon” Namespace=”ControlLibrary” Assembly=”ControlLibrary” %>
<Flixon:Repeater>
<ItemTemplate>
<%# Container.DataItem %>
</ItemTemplate>
<EmptyDataTemplate>
No data returned!
</EmptyDataTemplate>
</Flixon:Repeater>
on your page and you’re done.
I hope this helps.
Return all results within a category including sub categories
Monday, September 10th, 2007Hi, i recently ran in to a situation where i had to return every article in my database that was under a particular category (including all articles within the sub categories, sub sub categories and so on). The Categories table relates a sub category to a category by a ParentID.
At first i went along the lines of a recursive sql statement but this caused problems when there was no articles in one of the top level categories (not an easy problem to spot). The solution i finally come up with was one i particularly like as it meant i did not have to modify my query much. I simply add the following function:
CREATE FUNCTION [dbo].[GetCategories]
(
@ParentID INT,
@Output VARCHAR(255)
)
RETURNS VARCHAR(255)
AS
BEGIN
IF @Output = ''
BEGIN
SET @Output = Convert(VARCHAR(255), @ParentID)
ENDDECLARE @CategoryID INT
DECLARE c1 CURSOR FOR SELECT CategoryID FROM Categories WHERE ParentID = @ParentID
OPEN c1
FETCH NEXT FROM c1 INTO @CategoryID
WHILE @@FETCH_STATUS = 0
BEGIN
SET @Output = @Output + ', ' + Convert(VARCHAR(255), @CategoryID)
SET @Output = dbo.GetCategories(@CategoryID, @Output)
FETCH NEXT FROM c1 INTO @CategoryID
END
CLOSE c1
DEALLOCATE c1RETURN @Output
END
Now i could execute the following query:
SELECT * FROM Articles WHERE CategoryID IN dbo.GetCategories(1, '')
And it would return all the articles where the CategoryID is 1 or the category is a sub of the category specified.
Hope this helps.