So I have a .aspx page that is bound to some data. I can run a LINQ query and return it as an IQueryable object in the SelectMethod for the GridView control, and it will populate fine. What I am trying to do is populate the GridView based upon a selection from a ListBox control. Since the data is bound, I can't set the DataSource equal to the query. I could maybe figure out a way to re-use the SelectMethod based on a public string (which would in turn be based off the ListBox selection), but that would be pretty ugly.
Here is the gridview control on my .aspx page:
<asp:GridView runat="server" ID="employeesGrid"
ItemType="Foo.Models.Employee" DataKeyNames="EmployeeID"
SelectMethod="employeeGrid_GetData"
AutoGenerateColumns="false">
<Columns>
<asp:DynamicField DataField="EmployeeID" />
<asp:DynamicField DataField="FirstName" />
<asp:DynamicField DataField="LastName" />
<asp:DynamicField DataField="CompanyName" />
</Columns>
</asp:GridView>
And here is my SelectMethod in the .cs:
public IQueryable<Employee> employeeGrid_GetData()
{
FooContext db = new FooContext();
var query = db.Employees.Where(e => e.CompanyName == "NameOfCompany");
return query;
}
That populates the GridView just fine on load, but it doesn't do much for me with updating on click.
And here is the button click method that I've tried but doesn't work. I get an error about not being able to use DataSource or DataSourceId when the GridView is bound to a data model:
public void btn_RequestEmployees_Click(object sender, EventArgs e)
{
string currentlySelectedEmployer = lstbxEmployers.SelectedValue.ToString();
FooContext db = new FooContext();
var query = db.Employees.Where(employee => employee.CompanyName == currentlySelectedEmployer);
employeesGrid.DataSource = query;
DataBind();
}
Any advice? Thanks.
Try it like this:
public IQueryable<Employee> employeeGrid_GetData()
{
string currentlySelectedEmployer = lstbxEmployers.SelectedValue.ToString();
FooContext db = new FooContext();
var query = db.Employees.Where(employee => employee.CompanyName == currentlySelectedEmployer);
return query;
}
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 still a novice with asp.net, so I'm sure there are better ways to do some of this...
I want to allow a user to enter a date, and then execute an SQL query against a database to return all records matching that date. I want to display those records, and allow the user to select one for additional processing. It seemed like the DataGrid with a column of Pushbuttons was a good choice. In fact, I've done this before, but it those cases there was no user interaction involved. The page just ran a fixed SQL query. With what I'm doing now, the data is displayed as I want, but the Pushbuttons aren't working...the ItemCommand event doesn't fire.
I've read a lot of threads about the ItemCommand event not firing, but I still can't get this to work. My understanding is that I need to bind the DataGrid while not in a Postback, and I think my code does that. When I debug the Page_Load event, I can see that the code inside if (!IsPostBack){} is running, and the session variables have the expected data.
On the page that hosts the DataGrid, I have a 'Find' button that executes a SQL query against a database. The query uses a date entered into the textbox that is on that page. In the 'Find' button click event, I store the results of the query (a DataTable) in a session variable, then do a Redirect to re-load the page.
Once the page reloads, the session variables contain my expected data and the data is displayed in the DataGrid, along with the Pushbuttons. When I click any of the buttons in the DataGrid, the contents of the DataGrid disappear and the ItemCommand event does not fire.
Here's the definition of the DataGrid (updated to include the button):
<asp:Panel runat="server" ID="pnlSelect" HorizontalAlign="Left" Visible="true" style="margin-top:10px" >
Please select a participant.
<asp:DataGrid runat="server" ID="grdItems" AutoGenerateColumns="true" BorderColor="black" BorderWidth="1" CellPadding="3" style="margin-left:2px; margin-top:6px" OnItemCommand="grdSelect_ItemCommand" >
<Columns>
<asp:ButtonColumn HeaderStyle-HorizontalAlign="Center" ButtonType="PushButton" Text="Select" HeaderText="Select" CommandName="Select" >
<ItemStyle Width="60px" HorizontalAlign="Center"/>
</asp:ButtonColumn>
</Columns>
</asp:DataGrid>
</asp:Panel>
Here's the Page Load code (unneeded code-behind stuff is commented out):
protected void Page_Load(object sender, EventArgs e)
{
if (!IsPostBack)
{
if (Session["y"] != null)
{
txtFind.Text = Session["x"].ToString();
DataTable table = Session["y"] as DataTable;
Session.Remove("x");
Session.Remove("y");
if (table != null)
{
/*
ButtonColumn buttonColumn = new ButtonColumn
{
HeaderText = "Select",
Text = "Select",
CommandName = "Select",
ButtonType = ButtonColumnType.PushButton,
};
grdItems.Columns.Add(buttonColumn);
foreach (DataColumn c in table.Columns)
{
BoundColumn column = new BoundColumn
{
HeaderText = c.Caption,
DataField = c.Caption,
ReadOnly = true
};
grdItems.Columns.Add(column);
}
*/
grdItems.DataSource = table;
grdItems.DataBind();
}
}
}
}
Here's the relevant 'Find' button event code. I can't post the details of the sql query text, but that part does work and the DataTable does contain the expected data:
DataTable table = DatabaseHelper.FindBySQL(sqlText);
if (table != null && table.Rows.Count > 0)
{
Session["x"] = searchtext;
Session["y"] = table;
Response.Redirect(Request.RawUrl, true);
}
And this the ItemCommand event. I place a breakpoint on the first line, and the debugger never hits it.
protected void grdItems_ItemCommand(object source, DataGridCommandEventArgs e)
{
string item = "";
switch (e.CommandName.ToLower())
{
case "select":
item = e.Item.Cells[1].Text.Trim();
//todo: handle the selected data
break;
}
}
Thanks for any thoughts.
Don
In this case, there is absolutely no problem with the actual filtering, however, a cosmetic bug exists in which the expression is not removed from the textbox.
There is a within the RadGrid. Both Master and Detail tables have filtering enabled and it works perfectly fine. When I try to remove the filterexpression at the DetailTable level (using the 'nofilter') menu option, the filter expression still persists in the textbox. How can I remove this? What code can I write to identity if the filtering request happened from mastertable or detailtable? In the end, I just want the text from the filter textbox to disappear.
Sample code:
<telerik:RadGrid ID="SomeGridView" runat="server">
<ClientSettings>
<ClientEvents OnFilterMenuShowing="FilterMenuShowing" />
</ClientSettings>
<Columns>
</Columns>
<DetailTables>
<telerik:GridTableView Name="Details">
<Columns>
<telerik:GridBoundColumn DataField="FieldName" HeaderText="Some Header" SortExpression="FieldName" UniqueName="FieldName" />
</Columns>
</telerik:GridTableView>
</DetailTables>
</telerik:RadGrid>
I think I resolved my own question.
But I find this extra effort was not needed for the MasterTableView filters, since it automatically cleared the textbox values when I selected "NoFilter".
Is this a known Telerik bug?
You can follow the ticket here:
http://www.telerik.com/forums/clear-filter-expresssion-text-box-in-radgrid#me_BRg3iXUOgDucx47Wigg
protected void RadGridView_DetailTableDataBind(object sender, GridDetailTableDataBindEventArgs e)
{
GridDataItem dataItem = e.DetailTableView.ParentItem;
if (IsPostBack == true)
{
foreach (GridColumn thisColumn in e.DetailTableView.Columns)
{
if (thisColumn.CurrentFilterFunction == GridKnownFunction.NoFilter)
{
thisColumn.CurrentFilterValue = string.Empty;
}
}
}
}
I have a dynamically created Checkboxlist ( I am using Dynamic Data and I am talking about the Many-to-many_edit.aspx ).
I have like 20 000+ item added in my Checkboxlist and I need pagination for this.
I thought binding this checkboxlist to a gridview would do the trick? How can I do this ?
EDIT: I've read a lot about this subject but I couldn't find anything good . When I try to put Checkboxlist's source in my GridView.DatSource it throws OutOfMemory exception
EDIT2: I am trying something like this, but now, because my checkboxlist it's inside the gridview I don't know how to populate it anymore
code sample:
asp:GridView ID="GridView1" runat="server">
<Columns>
<asp:TemplateField>
<ItemTemplate>
<asp:CheckBoxList ID="CheckBoxList1" runat="server" RepeatColumns="3" OnDataBound="CheckBoxList1_DataBound" />
</ItemTemplate>
</asp:TemplateField>
</Columns>
</asp:GridView>
EDIT3:
My code aspx.cs
public partial class ManyToMany_EditField : System.Web.DynamicData.FieldTemplateUserControl
{
public void Page_Load(object sender, EventArgs e)
{
// Register for the DataSource's updating event
EntityDataSource ds = (EntityDataSource)this.FindDataSourceControl();
// This field template is used both for Editing and Inserting
ds.Updating += new EventHandler<EntityDataSourceChangingEventArgs>(DataSource_UpdatingOrInserting);
ds.Inserting += new EventHandler<EntityDataSourceChangingEventArgs>(DataSource_UpdatingOrInserting);
}
private void DataSource_UpdatingOrInserting(object sender, EntityDataSourceChangingEventArgs e)
{
MetaTable childTable = ChildrenColumn.ChildTable;
// Comments assume employee/territory for illustration, but the code is generic
// Get the collection of territories for this employee
RelatedEnd entityCollection = (RelatedEnd)Column.EntityTypeProperty.GetValue(e.Entity, null);
// In Edit mode, make sure it's loaded (doesn't make sense in Insert mode)
if (Mode == DataBoundControlMode.Edit && !entityCollection.IsLoaded)
{
entityCollection.Load();
}
// Get an IList from it (i.e. the list of territories for the current employee)
// REVIEW: we should be using EntityCollection directly, but EF doesn't have a
// non generic type for it. They will add this in vnext
IList entityList = ((IListSource)entityCollection).GetList();
// Go through all the territories (not just those for this employee)
foreach (object childEntity in childTable.GetQuery(e.Context))
{
// Check if the employee currently has this territory
bool isCurrentlyInList = entityList.Contains(childEntity);
// Find the checkbox for this territory, which gives us the new state
string pkString = childTable.GetPrimaryKeyString(childEntity);
ListItem listItem = CheckBoxList1.Items.FindByValue(pkString);
if (listItem == null)
continue;
// If the states differs, make the appropriate add/remove change
if (listItem.Selected)
{
if (!isCurrentlyInList)
entityList.Add(childEntity);
}
else
{
if (isCurrentlyInList)
entityList.Remove(childEntity);
}
}
}
protected void CheckBoxList1_DataBound(object sender, EventArgs e)
{
MetaTable childTable = ChildrenColumn.ChildTable;
var a = childTable.CreateContext();
var dataSource = childTable.GetQuery().AsQueryable();
var Context = new testEntities();
var daZtaSource = Context.GetType();
GridView1.DataSource = dataSource;
// Comments assume employee/territory for illustration, but the code is generic
IList entityList = null;
ObjectContext objectContext = null;
if (Mode == DataBoundControlMode.Edit)
{
object entity;
ICustomTypeDescriptor rowDescriptor = Row as ICustomTypeDescriptor;
if (rowDescriptor != null)
{
// Get the real entity from the wrapper
entity = rowDescriptor.GetPropertyOwner(null);
}
else
{
entity = Row;
}
// Get the collection of territories for this employee and make sure it's loaded
RelatedEnd entityCollection = Column.EntityTypeProperty.GetValue(entity, null) as RelatedEnd;
if (entityCollection == null)
{
throw new InvalidOperationException(String.Format("The ManyToMany template does not support the collection type of the '{0}' column on the '{1}' table.", Column.Name, Table.Name));
}
if (!entityCollection.IsLoaded)
{
entityCollection.Load();
}
// Get an IList from it (i.e. the list of territories for the current employee)
// REVIEW: we should be using EntityCollection directly, but EF doesn't have a
// non generic type for it. They will add this in vnext
entityList =((IListSource)entityCollection).GetList();
// Get the current ObjectContext
// REVIEW: this is quite a dirty way of doing this. Look for better alternative
ObjectQuery objectQuery = (ObjectQuery)entityCollection.GetType().GetMethod(
"CreateSourceQuery").Invoke(entityCollection, null);
objectContext = objectQuery.Context;
}
// Go through all the territories (not just those for this employee)
foreach (object childEntity in childTable.GetQuery(objectContext))
{
MetaTable actualTable = MetaTable.GetTable(childEntity.GetType());
// Create a checkbox for it
ListItem listItem = new ListItem(
actualTable.GetDisplayString(childEntity),
actualTable.GetPrimaryKeyString(childEntity));
// Make it selected if the current employee has that territory
if (Mode == DataBoundControlMode.Edit)
{
listItem.Selected = entityList.Contains(childEntity);
}
CheckBoxList1.Items.Add(listItem);
}
}
public override Control DataControl
{
get
{
return CheckBoxList1;
}
}
}
My code: Aspx
<%# Control Language="C#" CodeFile="ManyToMany_Edit.ascx.cs" Inherits="ManyToMany_EditField" %>
<asp:CheckBoxList ID="CheckBoxList1" runat="server" RepeatColumns="3" OnDataBound="CheckBoxList1_DataBound" />
Why do you want a checkbox list inside a gridview ? I think you may replace your checkboxlist by a checkbox and add a column with the data you want to display.
<asp:GridView ID="GridView1" runat="server" AutoGenerateColumns="false" showheader="false" allowpaging="true">
<Columns>
<asp:TemplateField>
<ItemTemplate>
<asp:CheckBox ID="CheckBoxList1" runat="server" checked='<%# Eval("myColumn")%>'/>
</ItemTemplate>
</asp:TemplateField>
<asp:BoundField DataField="cbxLabel" />
</Columns>
</asp:GridView>
You will have the pagination that you are wanting.
And also don't forget during the page changing to save the checked checkboxes and to restore it also.
To Paginate your result please take a look at this thread:
How to paginate a gridview and SQL custom query with ROW_NUMBER