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();
Related
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:
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.
I am currently rewriting an old VB.net script of mine in C#. I have a DataGrid and a Delete button in each row. I want to pass a given Id (KeyphraseToBrandId) to the OnDeleteCommand, to delete that row in the database. I am having trouble getting this row identified.
Here is my MyDataGrid_DeleteCommand, which shows all the things I have tried which have not worked, as comments:
protected void MyDataGrid_DeleteCommand(object source, DataGridCommandEventArgs e)
{
String connStr = ConfigurationSettings.AppSettings["ConnectionString"];
// Define db connection
MySqlConnection conn = new MySqlConnection(connStr);
String DeleteSql = "DELETE from KeyphrasesToBrands WHERE KeyphraseToBrandId=?KeyphraseToBrandId";
MySqlCommand DeleteCommand = new MySqlCommand(DeleteSql, conn);
// DeleteCommand.Parameters.Add("?KeyphraseToBrandId", MyDataGrid.DataKeys(CInt(E.Item.ItemIndex)));
// This works for Id 8, so it's not the rest of the code
// DeleteCommand.Parameters.Add("?KeyphraseToBrandId", "8");
// DeleteCommand.Parameters.Add("?KeyphraseToBrandId", MyDataGrid.SelectedDataKey.Value.ToString());
// DeleteCommand.Parameters.Add("?KeyphraseToBrandId", MyDataGrid.DataKeys.Equals(e.Item.ItemIndex));
// DeleteCommand.Parameters.Add("?KeyphraseToBrandId", e.Item.ItemIndex.ToString());
// DeleteCommand.Parameters.Add("?KeyphraseToBrandId", e.Item.ItemIndex);
// DeleteCommand.Parameters.Add("?KeyphraseToBrandId", e.CommandArgument.ToString());
// DeleteCommand.Parameters.Add("?KeyphraseToBrandId", e.Item.Cells[0].ToString());
DeleteCommand.Parameters.Add("?KeyphraseToBrandId", e.CommandArgument);
DeleteCommand.Connection.Open();
DeleteCommand.ExecuteNonQuery();
BindGrid();
DeleteCommand.Connection.Close();
}
... and here's my DataGrid:
<asp:DataGrid ID="MyDataGrid" runat="server" DataKeyField="KeyphraseToBrandId" OnCancelCommand="MyDataGrid_CancelCommand" OnDeleteCommand="MyDataGrid_DeleteCommand" OnSelectedIndexChanged="MyDataGrid_SelectedIndexChanged" AutoGenerateColumns="false">
<Columns>
<asp:BoundColumn DataField="KeyphraseText" HeaderText="Keyphrase Text"></asp:BoundColumn>
<asp:BoundColumn DataField="BrandName" HeaderText="Brand Name"></asp:BoundColumn>
<asp:BoundColumn DataField="KeyphraseToBrandId" HeaderText="Keyphrase To Brand Id"></asp:BoundColumn>
<asp:ButtonColumn ButtonType="PushButton" Text="Delete" CommandName="Delete" HeaderText="Delete?" />
</Columns>
</asp:DataGrid>
How do I pass KeyphraseBrandId to the delete command, to identify this row for deletion in the database?
Thanks
If you want to delete bulk Selected Rows then you can do it
foreach (DataGridViewRow row in yourDataGridView.SelectedRows)
{
yourDataGridView.Rows.RemoveAt(row.Index);
}
This is work as find the index(The identity of Listed Rows in DataGrid). It remove Rows temporary, If you want to remove the data from data base itself, you need to get identity id from Row and delete it or change active status as per your requirement.
I have a infragistics grid that gets data when a dropdown is changed
1- i want to hide the last column every time the grid is loaded with data
2- i also want to update header according to data
problem
I am trying
protected void DropDownList1_SelectedIndexChanged(object sender, EventArgs e)
{
EntityName.Text = DropDownList1.SelectedItem.Text;
string entity = "t_" + DropDownList1.SelectedItem.Text;
String strConnString = ConfigurationManager.ConnectionStrings["LiveLeaseConnectionString"].ConnectionString;
SqlConnection con = new SqlConnection(strConnString);
SqlCommand cmd = new SqlCommand("p_DataList_ByRegardingObject", con);
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.AddWithValue("#RegardingObjectName", entity);
SqlDataAdapter sqlDataAdapter = new SqlDataAdapter(cmd);
DataTable dt = new DataTable();
sqlDataAdapter.Fill(dt);
this.EntityGrid.DataSource = dt;
this.EntityGrid.Columns[6].Hidden = true;
its throwing null reference error and it doesnt seem to update column names each time it loads data.data is getting refreshed but not the column names.
here is my aspx
<asp:DropDownList ID="DropDownList1" AutoPostBack="true" runat="server" OnSelectedIndexChanged="DropDownList1_SelectedIndexChanged">
<asp:ListItem>Select Entity</asp:ListItem>
</asp:DropDownList>
<asp:Label runat="server" ID="EntityName"></asp:Label>
<ig:WebScriptManager ID="WebScriptManager1" runat="server"></ig:WebScriptManager>
<ig:WebDataGrid ID="EntityGrid" runat="server" Width="100%" Height="50%" StyleSetName="Claymation" >
<Behaviors>
<ig:Sorting>
</ig:Sorting>
</Behaviors>
<ClientEvents Click="NavigateOnClick" />
</ig:WebDataGrid>
If your AutogenerateColumns is True - grid column collection will be empty. The trick is to get to the column via Grid Rows.
After grid is bound try this:
this.EntityGrid.Rows[0].Items[6].Column.Hidden = true;
Just make sure that grid has at least one row.
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.