After reading Formatting the DataList and Repeater Based Upon Data (C#) on Microsoft Website I found the following code sample.
protected void ItemDataBoundFormattingExample_ItemDataBound(object sender, DataListItemEventArgs e)
{
if (e.Item.ItemType == ListItemType.Item ||
e.Item.ItemType == ListItemType.AlternatingItem)
{
// Programmatically reference the ProductsRow instance bound
// to this DataListItem
Northwind.ProductsRow product =
(Northwind.ProductsRow)((System.Data.DataRowView)e.Item.DataItem).Row;
// See if the UnitPrice is not NULL and less than $20.00
if (!product.IsUnitPriceNull() && product.UnitPrice < 20)
{
// TODO: Highlight the product's name and price
}
}
}
However in the above I would like to know where Northwind.ProductsRow product is coming from. Is it from the Northwind database name or somewhere else.
this is a great question, and like some time travel movie, I REALLY WISH someone had and did explain how this works. You note that sometimes the "data source" of the datalist works, and sometimes it does not, and why is this so???
So, the data source comes from WHEN the control was "binded" or spoon fed the data source.
So, on page load, there is most likly some code that set-up and "binds" the data control. Or, it might be that a sqldatasource was dropped into the web page, but AGAIN, either way?
The page load will then trigger the row data bound event.
(ItemDataBound).
So, first BIG tip:
The e.Item.DataItem used IS ONLY available DURING the data bind process. That quite much means at data bind time and the ItemDataBound event.
So, WHEN you shove a data source INTO the GridView, Datalist, ListView, repeater etc controls? And use the data bound event (the name varies a wee bit, bu they all do the same thing), then the DataItemView is ONLY available DURING the row data bind process and hence you are 100% free to use that data source DURING the data bind event - BUT NOT AFTER!!!!!
After, binding has occured, you find that the the dataitem (and control datasoruce is NOW null!!!!). This is VERY different then desktop versions of such controls in .net (in desktop land, you actually can get/use/see the ACTUAL object used for each row!!! - and they persist!!!!
In web land, that data source is converted to a DataRowView.
However, what is the basic knowledge here?
Well, it means that your data control does NOT have to display or hide all of the columns from the database, but you ARE STILL FREE to use those additional columns for things like tax calculations, or for formatting controls based on OTHER columns that you have in the datasource BUT ARE NOT DISPLAYED!!!
And you see BOATLOADS of examples posted where people use hidden fields and all kinds of tricks, and they did that because they DID NOT KNOW the above!!!
Now, it not clear if you want to use a "datalist" control. They tend to be good for say one reocrd, or say several records in a "card like" view - not a table like layout.
So, for a table like layout, I suggest using ListView (most flexible), or for simple, and not too fancy, then use GridView.
But, DataList, listView, GridView, Repeater ? They all of this Row bound (or Item bound) event (they all are VERY close named).
And as noted, JUST like your question, in most cases this event can be used for addtional formatting.
Lets do this with a GridView - (since it easy).
So, say we have a grid of hotel names, but I want to highlight in say blue color ONLY hotels that are active. But I ALSO do NOT want to display the "active" column from the data base in that grid.
So, say this simple markup:
<style> .MyCenter {text-align:center;}</style>
<asp:GridView ID="GridView1" runat="server" AutoGenerateColumns="False"
DataKeyNames="ID" cssclass="table" OnRowDataBound="GridView1_RowDataBound">
<Columns>
<asp:BoundField DataField="FirstName" HeaderText="FirstName" />
<asp:BoundField DataField="LastName" HeaderText="LastName" />
<asp:BoundField DataField="City" HeaderText="City" />
<asp:BoundField DataField="HotelName" HeaderText="HotelName" ItemStyle-Width="120px" />
<asp:BoundField DataField="Description" HeaderText="Description" />
<asp:TemplateField HeaderText="View" >
<HeaderStyle CssClass="MyCenter"/>
<ItemStyle CssClass="MyCenter" />
<ItemTemplate>
<asp:Button ID="bView" runat="server" Text="View" CssClass="btn" />
</ItemTemplate>
</asp:TemplateField>
</Columns>
</asp:GridView>
Now, our code to load, we have this:
protected void Page_Load(object sender, EventArgs e)
{
if (!IsPostBack)
LoadGrid();
}
void LoadGrid()
{
using (SqlConnection conn = new SqlConnection(Properties.Settings.Default.TEST4))
{
string strSQL = "SELECT * FROM tblHotelsA ORDER BY HotelName";
using (SqlCommand cmdSQL = new SqlCommand(strSQL,conn))
{
conn.Open();
DataTable rstData = new DataTable();
rstData.Load(cmdSQL.ExecuteReader());
GridView1.DataSource = rstData;
GridView1.DataBind();
}
}
}
And we now have this:
So, lets use the ItemDataBound event (RowdataBound for Gridview) to highlight the Hotel name and descrption ONLY for Hotels that are active. As noted, we do NOT display "Active" column in the gridview, but with Row data bound event, we still have use of the FULL row.
So, we can add this code to the Row data bound event.
protected void GridView1_RowDataBound(object sender, GridViewRowEventArgs e)
{
if (e.Row.RowType == DataControlRowType.DataRow)
{
// get the full data row used for binding
DataRowView OneRow = e.Row.DataItem as DataRowView;
// lets highlist the hotel name + description
if ((bool)OneRow["Active"])
{
e.Row.Cells[3].BackColor = System.Drawing.Color.LightSteelBlue;
e.Row.Cells[4].BackColor = System.Drawing.Color.LightSteelBlue;
}
// that button is not centered "vertical", so lets do that
Button btn = e.Row.FindControl("bView") as Button;
DataControlFieldCell g = btn.Parent as DataControlFieldCell;
g.Style.Add("vertical-align", "middle");
}
}
And now we we get this:
So, what about the view item click? For that we could use a datalist as you have, and we could then hide the grid, display the datalist, and we would be quite much on our way with a grid to edit items, then right?
Coffee break - I'll be back in a bit to add the view + datalist to see/view/edit one item.
Ok, part 2 - using a datalist to display above click
Ok, so lets drop in a data list - with a div area (to hide and show).
So, ok, now our data list we drop in.
<div id="MyEditArea" runat="server" style="width:44%;margin-left:35px;padding:8px;border:solid;display:none">
<asp:Datalist ID="DataList1" runat="server" DataKeyField="ID" >
<ItemTemplate>
<style>
.iForm label {display:inline-block;width:95px}
.iForm input {border-radius:8px;border-width:1px;margin-bottom:10px}
.iForm textarea {border-radius:8px;border-width:1px;margin-bottom:10px}
.iForm input[type=checkbox] {margin-right:6px}
</style>
<div style="float:left" class="iForm">
<label>HotelName</label>
<asp:TextBox ID="txtHotel" runat="server" Text='<%# Eval("HotelName") %>' width="280" /> <br />
<label>First Name</label>
<asp:TextBox ID="tFN" runat="server" Text='<%# Eval("FirstName") %>' Width="140" /> <br />
<label>Last Name</label>
<asp:TextBox ID="tLN" runat="server" Text='<%# Eval("LastName") %>' Width="140" /> <br />
<label>City</label>
<asp:TextBox ID="tCity" runat="server" Text='<%# Eval("City") %>' Width="140" /> <br />
<label>Province</label><asp:TextBox ID="tProvince" runat="server" Text='<%# Eval("Province") %>'
f="Province" Width="75"></asp:TextBox> <br />
</div>
<div style="float:left;margin-left:20px;width:420px " class="iForm">
<label>Description</label> <br />
<asp:TextBox ID="txtNotes" runat="server" Width="400" TextMode="MultiLine"
Height="150px" Text='<%# Eval("Description") %>' ></asp:TextBox> <br />
<asp:CheckBox ID="chkActive" Checked='<%# Eval("Active") %>' Text=" Active" runat="server" TextAlign="Right" />
<asp:CheckBox ID="chkBalcony" Checked='<%# Eval("Balcony") %>' Text=" Has Balcony" runat="server" TextAlign="Right" />
<asp:CheckBox ID="chkSmoking" Checked='<%# Eval("Smoking") %>' Text=" Smoking Area" runat="server" TextAlign="Right" />
</div>
<div style="clear:both"></div>
<div style="float:left" class="iForm">
<asp:Button ID="cmdSave" runat="server" Text="Save" CssClass="btn" />
<asp:Button ID="cmdCancel" runat="server" Text="Cancel" CssClass="btn" style="margin-left:10px" />
</ItemTemplate>
</asp:Datalist>
And now our button click for the grid view - to display that one data list control + data.
Hum, started writing some code routine, so lets clean this up a bit.
Ok, so we now have this to load + format the gridview.
void LoadGrid()
{
GridView1.DataSource = MyRst("SELECT * FROM tblHotelsA ORDER BY HotelName");
GridView1.DataBind();
}
protected void GridView1_RowDataBound(object sender, GridViewRowEventArgs e)
{
if (e.Row.RowType == DataControlRowType.DataRow)
{
// get the full data row used for binding
DataRowView OneRow = e.Row.DataItem as DataRowView;
// lets highlist the hotel name + description
if ((bool)OneRow["Active"])
{
e.Row.Cells[3].BackColor = System.Drawing.Color.LightSteelBlue;
e.Row.Cells[4].BackColor = System.Drawing.Color.LightSteelBlue;
}
// button is not centered "vertical", so lets do that
Button btn = e.Row.FindControl("bView") as Button;
DataControlFieldCell g = btn.Parent as DataControlFieldCell;
g.Style.Add("vertical-align", "middle");
}
}
void CenterControl(Control c)
{
DataControlFieldCell g = c.Parent as DataControlFieldCell;
g.Style.Add("vertical-align", "middle");
}
public DataTable MyRst(string strSQL)
{
DataTable rstData = new DataTable();
using (SqlConnection conn = new SqlConnection(Properties.Settings.Default.TEST4))
{
using (SqlCommand cmdSQL = new SqlCommand(strSQL, conn))
{
conn.Open();
rstData.Load(cmdSQL.ExecuteReader());
}
}
return rstData;
}
And for the button click on the grid, we have this now:
protected void bView_Click(object sender, EventArgs e)
{
Button btn = sender as Button;
GridViewRow gRow = btn.NamingContainer as GridViewRow;
int? PKID = GridView1.DataKeys[gRow.RowIndex]["ID"] as int?;
// Now load our datalist
string strSQL = "SELECT * from tblHotelsA WHERE ID = " + PKID;
DataList1.DataSource = MyRst(strSQL);
DataList1.DataBind();
// hide grid, show edit area
GridView1.Style.Add("display", "none");
MyEditArea.Style.Add("display", "normal");
}
So, now when we click on a row, we hide grid, show our data list control, and we now see/have this:
And just like the grid view, lets highlight the hotelname and description as light grey. Once again, we now use the data list row data bound event.
Say, this:
Note how we don't use .cells for the data list, but have to use find control.
protected void DataList1_ItemDataBound(object sender, DataListItemEventArgs e)
{
if ( (e.Item.ItemType == ListItemType.Item) |
e.Item.ItemType == ListItemType.AlternatingItem)
{
DataRowView gData = e.Item.DataItem as DataRowView;
if ((bool)gData["Active"])
{
TextBox txtHotel = e.Item.FindControl("txtHotel") as TextBox;
txtHotel.BackColor = System.Drawing.Color.LightSteelBlue;
TextBox txtDescript = e.Item.FindControl("txtNotes") as TextBox;
txtDescript.BackColor = System.Drawing.Color.LightSteelBlue;
}
}
}
And, you do have to check for item, and "alternating" item.
But, now, when we click on a grid row, we get this:
Related
So I have a web application that uses a GridView.
We currently add 1 new blank record at a time fill it in and move on. There are times when the data needing to be entered has several fields that need to be duplicated. Is there a way to add a blank row, fill it in , and then copy that row and paste it back into the GridView?
I've looked at clone, but i haven't seen anything that works in a web application. Thanks for any advice.
Well, one way, is you could add a copy button to the grid?
Say we have this grid:
<asp:GridView ID="GridView1" runat="server" AutoGenerateColumns="False" DataKeyNames="ID"
cssclass="table table-hover borderhide">
<Columns>
<asp:TemplateField HeaderText ="First Name">
<ItemTemplate>
<asp:TextBox ID="txtFirst" runat="server" Text = '<%# Eval("FirstName") %>'></asp:TextBox>
</ItemTemplate>
</asp:TemplateField>
<asp:TemplateField HeaderText ="Last Name">
<ItemTemplate>
<asp:TextBox ID="txtLast" runat="server" Text = '<%# Eval("LastName") %>'></asp:TextBox>
</ItemTemplate>
</asp:TemplateField>
<asp:TemplateField HeaderText ="City">
<ItemTemplate>
<asp:TextBox ID="txtCity" runat="server" Text = '<%# Eval("City") %>'></asp:TextBox>
</ItemTemplate>
</asp:TemplateField>
<asp:TemplateField HeaderText ="Active">
<ItemTemplate>
<asp:CheckBox ID="Active" runat="server" Checked = '<%# Eval("Active") %>'></asp:CheckBox>
</ItemTemplate>
</asp:TemplateField>
<asp:TemplateField HeaderText ="Copy">
<ItemTemplate>
<asp:ImageButton ID="cmdCopy" runat="server" Text="Copy"
ImageUrl="~/Content/copy1600.png" Height="32px" Width="32px"
OnClick="cmdCopy_Click" />
</ItemTemplate>
</asp:TemplateField>
</Columns>
</asp:GridView>
<asp:Button ID="cmdSave" runat="server" Text="Save" CssClass="btn-primary" OnClick="cmdSave_Click1" />
<asp:Button ID="cmdAdd" runat="server" Text="Add Row" CssClass="btn-primary" style="margin-left:20px" OnClick="cmdAdd_Click1"/>
<br />
So, code to load this grid up
private DataTable rstPeople = new DataTable();
protected void Page_Load(object sender, EventArgs e)
{
if (!IsPostBack)
{
LoadGrid();
ViewState["MyTable"] = rstPeople;
}
else
rstPeople = (DataTable)ViewState["MyTable"];
}
public void LoadGrid()
{
using (SqlCommand cmdSQL = new SqlCommand("SELECT * from People",
new SqlConnection(Properties.Settings.Default.TEST4)))
{
cmdSQL.Connection.Open();
rstPeople.Load(cmdSQL.ExecuteReader());
GridView1.DataSource = rstPeople;
GridView1.DataBind();
}
}
And the add row button code:
protected void cmdAdd_Click1(object sender, EventArgs e)
{
GridToTable(); // user may have edits
// add a new row to the grid
DataRow OneRow = rstPeople.NewRow();
OneRow["Age"] = 0;
OneRow["Active"] = true;
rstPeople.Rows.Add(OneRow);
GridView1.DataSource = rstPeople;
GridView1.DataBind();
}
So we click on add row, and that gives us this:
So, we have that blank row. But, in place of add row, we could click the copy button, and that would add a row - but copy from current. (I suppose we could have a copy + paste button - but that's WAY TOO much UI here).
So, add row = add blank row.
but, copy row button - add row - copy.
The code for that looks like this:
protected void cmdCopy_Click(object sender, ImageClickEventArgs e)
{
GridToTable(); // user might have done some editing
ImageButton cmdCopy = (ImageButton)sender;
GridViewRow gvRow = (GridViewRow)cmdCopy.Parent.Parent;
DataRow CopyFrom = rstPeople.Rows[gvRow.RowIndex];
DataRow OneRow = rstPeople.NewRow();
OneRow["Age"] = 0;
OneRow["FirstName"] = CopyFrom["FirstName"];
OneRow["LastName"] = CopyFrom["LastName"];
OneRow["City"] = CopyFrom["City"];
OneRow["Active"] = CopyFrom["Active"];
rstPeople.Rows.Add(OneRow);
GridView1.DataSource = rstPeople;
GridView1.DataBind();
}
Now I not shared how teh save button works.
So, the user now can add new rows. Tab around - edit any row. or even copy a new row (and again, edit some more).
So save the whole mess and grid back to the database, we
Send Grid back to table
Send table back to database.
So the save all button is thus this:
protected void cmdSave_Click1(object sender, EventArgs e)
{
GridToTable();
// now send table back to database with updates
string strSQL = "SELECT ID, FirstName, LastName, City, Active from People WHERE ID = 0";
using (SqlCommand cmdSQL = new SqlCommand(strSQL,
new SqlConnection(Properties.Settings.Default.TEST4)))
{
cmdSQL.Connection.Open();
SqlDataAdapter daupdate = new SqlDataAdapter(cmdSQL);
SqlCommandBuilder cmdBuild = new SqlCommandBuilder(daupdate);
daupdate.Update(rstPeople);
}
}
again, really simple. And note how we did not bother with templates etc. (too messy, and really not less code then above).
I suppose you could have for a new row a "paste" icon, but if the users going to have to pick a row and copy - might as well make it "add row + copy" in one operation.
(and it not really a copy button, but more of a "duplicate row" button.
Only routine not posted was the GridToTable. This works, since we always persist the table, but any edits in GV have to be send back to the table, and thus we use this:
void GridToTable()
{
// pull grid rows back to table.
foreach (GridViewRow rRow in GridView1.Rows)
{
int RecordPtr = rRow.RowIndex;
DataRow OneDataRow;
OneDataRow = rstPeople.Rows[RecordPtr];
OneDataRow["FirstName"] = ((TextBox)rRow.FindControl("txtFirst")).Text;
OneDataRow["LastName"] = ((TextBox)rRow.FindControl("txtLast")).Text;
OneDataRow["City"] = ((TextBox)rRow.FindControl("txtCity")).Text;
OneDataRow["Active"] = ((CheckBox)rRow.FindControl("Active")).Checked;
}
}
As noted I suppose you could drop in a "copy" button, and ONLY persist + save the row index, and then have a paste button - this would allow a cut + paste between any rows, and not be limited to just new rows. (but, that would allow the user quite easy to over-write a existing row - again UI overload from user training point of view.
The above trick is quite slick, since any new rows, any edits are sent back to the database in one simple Update operation. (and the provider is smart - if a row was not touched or changed, then sql update statements are not generated.
This whole trick works due to persisting the table in ViewState. Be warned, if you have a datapager, then you would have to call GridToTable in such navigation, or in fact execute a save command.
Also note how we picked up the .parent.parent (grid row) and again did not bother with teh GV event model. And without a gazilion template stuff, we also saved world poverty in terms of not having to mess with mutiple templates.
In fact, for any grid beyond the above, I use the exact same above approch, but perfer a ListView. The reason is that you can just drag + drop in standard asp.net controls, and you don't have to needless surround them with "item template" which again is too much work and effort.
GridView contains ShowDeleteButton CommandField along with other textbox fields.
I'm adding new row to this grid in C# that is adding new textboxes for each newly added row. How to add Delete link while adding new row?
<asp:GridView ID="Gridview1" runat="server" ShowFooter="true" OnRowDataBound="Gridview1_OnRowDataBound"
OnRowDeleting="Gridview1_RowDeleting" AutoGenerateColumns="false" ShowHeaderWhenEmpty="True" EmptyDataText="No Record Available">
<asp:TemplateField HeaderText="Question">
<asp:TextBox ID="txtQuestion" runat="server" Text='<%# Eval("Question") %>'></asp:TextBox>
</asp:TemplateField>
<asp:TemplateField HeaderText="Answer">
<ItemTemplate>
<asp:TextBox ID="txtAnswer" ReadOnly="true" Enabled="false" runat="server" Text='<%# Eval("Answer") %>'></asp:TextBox>
</ItemTemplate>
<FooterStyle HorizontalAlign="Right" />
<FooterTemplate>
<asp:Button ID="btnAddNewQuestionnaire" runat="server" Text="Add New Row" OnClick="btnAddNewQuestionnaire_Click" />
</FooterTemplate>
</asp:TemplateField>
<asp:CommandField ShowDeleteButton="true" />
</Columns>
</asp:GridView>
private void AddNewRow()
{
DataTable dt = new DataTable();
DataRow dr;
dt.Columns.Add(new System.Data.DataColumn("Question", typeof(String)));
dt.Columns.Add(new System.Data.DataColumn("Answer", typeof(String)));
foreach (GridViewRow row in Gridview1.Rows)
{
TextBox txtQuestion = (TextBox)row.FindControl("txtQuestion");
TextBox txtAnswer = (TextBox)row.FindControl("txtAnswer");
dr = dt.NewRow();
dr[0] = txtQuestion.Text;
dr[1] = txtAnswer.Text;
dt.Rows.Add(dr);
}
dt.Rows.Add(dt.NewRow());
dt.Rows[dt.Rows.Count - 1]["Question"] = "";
dt.Rows[dt.Rows.Count - 1]["Answer"] = "";
dt.AcceptChanges();
Gridview1.EditIndex = dt.Rows.Count - 1;
Gridview1.DataSource = dt;
Gridview1.DataBind();
}
I recommend that you simple add the delete button to each row. That way the user can easy delete a row.
I also recommend that you load the grid one time with the datatable, and then add the row to the datatable - NOT the grid.
I could write tuckloads of advantages of this approach. But some advantages are:
user does not get presented with multiple UI
user can edit any row on the grid - not have to hit edit, or save
a single un-do button can un-do all changes
a single SAVE button in ONE operation can save the WHOLE TABLE and change back to database
Your code to add a row, and the code to setup and create the database are seperate. This means that you can change the data table, or even have the source of the table come from sql server. And as noted, this also allows you to save the WHOLE table. The result is the user can edit one rows, many rows - tab around MUCH like a excel spreadsheet. When done, they can:
hit save - all data back to database
hit un-do - un-do all their edits
hit + (add row) - add a new row to edit
hit delete key - it will delete the row in question (but UN-DO STILL ACTIVE!!!).
so we have a far more pleasant way to edit.
And thus, I suggest you don't use the built in delete button. Drop in your own button - it will look nicer anyway. (and thus no edit button required either!!!!).
So, here is our markup:
<asp:GridView ID="GridView1" runat="server" AutoGenerateColumns="False">
<Columns>
<asp:TemplateField>
<ItemTemplate>
<asp:ImageButton ID="ImageButton1" runat="server"
ImageUrl="~/Content/uncheck1.jpg" Height="35px" Width="45px"
OnClick="ImageButton1_Click"
MyRowID = '<%# Container.DataItemIndex %>' />
</ItemTemplate>
</asp:TemplateField>
<asp:TemplateField HeaderText="Question">
<ItemTemplate>
<asp:TextBox ID="txtQuestion" runat="server" Text='<%# Eval("Question") %>'></asp:TextBox>
</ItemTemplate>
</asp:TemplateField>
<asp:TemplateField HeaderText="Answer">
<ItemTemplate>
<asp:TextBox ID="txtAnswer" runat="server" Text='<%# Eval("Answer") %>'></asp:TextBox>
</ItemTemplate>
</asp:TemplateField>
</Columns>
</asp:GridView>
<br />
<asp:Button ID="btnAddNewQuestionnaire" runat="server" Text="+Add New Row" OnClick="btnAddNewQuestionnaire_Click" />
When you drop the button (I used a image button), then in the code editor do this:
Note how RIGHT after you hit "=" then intel-sense pops up - choose create new event. It looks like NOTHING occurs, but if you flip over to code behind, you see a code sub was generated. You can't double click on the controls inside the grid, but you CAN create a event click for a button - or in fact any event for a standard asp.net control this way.
I also to save world poverties also added a custom attribute to that button called MyRowID - this will set the row index for that button.
Since the add new row button is OUTSIDE of the grid, then we can just double click on that button, and jump to the code behind.
Now, our code looks like this:
private DataTable MyTable = new DataTable();
protected void Page_Load(object sender, System.EventArgs e)
{
if (IsPostBack == false)
{
LoadGrid();
ViewState["MyTable"] = MyTable;
}
else
MyTable = ViewState["MyTable"];
}
public void LoadGrid()
{
MyTable.Columns.Add(new DataColumn("Question", typeof(string)));
MyTable.Columns.Add(new DataColumn("Answer", typeof(string)));
GridView1.DataSource = MyTable;
GridView1.DataBind();
}
protected void btnAddNewQuestionnaire_Click(object sender, EventArgs e)
{
DataRow MyRow = MyTable.NewRow;
MyRow("Question") = "my question";
MyRow("Answer") = "my ans";
MyTable.Rows.Add(MyRow);
GridView1.DataSource = MyTable;
GridView1.DataBind();
}
protected void ImageButton1_Click(object sender, ImageClickEventArgs e)
{
ImageButton btn = sender;
int RowID = btn.Attributes.Item("MyRowID");
MyTable.Rows(RowID).Delete();
GridView1.DataSource = MyTable;
GridView1.DataBind();
}
Note VERY carefull - we added a MyTable to the form class. Thus we have a persited table, and we operate against that table.
In fact, the EXACT same above desing can be used if the data came from SQL server. And not only MORE amazing, but we can NOW thus SAVE + SEND the whole table BACK to sql server in one update operation. (you just loop the grid, put values back into table, and execute one save).
And the above has no messy edit button to edit a row. and I suppose we COULD add for extra marks a delete conformation box - the user if they bump or hit delete button, they would get a confirm dialog box. That code could be this:
<asp:ImageButton ID="ImageButton1" runat="server"
ImageUrl="~/Content/uncheck1.jpg" Height="35px" Width="45px"
OnClick="ImageButton1_Click"
MyRowID = '<%# Container.DataItemIndex %>'
OnClientClick="return confirm('Delete this row?');"/>
So now when you hit delete row, you get this:
If you click ok - it deletes - cancel - nothing occurs.
So, you can save a LOT of code and complexity here with:
Persist the data table
Pass the row ID with the button
use the intel-sense trick to wire up a separate and specific button event code.
As noted, this whole above process would ALSO work if you load the table with data from sql server - the REST of the code "as is" will work.
And to save the data back to sql server, then you can as noted:
Pull data from grid back to that persisted table. And then with a data adaptor, ONE update command will send all changes back to sql server.
My question is how can i insert values into gridviews rows.Basically I have created a gridview in which i bound the footer and i want to insert values in the footer.I have create a 'textboxs','dropdownlist' and 'checkboxes'.I want when i insert value and press "Insert" button then values shown in the gridview and again i insert value and press button then show inserted values in the gridview.Here is my gridview image
and i also want to edit and delete rows as well.Here is my code :
aspx code
<asp:GridView ID="GridView1" runat="server" AutoGenerateColumns="False"
Height="104px" ShowFooter="True" Width="463px"
AutoGenerateDeleteButton="True" AutoGenerateEditButton="True">
<Columns>
<asp:TemplateField HeaderText="Column Name">
<FooterTemplate>
<asp:TextBox ID="name" runat="server"></asp:TextBox>
</FooterTemplate>
</asp:TemplateField>
<asp:TemplateField HeaderText="Data Type">
<FooterTemplate>
<asp:DropDownList ID="DropDownList2" runat="server">
</asp:DropDownList>
</FooterTemplate>
</asp:TemplateField>
<asp:TemplateField HeaderText="Allow Null">
<FooterTemplate>
<asp:CheckBox ID="allow_null" runat="server" />
</FooterTemplate>
</asp:TemplateField>
<asp:TemplateField HeaderText="Primary Key">
<FooterTemplate>
<asp:CheckBox ID="primary" runat="server" />
</FooterTemplate>
</asp:TemplateField>
</Columns>
</asp:GridView>
and here is my aspx.cs code :
protected void Button1_Click(object sender, EventArgs e)
{
string name = TextBox1.Text;
string type = DropDownList1.Text;
string allow=Null.Text;
string primary = Primary.Text;
name = ((TextBox)GridView1.FooterRow.FindControl("TextBox2")).Text;
type = ((DropDownList)GridView1.FooterRow.FindControl("DropDownList2")).Text;
allow = ((CheckBox)GridView1.FooterRow.FindControl("allow_null")).Text;
primary = ((CheckBox)GridView1.FooterRow.FindControl("primary")).Text;
}
To use the GridView in this way, with input fields in the footer and a Insert Button that is outside the GridView, you need to make a manual Insert.
I don't know how you perform an Insert using the EF model as I don't currently use it. I guess you could say the "old" way is to use an instance of SqlConnection and SqlCommand. You would use code similar to this inside your Button Click event:
// This is VB. C# is similar, you will have to convert where needed
Using connection As New SqlConnection(connectionString)
Using command As New SqlCommand(queryString, connection)
command.Connection.Open()
command.CommandType = // Typically Text or StoredProcedure as needed
command.Parameters.AddWithValue("#parameter_name1", some_value_1)
command.Parameters.AddWithValue("#parameter_name2", some_value_2)
.
.
.
command.ExecuteNonQuery()
End Using
End Using
queryString is your sql INSERT statement or a stored procedure
command.Parameters is a collection of replaceable parameter in your INSERT statement or Stored Proc.
Addendum
A Gridview is a Data bound control so typically when you use a gridview it's bound to some backing data source. If you are not using a database then you are using some other construct.
If you are using, for example, a DataTable, you would add the new rows and columns to the DataTable using its row and column methods and then rebind the DataTable to the Gridview. You don't add rows and columns directly to a GridView.
See this other SO answer by JonH for an example
In case this helps this is based on the ASPSnippets but modified to be more like you might need. Note that instead of using a DataTable I am using a List where "Row" is a class. It is somewhat a matter of personal preference which one you use. The "Row" class is:
[Serializable]
public class Row
{
public string FieldName { get; set; }
public string FieldType { get; set; }
public Boolean FieldNullible { get; set; }
public Boolean FieldPrimaryKey { get; set; }
}
My names are different from your names. Note that the ASPSnippets sample does not use TemplateFields so I am not. I am not sure if you need to make the "allow"
and/or "primary" fields Boolean so I did not process them in the form. So the ASP.Net form I have is:
<asp:GridView ID="GridView1" runat="server" AutoGenerateColumns="False"
Height="104px" Width="463px"
AutoGenerateDeleteButton="True" AutoGenerateEditButton="True">
<Columns>
<asp:BoundField DataField="FieldName" HeaderText="Name" ItemStyle-Width="120" />
<asp:BoundField DataField="FieldType" HeaderText="Type" ItemStyle-Width="120" />
</Columns>
</asp:GridView>
<br /><asp:Label ID="Label1" runat="server" Text="Name:"></asp:Label>
<br /><asp:TextBox ID="txtName" runat="server" />
<br /><asp:Label ID="Label2" runat="server" Text="Type:"></asp:Label>
<br /><asp:TextBox ID="txtType" runat="server" />
<br /><asp:Button ID="btnAdd" runat="server" Text="Add" OnClick="Insert" />
The code-behind is:
protected void Page_Load(object sender, EventArgs e)
{
if (this.IsPostBack)
return;
List<Row> Rows = new List<Row>();
ViewState["Rows"] = Rows;
BindGrid();
}
protected void BindGrid()
{
GridView1.DataSource = (List<Row>)ViewState["Rows"];
GridView1.DataBind();
}
protected void Insert(object sender, EventArgs e)
{
List<Row> Rows = (List < Row >)ViewState["Rows"];
Row r = new Row();
r.FieldName = txtName.Text.Trim();
r.FieldType = txtType.Text.Trim();
Rows.Add(r);
ViewState["Rows"] = Rows;
BindGrid();
txtName.Text = string.Empty;
txtType.Text = string.Empty;
}
One thing I do not like is that the GridView does not show until there is data in it. I assume you can fix that.
This does not implement the editing and deleting.
I am trying to bind a grid view into a repeater for collapsible panel extender body. Here is the code:
<!-- Collapsible panel extender body -->
<asp:Panel ID="pBody1" runat="server" CssClass="cpBody">
<asp:Label ID="lblBodyText1" runat="server" />
<asp:Repeater ID="Repeater2" runat="server" OnItemDataBound="Repeater2_ItemDataBound">
<ItemTemplate>
<asp:GridView ID="gvProduct" runat="server" AutoGenerateColumns="False">
<Columns>
<asp:BoundField DataField="id" HeaderText="ID" />
<asp:BoundField DataField="name" HeaderText="Name" />
<asp:BoundField DataField="categoryName" HeaderText="Category" />
<asp:BoundField DataField="inventoryQuantity" HeaderText="Quantity" />
</Columns>
</asp:GridView>
</ItemTemplate>
</asp:Repeater>
</asp:Panel>
And from the code behind, I am trying to loop thru the list to get category name. Then, I get all the products based on category name and display them in gridview.
protected void Repeater2_ItemDataBound(object sender, RepeaterItemEventArgs e)
{
// This event is raised for the header, the footer, separators, and items.
//Execute the following logic for Items and Alternating Items.
if (e.Item.ItemType == ListItemType.Item || e.Item.ItemType == ListItemType.AlternatingItem)
{
for (int count = 0; count < categoryList.Count; count++)
{
string category = categoryList[count].categoryName;
List<ProductPacking> prodList = new List<ProductPacking>();
prodList = packBLL.getAllProductByCategory(category);
gvProduct.DataSource = prodList;
gvProduct.DataBind();
}
}
}
However, it told me that gvProduct does not exist in current context. I wonder how can I get the components inside a repeater? Or am I doing in the wrong way?
Updated Portion.
This is how I bind the header for category name. And I am using another repeater:
<asp:Label ID="lblCategory" Text='<%# DataBinder.Eval(Container.DataItem, "categoryName") %>' runat="server" />
And from the code behind, at the page load, I get all the category:
if (!IsPostBack)
{
//Get all category and bind to repeater to loop
categoryList = packBLL.getAllCategory();
Repeater1.DataSource = categoryList;
Repeater1.DataBind();
}
And for repeater2 which shows the product in each category, I edited it to become like this:
protected void Repeater2_ItemDataBound(object sender, RepeaterItemEventArgs e)
{
// This event is raised for the header, the footer, separators, and items.
string category = e.Item.FindControl("categoryName").ToString();
//Execute the following logic for Items and Alternating Items.
if (e.Item.ItemType == ListItemType.Item || e.Item.ItemType == ListItemType.AlternatingItem)
{
GridView gv = (GridView)e.Item.FindControl("gvProduct");
if (gv != null)
{
List<ProductPacking> prodList = new List<ProductPacking>();
prodList = packBLL.getAllProductByCategory(category);
DataRowView drv = (DataRowView)e.Item.DataItem;
gv.DataSource = prodList;
gv.DataBind();
}
}
}
However, when I expand the extender, nothing shows up.
If I remember right, you need to use FindControl to access controls that are part of templates, like
GridView oGV = e.Item.FindControl ( "gvProducts" ) as GridView;
You cannot refer to the GridView as gvProducts because there's no single GridView control with that name - a different instance (with a different name) is created for each data item from the template. You need the instance for the current row.
Here's an MSDN example that shows how to access a control during data binding (the example uses a Label).
I have a gridview with a hyperlink in first column. Upon clicking the hyperlink, the user is redirected to Vendor.aspx. Now, I need to pass the consumer id (of the clicked row) as a query string to the Vendor.aspx.
What is the best method to achieve it? Is there a way in which we can handle it using markup code only?
<asp:GridView ID="grdConsumers" runat="server" AutoGenerateColumns="False"
EnableViewState="True" >
<Columns>
<asp:TemplateField HeaderText="ConsumerID" SortExpression="ConsumerID" >
<ItemTemplate>
<asp:HyperLink ID="lnkConsumerID" href="Vendor.aspx" runat="server"><%# Eval("ConsumerID")%></asp:HyperLink>
</ItemTemplate>
</asp:TemplateField>
<asp:BoundField HeaderText="Status" DataField="Status" SortExpression="Status"></asp:BoundField>
</Columns>
</asp:GridView>
READINGS:
Set Gridview DataNavigateUrlFormatString Dynamically inside User Control(ASCX)
How do I add "&Source" to DataNavigateUrlFormatString?
Select row in GridView with JavaScript
How to bind the URL of a GridView HyperLinkField when the bound value contains a colon?
asp.net gridview DataNavigateUrlFormatString from DataSource
Try using the DataNavigateUrlFormatString
<ItemTemplate>
<asp:HyperLinkField DataNavigateUrlFields="ConsumerID" DataTextField="ConsumerID" DataNavigateUrlFormatString="Vendor.aspx?id={0}" />
</ItemTemplate>
... it will spare you Eval() and the problem with single/double quotes when putting it inside your href.
You can substitute the DataTextField if you like - I just put the ConsumerID there to be consistent with your example.
Rewrite your hyperlink in gridview in .aspx file like this:
<asp:HyperLink ID="lnkConsumerID" runat="server" Text='<%# Eval("ConsumerID")%>' />
Then in code-behind create a RowDataBound event handler:
protected void grdConsumers_RowDataBound(object sender, GridViewRowEventArgs e)
{
if (e.Row.RowType != DataControlRowType.DataRow) return;
var hlnkhlnk = (HyperLink)e.Row.FindControl("lnkConsumerID");
if (hlnkhlnk != null)
{
hlnkhlnk.NavigateUrl = "Vendor.aspx" + "?Consumer ID=" + hlnkhlnk.Text;
}
}
Hope it helps.
You can do same using at Grid view Item Data Bound Event
protected void grdConsumers_ItemDataBound(object sender,DataGridItemEventArgs e)
{
if(e.Item.ItemType == ListItemType.Item || e.Item.ItemType == ListItemType.AlternatingItem)
{
// Get your consumerId here
((HyperLink)e.Item.FindControl("Edit")).NavigateUrl = "Vendor.aspx?id=" + consumerId
}
}