Saturday, March 17, 2007

Asp.net : the datagrid

The datagrid controle is a powerful datacontrol but because of the amount of code to get it to work the way you want, it's a hard thing to crack for beginners. All code from this article is used in the example.

The html code



<asp:DataGrid ID="datagrid" runat="server" AutoGenerateColumns="false" ShowFooter="true"

The tag is named asp:DataGrid and like all controls it needs an id and a runat attribute.
The autogeneratecolumns attribute is needed when you want more control over the output of the data, this will be the case most of the time.
The showfooter attribute is selfexplaining and i added it because i want a row to insert new data.

OnEditCommand="datagridEdit" OnCancelCommand="datagridCancel" OnUpdateCommand="datagridUpdate" OnItemDataBound="datagridBound" OnItemCommand="datagridVarCommands" >

These are all hooks to functions for the different events.

Now we are going to style the datagrid. You can do it inside the datagrid tag but for better readability you have different style tags.

<HeaderStyle CssClass="th" />
<AlternatingItemStyle CssClass="even" />
<SelectedItemStyle CssClass="editrow" />

All style attributes can be use inside these tags but they don't have the same names as the standard html tags. There is also a FooterStyle tag.

The next thing to do is to set up the columns.

<Columns>
<asp:BoundColumn HeaderText="Id" DataField="id" Visible="false" ReadOnly="true"></asp:BoundColumn>

<asp:TemplateColumn HeaderText="Title">
<ItemTemplate><asp:Literal ID="title" runat="server"></asp:Literal></ItemTemplate>
<EditItemTemplate><asp:TextBox ID="editTitle" runat="server"></asp:TextBox></EditItemTemplate>
<FooterTemplate><asp:TextBox ID="addTitle" runat="server">></asp:TextBox></FooterTemplate>
</asp:TemplateColumn>

All columns must be inside the Columns tag.
The first column is of the type BoundColumn. This type has the least flexibility but is good for important processing data like an id or if the data can be displayed without manipulation. If you want to get the data from the column it's best to set the readonly attribute true so you can get it in every event as a string. If you set the visible attribute false the column will not be visible on the page but you can use the data that is stored in the column.

The second column is of the TemplateColumn type. Inside the template you have several tags to control the content and/or display of the column. I used ItemTemplate, that is manditory, EditItemTemplate and FooterTemplate. I repeated this for the other columns.
It is allowed to put input fields in every templatetag.

Now that we have the content of our data grid we need to add the buttons to manipulate it.

<asp:EditCommandColumn HeaderText="" EditText="Edit Info" UpdateText="Update" CancelText="Cancel"></asp:EditCommandColumn>

<asp:TemplateColumn HeaderText="">
<ItemTemplate><asp:LinkButton CommandName="Delete" Text="Delete" ID="btnDel" Runat="server" /></ItemTemplate>
<FooterTemplate><asp:LinkButton CommandName="Insert" Text="Toevoegen" ID="btnAdd" Runat="server" /></FooterTemplate>
</asp:TemplateColumn>

There are two special types of command columns. The EditCommandColumn generates three buttons for you:

- the edit button is used to change the row from regular view to edit view.
- the cancel button is used to change the row from edit view to regular view
- the update button is used to commit the changes in the edit view and return to the regular view if needed

These actions aren't programmed by default that is why you need to put the function hooks in the DataGrid tag.

The second command column is the ButtonColumn. It gives you a column with buttons who are bound to a function. It's less code but i need a column where i have delete buttons for the existing rows and an add button for inserting a row. That is why i added another TemplateColumn.

That was it for the html side of the control. If you have a long datagrid you can set the MaintainScrollPositionOnPostback true if you have the 2.0 framework or higher. If you use the 1.x framework you can use the 4GuysFromRolla solution, it worked for me.

C# code


The most important function in the code behind page is Page_Load. Here you call everything you need to display the page. Mine looks like this.

void Page_Load(object sender, EventArgs e)
{
if (!IsPostBack)
{
Data();
}
}

Data is the function that binds the database to the datagrid control. And because the data can be different after a button click it only binds the data when there is no button click. If you don't do this your data will never change because the function is called before all the others.

public void Data()
{
try
{
conn.Open();
string sql = "select id, title,description,online from Content order by title";
SqlCommand com = new SqlCommand(sql, conn);
SqlDataAdapter myDA = new SqlDataAdapter();
myDA.SelectCommand = com;

DataSet myDS = new DataSet();
myDA.Fill(myDS);

datagrid.DataSource = myDS;
datagrid.DataBind();

}
catch (SqlException ex)
{
message.Text = ex.Message;
}
catch (Exception ex)
{
message.Text = ex.Message;
}
finally
{
conn.Close();
}
}

With the datagrid.DataSource variable you transfer the database query to the datagrid and datagrid.DataBind() does exactly what it says.

Now is you data connected with the datagrid but if you would run the code now you would see that only the BoundColumn has values. The template columns need some more attention.

public void datagridBound(Object sender, DataGridItemEventArgs e)
{
// get bound data row
DataRowView row = (DataRowView)e.Item.DataItem;

switch(e.Item.ItemType)
{
// @@@@ EDIT TEMPLATE
case ListItemType.EditItem:
((TextBox)e.Item.FindControl("editTitle")).Text = row["title"].ToString();
((TextBox)e.Item.FindControl("editContent")).Text = row["description"].ToString();
if ("1" == row["online"].ToString()) { ((CheckBox)e.Item.FindControl("editOnline")).Checked = true; }
break;
// @@@@ REGULAR TEMPLATES
case ListItemType.AlternatingItem:
case ListItemType.Item:
((Literal)e.Item.FindControl("title")).Text = row["title"].ToString();
((Literal)e.Item.FindControl("content")).Text = row["description"].ToString();
string online = "No";
if("1" == row["online"].ToString()){ online = "Yes"; }
((Literal)e.Item.FindControl("online")).Text = online;
break;
}

}

The datagridbound function takes care of the data for the regular templates and for the edit template. This way you have all the bound data manipulations in one function.
There is another way to bind the data in the templatetags.

<ItemTemplate><%# DataBinder.Eval(Container, "DataItem.id")%></ItemTemplate>

To manipulate this data you need to create a function, if you need to do this for several columns it can add a lot of code.

Next we have the event functions

public void datagridEdit(Object sender, DataGridCommandEventArgs e)
{
datagrid.EditItemIndex = (int)e.Item.ItemIndex;
Data();
}

public void datagridCancel(Object sender, DataGridCommandEventArgs e)
{
datagrid.EditItemIndex = -1;
Data();
}

These functions are for the edit and cancel button from the EditCommanndColumn.

public void datagridUpdate(Object sender, DataGridCommandEventArgs e)
{
message.Text = "<p>Updated row:</p><p>Id : " + e.Item.Cells[0].Text + "</p>";
message.Text += "<p>Title : " + ((TextBox)e.Item.FindControl("editTitle")).Text+"</p>";
message.Text += "<p>Content : " + ((TextBox)e.Item.FindControl("editContent")).Text + "</p>";
string online = "No";
if (((CheckBox)e.Item.FindControl("editOnline")).Checked) { online = "Yes"; }
message.Text += "<p>Online : " + online + "</p>";
Data();
}

This function supports the update button. Normally you will put your database code to update the databasefields.

public void datagridVarCommands(Object sender, DataGridCommandEventArgs e)
{
switch(e.CommandName)
{
// !!!! DELETE
case "Delete":
message.Text = "<p>Delete row</p><p>Id : " + e.Item.Cells[0].Text + "</p>";
break;
// !!!! ADD
case "Insert":
message.Text = "<p>Insert row:</p>";
message.Text += "<p>Title : " + ((TextBox)e.Item.FindControl("addTitle")).Text + "</p>";
message.Text += "<p>Content : " + ((TextBox)e.Item.FindControl("addContent")).Text + "</p>";
string online = "No";
if (((CheckBox)e.Item.FindControl("addOnline")).Checked) { online = "Yes"; }
message.Text += "<p>Online : " + online + "</p>";
break;
}
}

This last function is used to handle the delete and add buttons.

Conclusion


There is a fair amount of code necessary to come to a working datagrid but there are also parts where the datagrid control does the legwork. You can do a lot more with the datagrid control but the code in this article will come back most of the times.

The full code can be dowloaded.

Friday, March 16, 2007

Menu with javascript elements

There are a lot of javascript menu enchanters but the one thing seems to be missing the ability to degrade the menu.

I made an example that generates a part of the menu in javascript and a part that is accessible all the time. I wanted to have a different design for the non javascript menu.

The code can be shorter but i wrote it out so it's easier to understand.

Saturday, March 03, 2007

Google services going down?

In one day i got accused by google search and blogger of being a robot. Now i know i like technology but there aren't any computerized parts in my body, yet.

At work i was looking for code snippets and suddenly i got a page telling me my computer was infected and i had to fill in the captcha and then i could continue.

When i came home i wanted to write a poem on my other blog but it was flagged as a spam blog. I had to verify again that i was a human but to make it harder the captcha kept disappearing in firefox. I had to do the verification with internet explorer.

I read in the feeds people were having trouble with gmail as well. My gmail is a secondary email so i don't have a problem if it breaks down but people who depend on gmail as their master mail center must have had a hard time.

I think it's a conspiracy to promote the image captcha. People are coming up with other ways to protect their sites against bots and google can't have that. Then they can't do anything with the ocr software they bought or developed, i'm not sure. Tricks to outsmart the king of bots is a big no-no.

Anyway i'm declared human again by google search and blogger, sci-fi has become reality, now i can implant many rfid tags and a bioport for games.