Maintain sorting throughout paging in gridview in asp.net - c#

How to maintain sorting throughout paging in gridview in asp.net.
Below is my code behind for binding grid, paging, and sorting
private string SortDirection
{
get { return ViewState["SortDirection"] != null ? ViewState["SortDirection"].ToString() : "ASC"; }
set { ViewState["SortDirection"] = value; }
}
private void BindGV(string sortExpression = null)
{
string CS = ConfigurationManager.ConnectionStrings["DBCS"].ConnectionString;
SqlConnection con = new SqlConnection(CS);
SqlCommand cmd = new SqlCommand("SPGetEmpDetailes", con);
cmd.CommandType = CommandType.StoredProcedure;
SqlDataAdapter sda = new SqlDataAdapter(cmd);
using (DataTable dt = new DataTable())
{
sda.Fill(dt);
if (sortExpression != null)
{
DataView dv = dt.AsDataView();
this.SortDirection = this.SortDirection == "ASC" ? "DESC" : "ASC";
dv.Sort = sortExpression + " " + this.SortDirection;
EmployeeGV.DataSource = dv;
}
else
{
EmployeeGV.DataSource = dt;
}
EmployeeGV.DataBind();
}
}
I am new to .NET and I want to maintain sorting throughout paging but I don't know how to do it.
protected void EmployeeGV_PageIndexChanging(object sender, GridViewPageEventArgs e)
{
EmployeeGV.PageIndex = e.NewPageIndex;
BindGV();
}
protected void EmployeeGV_Sorting(object sender, GridViewSortEventArgs e)
{
this.BindGV(e.SortExpression);
}

Ok, so since we have a data pager here, then that means the data page routine will re-bind the GV. So, that means:
Loading up - first page load - we fill the grid
When we page - we fill the grid
When we sort - we fill the grid.
So, from above, we see at least 3 places. That suggests we need (and want) ONE routine to load up the grid - since we have several places where we will do this.
And I see you have one routine for this. We tweak it up a bit.
Next up:
The sort value we get/have does not persit, so once again we can use ViewState for that. (and again I see you attempting to use that).
So, lets lay this out.
First up, and NEVER forget this rule:
You want to load combo boxes, grids, listbox and ANY data on your FIRST page load, and ONLY your first page load, and ONLY first time. If you break this rule then you in effect cannot build a working web forms page - you just can't!!!!
And the reason is simple:
Any button click, any post-back, and any code operations on that page will ALWAYS and I repeat ALWAYS will trigger the page load event first and then YOUR event - (button click, paging or whatever). So, that means we ALWAYS will have our page load code stub and have a if (!IsPostBack) code stub - (darn near always follow this design pattern).
Ok, so lets code this up.
next up: I see no reason to put say a datatable in some using block - they safe dispose on their own. However, adopting using blocks for sql connections, and things like a sql command object - yes, do apply a using block.
Ok, I think we quite much cleared up the base issues.
So, lets setup the code, say like this:
So, say our simple markup - we dropped in a row select button.
<div style="padding:35px;width:55%">
<asp:GridView ID="GridView1" runat="server"
AutoGenerateColumns="False" DataKeyNames="ID" CssClass="table"
AllowPaging="True" OnPageIndexChanging="GridView1_PageIndexChanging" PageSize="8" AllowSorting="True" OnSorting="GridView1_Sorting">
<Columns>
<asp:BoundField DataField="FirstName" HeaderText="First" SortExpression="FirstName" />
<asp:BoundField DataField="LastName" HeaderText="Last" SortExpression="LastName" />
<asp:BoundField DataField="HotelName" HeaderText="HotelName" SortExpression="HotelName" />
<asp:BoundField DataField="City" HeaderText="City" SortExpression="City" />
<asp:BoundField DataField="Description" HeaderText="Description" SortExpression="Description" />
<asp:CheckBoxField DataField="Active" HeaderText="Active" ItemStyle-HorizontalAlign="Center" />
<asp:TemplateField>
<ItemTemplate>
<asp:Button runat="server" Text="View" CssClass="btn" />
</ItemTemplate>
</asp:TemplateField>
</Columns>
<PagerStyle CssClass="GridPager" />
</asp:GridView>
</div>
Ok, now our code, say this:
protected void Page_Load(object sender, EventArgs e)
{
if (!IsPostBack)
{
ViewState["MySort"] = "HotelName";
BindGV();
}
}
void BindGV()
{
using (SqlConnection conn = new SqlConnection(Properties.Settings.Default.TEST4))
{
using (SqlCommand cmdSQL = new SqlCommand("SELECT * FROM MyHotels", conn))
{
conn.Open();
DataTable rstData = new DataTable();
rstData.Load(cmdSQL.ExecuteReader());
// apply optional sort
if (ViewState["MySort"] != null)
rstData.DefaultView.Sort = ViewState["MySort"] as string;
GridView1.DataSource = rstData;
GridView1.DataBind();
}
}
}
protected void GridView1_PageIndexChanging(object sender, GridViewPageEventArgs e)
{
GridView1.PageIndex = e.NewPageIndex;
BindGV();
}
protected void GridView1_Sorting(object sender, GridViewSortEventArgs e)
{
ViewState["MySort"] = e.SortExpression;
BindGV();
}
And now we get this:
So, the trick or approach?
We have ONE view sate that holds our sort - when we want to sort, we simply put into this ViewState["MySort"] = some column to sort. After that, our bind + load routines are free to run - and we sort if set.
Thus, the pager routine, sort routine, and load routine -- they all call that one routine, and thus we don't have to write a lot of code.
And the view button? Well, if we click on that button (say we add a click event) like this:
Then the row button click can be this:
protected void Unnamed_Click(object sender, EventArgs e)
{
Button btn = sender as Button;
GridViewRow gRow = btn.NamingContainer as GridViewRow;
Debug.Print("Row index click = " + gRow.RowIndex.ToString());
// get database (hidden pk row id
int? PKID = GridView1.DataKeys[gRow.RowIndex]["ID"] as int?;
Debug.Print("Database PK = " + PKID.ToString());
Debug.Print("Hotel name this row = " + gRow.Cells[2].Text);
}
And clicking, we get:

Related

Replace DataReader data

I'm working on a ASP.Net Web Forms Application.Currently SqlDataReaders directly bind to the two grid views. I need to change the data set before binding it to the grid view .There are two datasets coming from Stored Procedure (Two select Queries). I need to edit both of them and replace some data before binding to grids .What is the best way to achieve this.?
private void BindTable()
{
LiteralMessage.Text = "";
RecentLiteralMessage.Text = "";
ErrorLiteralMessege.Text = "";
var isStandbySelected = SelectedDatabase.SelectedValue == "stats";
using (var db = new Database(isStandbySelected))
{
try
{
//db.BeginTransaction(IsolationLevel.Snapshot);
db.BeginTransaction(); //Need a transaction block to stop from closing connection
db.StoredProcedure = "dbo.NewExportList";
if (!string.IsNullOrEmpty(TextBoxNameFilter.Text))
db.AddParameter("#nameFilter", TextBoxNameFilter.Text);
db.AddParameter("#excludedExports", CheckBoxExcludedExports.Checked);
db.AddParameter("#RunOnReportingServer", SelectedDatabase.SelectedValue == "stats" );
if(CheckBoxRecentlyRun.Checked)
db.AddParameter("#recentExports", true);
System.Data.SqlClient.SqlDataReader reader = db.GetDataReader();
GridViewExports.DataSource = reader;
GridViewExports.DataBind();
if (GridViewExports.Rows.Count > 0)
{
GridViewExports.Visible = true;
}
else
{
GridViewExports.Visible = false;
LiteralMessage.Text = "No Results";
}
GridViewRecentExports.DataSource = null; //clear any exsisting data
if (reader.NextResult()) //Get the second data set if any
{
GridViewRecentExports.DataSource = reader;
}
GridViewRecentExports.DataBind();
if (GridViewRecentExports.Rows.Count > 0)
{
GridViewRecentExports.Visible = true;
}
else
{
GridViewRecentExports.Visible = false;
RecentLiteralMessage.Text = "No Results";
}
db.CloseConnection();
//db.CommitTransaction();
}
catch (Exception ex)
{
}
}
Ok, there are two approaches common used here.
First, lets take a sample GV, and work though the two common apporaches here.
Our simple markup:
<asp:GridView ID="GridView1" runat="server"
AutoGenerateColumns="False" DataKeyNames="ID" CssClass="table">
<Columns>
<asp:BoundField DataField="FirstName" HeaderText="FirstName" />
<asp:BoundField DataField="LastName" HeaderText="LastName" />
<asp:BoundField DataField="HotelName" HeaderText="HotelName" />
<asp:BoundField DataField="Description" HeaderText="Description" />
<asp:BoundField DataField="Nights" HeaderText="Nights" ItemStyle-HorizontalAlign="Center" />
<asp:BoundField DataField="Price" HeaderText="Price" DataFormatString="{0:c2}"
ItemStyle-HorizontalAlign="Right" />
</Columns>
</asp:GridView>
Ok, and our code to load:
protected void Page_Load(object sender, EventArgs e)
{
if (!IsPostBack)
LoadGrid();
}
void LoadGrid()
{
DataTable rstData = new DataTable();
using (SqlConnection conn = new SqlConnection(Properties.Settings.Default.TEST4))
{
string strSQL = "SELECT * FROM tblHotels ORDER BY HotelName";
using (SqlCommand cmdSQL = new SqlCommand(strSQL, conn)) {
conn.Open();
rstData.Load(cmdSQL.ExecuteReader());
}
}
GridView1.DataSource = rstData;
GridView1.DataBind();
}
Ok, and we now have this:
so, with above, we say want the the total price for each row booking to show.
So, lets use the "common" approach here. We first add our extra control to the grid view. Lets use a plane jane text box:
eg: this:
<asp:BoundField DataField="Price" HeaderText="Price" DataFormatString="{0:c2}"
ItemStyle-HorizontalAlign="Right" />
<asp:TemplateField HeaderText="Total">
<ItemTemplate>
<asp:Label ID="lblTotal" runat="server"></asp:Label>
</ItemTemplate>
</asp:TemplateField>
</Columns>
so, we just added a label to the gv.
So, the event we use is the row data bound event This even is great, since it gives us the one data row, and the one grid view row to work with. This event also great for changing the color of a row, or text box, and of course also doing some calculation or setting up the value of a un-bound control - such as our label called
So, now in our row data bound event, we can do this:
protected void GridView1_RowDataBound(object sender, GridViewRowEventArgs e)
{
if (e.Row.RowType == DataControlRowType.DataRow)
{
DataRowView gData = (DataRowView)e.Row.DataItem;
Label lblTotal = e.Row.FindControl("lblTotal") as Label;
decimal MyPrice = (int)gData["Nights"] * (decimal)gData["Price"];
lblTotal.Text = string.Format("{0:c2}", MyPrice);
}
}
And now our gird is this:
Ok, so above is the common approach.
However, a interesting approach?
Once you fill a data table, then you are free to process that table, and that EVEN includes adding new columns to the table!!!
So, lets dump (remove) our data bound row event.
lets CHANGE the label to use bound data from the table. So, our markup is now:
<asp:TemplateField HeaderText="Total">
<ItemTemplate>
<asp:Label ID="lblTotal" runat="server"
Text = '<%# string.Format("{0:c2}", Eval("MyTotal")) %>'
></asp:Label>
</ItemTemplate>
</asp:TemplateField>
</Columns>
</asp:GridView>
Now, we don't' have a column in data table called MyTotal, do we?
but, we can AFTER getting the table from the query or stored procedure ADD the table.
so, our grid load code now becomes this:
DataTable rstData = new DataTable();
using (SqlConnection conn = new SqlConnection(Properties.Settings.Default.TEST4))
{
string strSQL = "SELECT * FROM tblHotels ORDER BY HotelName";
using (SqlCommand cmdSQL = new SqlCommand(strSQL, conn)) {
conn.Open();
rstData.Load(cmdSQL.ExecuteReader());
}
}
rstData.Columns.Add("MyTotal", typeof(decimal));
foreach (DataRow MyRow in rstData.Rows)
{
MyRow["MyTotal"] = (int)MyRow["Nights"] * (decimal)MyRow["Price"];
}
GridView1.DataSource = rstData;
GridView1.DataBind();
}
Note how we just added a new column, and then did a table process loop to setup that value. The results of the GV are thus the same.
Last but not least?
FEW PEOPLE realize that a data table supports expressions!!!
And when you modify values etc. the update automatic. So, in place of that row processing loop above? We could in fact do this:
DataColumn MyCol = new DataColumn("MyTotal", typeof(decimal));
MyCol.Expression = "[Nights] * [Price]";
rstData.Columns.Add(MyCol);
GridView1.DataSource = rstData;
GridView1.DataBind();
So, in most cases, I often just use the row data bound. And this event is nice since you don't write a loop, and for conditional format such as a row color change, or a column/cell format of color etc., or the new setting of the new text box? row data bound is nice.
But, you can also as noted, add columns, and loop + process the data table, and you can even add columns that are based on expressions of other columns. You then send that updated and pre-processed table to the gridview as normal.
Also note that while I used a query, a store procedure would work the same way:
eg:
using (SqlConnection conn = new SqlConnection(Properties.Settings.Default.TEST4))
{
using (SqlCommand cmdSQL = new SqlCommand("MyStoreProcedure", conn)) {
cmdSQL.CommandType = CommandType.StoredProcedure;
cmdSQL.Parameters.Add("#Active", SqlDbType.Bit).Value = 1;
conn.Open();
rstData.Load(cmdSQL.ExecuteReader());
}
}
DataColumn MyCol = new DataColumn("MyTotal", typeof(decimal));
MyCol.Expression = "[Nights] * [Price]";
rstData.Columns.Add(MyCol);
GridView1.DataSource = rstData;
GridView1.DataBind();

C# ASP.NET Add "remove button" to List in grid-view

I am making a checkout program using C# and ASP.NET (because it has to work for offline(as a backup checkout register)).
I got the list working into a GridView but i want to make per added list added to the GridView that it will automatically make a "remove button".
I have tried to find in on the internet but most are database related(removing an item in database) and not just removing a row in a GridView.
what i have:
public static List<KassaItem> KassaList = new List<KassaItem>();
protected void Page_Load(object sender, EventArgs e)
{
TextBox1.Focus();
}
public string Connection(string eannr)
{
// connection stuff here
}
public class KassaItem
{
public string EanNr { get; set; }
public string zoekName { get; set; }
public int Quantity { get; set; }
public double Price { get; set; }
public KassaItem(string eanNr,string zoekname, int quantity, double price)
{
EanNr = eanNr;
zoekName = zoekname;
Quantity = quantity;
Price = price;
}
}
protected void TextBox1_TextChanged(object sender, EventArgs e)
{
double TotalPrice = 0;
string zoekfunctie = Connection(TextBox1.Text);
string[] zoekSplit = zoekfunctie.Split('-');
string zoekNaam = zoekSplit[1];
double Prijs = Convert.ToDouble(zoekSplit[0]);
List<KassaItem> KassaNew = new List<KassaItem>();
bool isNew = true;
if (TextBox1.Text != "")
{
foreach (var KassaItem in KassaList)
{
if (TextBox1.Text == KassaItem.EanNr)
{
KassaNew.Add(new KassaItem(KassaItem.EanNr, KassaItem.zoekName, KassaItem.Quantity + 1, KassaItem.Price + Prijs));
isNew = false;
}
else
{
KassaNew.Add(KassaItem);
}
}
if (isNew)
{
KassaNew.Add(new KassaItem(TextBox1.Text, zoekNaam, 1, Prijs));
}
KassaList = KassaNew;
GridView1.DataSource = KassaList;
GridView1.DataBind();
foreach(var item in KassaList)
{
TotalPrice += item.Price;
}
// here i want to make a button
foreach(var item in KassaList)
{
Button RemoveButton = new Button();
RemoveButton.Text = "remove product??";
RemoveButton.ID = "ButtonRemove_" + Item.EanNr;
}
}
TextBox1.Text = "";
TextBox1.Focus();
TotalItems.Text = TotalPrice.ToString();
}
What I want is basically:
how to make a "button" that is assigned to the row when, the row is created to delete the row when it is clicked.
P.S.
I'm also happy if i get a link to a documentation that i might not have seen/missed.
I wish you a happy new year in advance!
Ok, so we have a grid view. We want to drop in a simple plane jane asp.net button, and when we click on this, we delete that row. But we do NOT YET delete from the database. And we could of course have a button below the gv that says "save changes" and then the deletes would actually occur against the database.
So, lets do the first part. GV, load with data, add delete button.
So we have this markup:
<asp:GridView ID="GHotels" runat="server" CssClass="table"
width="50%" AutoGenerateColumns="False" DataKeyNames="ID" >
<Columns>
<asp:BoundField DataField="FirstName" HeaderText="FirstName" />
<asp:BoundField DataField="LastName" HeaderText="LastName" />
<asp:BoundField DataField="HotelName" HeaderText="HotelName" />
<asp:BoundField DataField="City" HeaderText="City" />
<asp:BoundField DataField="Description" HeaderText="Description" />
<asp:TemplateField HeaderText = "Delete" ItemStyle-HorizontalAlign ="Center" >
<ItemTemplate>
<asp:Button ID="cmdDelete" runat="server" Text="Delete"
CssClass="btn"
/>
</ItemTemplate>
</asp:TemplateField>
</Columns>
</asp:GridView>
<br />
<asp:Button ID="cmdSave" runat="server" Text="Save Changes" CssClass="btn" />
<asp:Button ID="cmdUndo" runat="server" Text="Undo Changes" CssClass="btn"
style="margin-left:25px" />
I dropped in two buttons below the GV - we deal with those later.
So, now here is our code to load the GV - NOTE very careful how we persit the table that drives this table. So we can load the table from database, and now our operations can occur against that table that drives the gv - but we do NOT send the changes back to the database (changes in this case = deletes - but it could be edits or additions if we want - and that's easy too!).
Ok, so our code is this:
DataTable rstData = new DataTable(); // data to drive grid
protected void Page_Load(object sender, EventArgs e)
{
if (!IsPostBack)
{
LoadData();
Session["rstData"] = rstData;
LoadGrid();
}
else
{
rstData = Session["rstData"] as DataTable;
}
}
void LoadData()
{
using (SqlConnection conn = new SqlConnection(Properties.Settings.Default.TEST4))
{
string strSQL = "SELECT * FROM tblHotels ORDER BY HotelName";
using (SqlCommand cmdSQL = new SqlCommand(strSQL, conn))
{
conn.Open();
rstData.Load(cmdSQL.ExecuteReader());
}
}
}
void LoadGrid()
{
GHotels.DataSource = rstData;
GHotels.DataBind();
}
}
and now we have this:
So, now lets add the code behind event stub for the delete button:
We can't double click on the button to jump to the click code behind event stub, so we have to type into the markup the onclick= (note VERY close how you are THEN offered to create a click event for the button. You see this:
So, we choose create new event (don't look like much occures, but we can now flip to code behind - and you see our event stub created for us.
Ok, so all we have to do is delete (remove) the row from the gv. The code looks like this:
protected void cmdDelete_Click(object sender, EventArgs e)
{
Button btn = sender as Button;
GridViewRow gRow = btn.NamingContainer as GridViewRow;
int? pkID = (int?)GHotels.DataKeys[gRow.DataItemIndex]["ID"];
// find row and delete
DataRow[] DelRow = rstData.Select("ID = " + pkID);
DelRow[0].Delete();
LoadGrid();
}
Now, the delete code was a bit messy. In MOST cases, I could have done just this:
rstData.Rows[gRow.DataItemIndex].Delete();
However, when you delete from the table, we NOT YET done a rstData.AcceptChanges. So the row sync between table and Gv gets messed up.
Now I COULD DO a accept changes to the table, but if we do that, then we LOSE the deleted rows - and we NEED that for the single final save button that would actually commit and do the deletes. So we use find on the table by PK row id, and we eliminate this issue but MORE important PERSERVE the deleted rows in that data table. (by NOT doing accept changes). And we do that, since now we can send with ONE SINGLE update to the database, the deleted rows. However, as noted, the problem is that when you delete the rstData row, but not accept changes - it STILL exists, an thus when we feed to GV, those deleted rows don't show - but the data index is still set, but dataindex at that point does not match rows from the table.
So, now when we click on the delete button, the row will be deleted. But we not deleted the row from the database.
The undo command? Well, it would just run the same code we have on first page (postback = false) code, and thus we could un-do deletes with great ease.
As you can see - not a lot of code here.
And if you wish, I can post the actual delete code we would use for this to commit the deletes to the database. With above, it is only ONE update command to send the deletes to the database.
Thanks Albert but got a way answer to this.
C#:
protected void GridView1_RowDeleting(object sender, GridViewDeleteEventArgs e)
{
List<KassaItem> KassaNew = new List<KassaItem>();
TableCell NR = GridView1.Rows[e.RowIndex].Cells[1];
string eanrNR = Convert.ToString(NR.Text);
string zoekfunctie = Connection(eanrNR);
string[] zoekSplit = zoekfunctie.Split('-');
string zoekNaam = zoekSplit[1];
double Prijs = Convert.ToDouble(zoekSplit[0]);
GridViewRow row = GridView1.Rows[e.RowIndex];
TableCell cell = GridView1.Rows[e.RowIndex].Cells[3];
int quantity = Convert.ToInt32(cell.Text);
int newquantity = quantity - 1;
if (newquantity == 0)
{
KassaList.RemoveAt(e.RowIndex);
}
else
{
foreach (var KassaItem in KassaList)
{
if (eanrNR == KassaItem.EanNr)
{
KassaNew.Add(new KassaItem(KassaItem.EanNr, KassaItem.zoekName, newquantity, KassaItem.Price - Prijs));
}
else
{
KassaNew.Add(KassaItem);
}
}
KassaList = KassaNew;
}
foreach (var item in KassaList)
{
TotalPrice += item.Price;
}
GridView1.DataSource = KassaList;
GridView1.DataBind();
TextBox1.Text = "";
TextBox1.Focus();
TotalItems.Text = TotalPrice.ToString();
}
protected void GridView1_RowDeleted(object sender, GridViewDeletedEventArgs e)
{
}

Populate correlated data to selected row based on id C#

I'm trying to populate matching patientID data from Patients table with Demographics table. I'm using two different forms. My latest try is to join both tables and call .Where() to match the ID.
This works but it calls all matched data. The goal is to call Only the selected row from the Patients form. Initially I tried to select the PatientID then match it with C_PatientID but no luck.
var patientID = _db.Patients.Where(i => i.PatientID > 0).Select(i => new
{
PatientNumber = i.PatientID,
});
My latest attempt is to use the selected row index but have not figured out how to properly implement it to call the patients data.
var patientID = gvDemographics.SelectedCells.Cast<DataGridViewCell>()
.Select(cell => cell.RowIndex)
.Distinct();
The initial form to be loaded is the patients the it should call the demographics once the row is selected. Please advice.
Using Microsoft SQL Server Management Studio for data. First form to populate patients
private void PopulateGrid()
{
var records = _db.Patients.Select(q => new
{
PatientNumber = q.PatientID,
PatientFirstName = q.PatientFirstName,
PatientLastName = q.PatientLastName,
DateOfBirth = q.DateOfBirth,
Gender = q.Gender,
Address = q.Address,
State = q.State,
City = q.City,
ZipCode = q.ZipCode,
ContactNumber = q.ContactNumber,
Email = q.Email,
}).ToList();
gvPatients.DataSource = records;
gvPatients.Columns["PatientNumber"].HeaderText = "Patient#";
gvPatients.Columns["PatientFirstName"].HeaderText = "First Name";
gvPatients.Columns["PatientLastName"].HeaderText = "Last Name";
gvPatients.Columns["DateOfBirth"].HeaderText = "DOB";
gvPatients.Columns["ZipCode"].HeaderText = "Zip Code";
gvPatients.Columns["ContactNumber"].HeaderText = "Contact Number";
//Hide the column for ID. Changed from the hard coded column value to the name,
// to make it more dynamic.
gvPatients.Columns["PatientNumber"].Visible = true;
}
button to call second form
private void btnViewMedicalHistory_Click(object sender, EventArgs e)
{
var viewMedicalHistory = new Demographics();
viewMedicalHistory.MdiParent = this.MdiParent;
viewMedicalHistory.Show();
}
It would help if you noted what control or how you displaying the data on the first page.
I mean, say I have a list of hotels - like this:
<asp:GridView ID="GridView1" runat="server" CssClass="table" Width="45%"
AutoGenerateColumns="False" DataKeyNames="ID" >
<Columns>
<asp:BoundField DataField="FirstName" HeaderText="FirstName" />
<asp:BoundField DataField="LastName" HeaderText="LastName" />
<asp:BoundField DataField="HotelName" HeaderText="HotelName" />
<asp:BoundField DataField="City" HeaderText="City" />
<asp:BoundField DataField="Description" HeaderText="Description" />
<asp:TemplateField HeaderText="View">
<ItemTemplate>
<asp:Button ID="cmdView" runat="server" Text="View" cssclass="btn"
OnClick="cmdView_Click"
/>
</ItemTemplate>
</asp:TemplateField>
</Columns>
</asp:GridView>
And we fill the grid like this:
protected void Page_Load(object sender, EventArgs e)
{
if (!IsPostBack)
LoadGrid();
}
void LoadGrid()
{
using (SqlConnection conn = new SqlConnection(Properties.Settings.Default.TEST4))
{
using (SqlCommand cmdSQL = new SqlCommand("SELECT * from tblHotels ORDER BY HotelName", conn))
{
conn.Open();
DataTable rst = new DataTable();
rst.Load(cmdSQL.ExecuteReader());
GridView1.DataSource = rst;
GridView1.DataBind();
}
}
}
So our output is now like this:
And the button click to get the primary key of the database row is this:
protected void cmdView_Click(object sender, EventArgs e)
{
Button btn = (Button)sender;
GridViewRow gRow = (GridViewRow)btn.Parent.Parent;
int PKID = (int)GridView1.DataKeys[gRow.RowIndex]["ID"];
Session["PKID"] = PKID;
Response.Redirect("ShowOneHotel.aspx");
}
}
NOTE very close how I did not expose, or even show/display the database primary key in the above GV, but I am able to have row click, get row index, and from that I get the PK value (that is what DataKeyNames="ID" is for.
Now, it is a simple matter to pass the PK id from the current page to the next page - I used session. So now, in the next page you jump to, you can retrieve and display the information.
Note how I did not really even bother with the "selected" row setup of the GV - just drop in a plane jane button, write up a plane jane button click, and get the PK and row index as I did.

How do I make bit value from SQL Server Stored Procedure clickable to update?

I have a table for projects that has a bit value for whether the project is still active or not. I created a stored procedure that pulls the project name and the value for the project (0 for inactive, 1 for active). When I display it on my webpage, the bit values show up in checkboxes (which is good because I would like to be able to update those values with a button).
The problem is that I'm unable to click or unclick them.
How could I pull my data correctly so that the checkboxes for the project name is clickable to be updated?
Any help would be much appreciated.
public void loadProj()
{
SqlConnection con;
DataTable dt = new DataTable();
string CS = ConfigurationManager.ConnectionStrings["ProjDB"].ConnectionString;
using (con = new SqlConnection(CS))
{
con.Open();
SqlCommand cmd = new SqlCommand("GetProjActive", con);
SqlDataAdapter sa = new SqlDataAdapter(cmd);
callStoredProcedure(cmd, sa);
sa.Fill(dt);
GridView.DataSource = dt;
GridView.DataBind();
}
}
You need to set up a asp:TemplateField in your GridView columns, with a asp:CheckBox control inside this template.
Setting the CheckBox value: The Boolean database value should be bound into the CheckBox using the OnRowDataBound event.
Changing the database value: Register a OnCheckedChanged event on the template field's CheckBox control.
HTML markup:
<asp:GridView ID="gv"
runat="server"
DataSourceID="..."
OnRowDataBound="gv_DataBound">
<Columns>
<asp:TemplateField>
<ItemTemplate>
<asp:CheckBox ID="chk_selection" runat="server" OnCheckedChanged="chk_selection__CheckedChanged" />
</ItemTemplate>
</asp:TemplateField>
....
</Columns>
</asp:GridView>
Code behind:
protected void gv_DataBound(object sender, GridViewRowEventArgs e)
{
// Get this row's CheckBox control
CheckBox chkSelector = (CheckBox)e.Row.FindControl("chk_selection");
// Cast the database boolean value and set the checkbox's Checked property
chkSelector.Checked = Convert.ToBoolean(DataBinder.Eval(e.Row.DataItem, "bool_field"));
}
protected void chk_selection__CheckedChanged(object sender, EventArgs e)
{
// Update your database
}

Enable Sorting and Paging in Gridview Efficiently

By using sample code as a guide, I managed to slap together some code that will perform sorting and paging on a gridview. However, I'm relatively new to webpage programming so I used ViewState quite often (which from what I've gathered, is BAD). As a result, I was wondering if there's anyway to make my code more efficient? (Or different ways of accomplishing the same thing?)
Front end:
<asp:GridView ID="UserAccounts" runat="server" AllowSorting="true" AutoGenerateColumns="false" AllowPaging="true"
OnSorting="gridView_Sorting" OnPageIndexChanging="gridView_PageIndexChanging" PageSize = "20">
<Columns>
<asp:BoundField DataField="UserName" HeaderText="UserName" SortExpression="UserName" />
<asp:BoundField DataField="Email" HeaderText="Email" SortExpression="Email" />
<asp:BoundField DataField="Roles" HeaderText="Role" SortExpression="Roles" />
<asp:CheckBoxField DataField="IsLockedOut" HeaderText="Locked Out?" SortExpression="IsLockedOut" />
<asp:CheckBoxField DataField="IsOnline" HeaderText="Online?" SortExpression="IsOnline" />
<asp:BoundField DataField="LastLoginDate" HeaderText="Last Login Date" SortExpression="LastLoginDate" />
<asp:HyperLinkField Text="Manage" DataNavigateUrlFields="UserName" DataNavigateUrlFormatString="ManageDetails.aspx?user={0}" />
</Columns>
</asp:GridView>
Code Behind to generate table:
private void BindUserAccounts()
{
DataTable dt = new DataTable();
dt.Columns.Add("UserName");
dt.Columns.Add("Email");
dt.Columns.Add("Roles");
dt.Columns.Add("IsLockedOut");
dt.Columns.Add("IsOnline");
dt.Columns.Add("LastLoginDate");
var userRoles = from MembershipUser user in Membership.FindUsersByName(this.UsernameToMatch + "%")
let roles = Roles.GetRolesForUser(user.UserName)
select new
{
UserName = user.UserName,
Email = user.Email,
Roles = string.Join(", ", roles),
IsLockedOut = user.IsLockedOut,
IsOnline = user.IsOnline,
LastLoginDate = user.LastLoginDate
};
foreach (var u in userRoles)
{
DataRow dr = dt.NewRow();
dr["UserName"] = u.UserName;
dr["Email"] = u.Email;
dr["Roles"] = u.Roles;
dr["IsLockedOut"] = u.IsLockedOut;
dr["IsOnline"] = u.IsOnline;
dr["LastLoginDate"] = u.LastLoginDate;
dt.Rows.Add(dr);
}
UserAccounts.DataSource = dt;
UserAccounts.DataBind();
ViewState["DataSource"] = dt;
}
To enable sorting and paging:
protected void gridView_PageIndexChanging(object sender, GridViewPageEventArgs e)
{
UserAccounts.PageIndex = e.NewPageIndex;
BindUserAccounts();
}
protected void gridView_Sorting(object sender, GridViewSortEventArgs e)
{
DataTable dataTable = (DataTable)ViewState["DataSource"];
if (dataTable != null)
{
DataView dataView = new DataView(dataTable);
if ((string)ViewState["SortDir"] == "ASC" || String.IsNullOrEmpty((string)ViewState["SortDir"]))
{
dataView.Sort = e.SortExpression + " ASC";
ViewState["SortDir"] = "DESC";
}
else if ((string)ViewState["SortDir"] == "DESC")
{
dataView.Sort = e.SortExpression + " DESC";
ViewState["SortDir"] = "ASC";
}
UserAccounts.DataSource = dataView;
UserAccounts.DataBind();
}
}
The code you posted is as efficient as it can be due to the way that you are binding the data to the GridView. More efficient ways can be achieved but not without changing the data binding logic.
For example, a more efficient way to do this could be to handle paging and sorting on the client side using jQuery+dataTables. Another way that would require less code from your part can be accomplished using SqlDataSource and setting it as the DataSource for the GridView - you wouldn't have to do the resorting/paging in code. But again, both approaches require significant changes. Your code is as efficient as it can be IMO.
Update:
Tim made a good point on his comment - don't persist the data table on ViewState. Either put it in Session (consider the size of the data before starting to put things in Session) or simply ask the DB to send the data again. Adding this DataTable to ViewState will considerably increase the page size.
good tips given you provided to see what to use what is not. Thanks for sharing your thoughts. I also personally believe that session is more light and secure than viewstate. i found one more simple example at this site http://blogfornet.com/2013/09/gridview-paging-and-sorting-example/. It will help beginner to understand.

Categories

Resources