Programmatic Gridview Understanding - c#

This may be a fairly simple question, but I can't seem to find the answer I'm looking for.
I've been working with a GridView control programmatically, loading it from a dataset in the code behind. So it's a fairly simple process, instantiate a new Gridview, load the Dataset, bind the data, and then load the control.
However, I'm running into an issue where I can't load directly into the GridView from the code. In the .aspx file I have a simple:
<asp:GridView ID="Supp_Data" runat="server" />
In the code behind my creation of the GridView is as follows:
Supp_Data = new GridView();
Supp_Data = OutsideClass.GetData(str_sql);
//Add other features here (such as AllowSorting, GridLines, PageSize, etc.)
Supp_Data.DataBind();
And in my outside class:
public static GridView GetData(string str_sql)
{
// string str_sql is simply the sql query that we will get the dataset with.
OdbcConnection dbc_conn = ODBC_Conn(""); //This simply instantiates the connection
OdbcCommand dbc_cmd = null;
OdbcDataAdapter dbc_adpt = null;
DataSet dta_ds = new DataSet();
GridView ret_val = new GridView();
try
{
if (dbc_conn.State != System.Data.ConnectionState.Open) { dbc_conn.Open(); }
dbc_cmd = new OdbcCommand(str_sql, dbc_conn);
dbc_adpt = new OdbcDataAdapter(dbc_cmd);
dbc_adpt.Fill(dta_ds);
ret_val.DataSource = dta_ds;
ret_val.DataBind();
dbc_conn.Close();
}
catch (Exception e)
{
string tst_msg = e.Message;
}
return ret_val;
}
But this doesn't display the grid at all.
Now, as a workaround (which displays the data), adding a PlaceHolder in the .aspx file and using PlaceHolder.Controls.Add(Supp_Data) displays the data just fine.
So my actual question is, why doesn't the data get displayed in the GridView, but has no problem displaying in the PlaceHolder? Does it have something to do with the Page Life Cycle that I'm overlooking (I tried with both Page_Load and Page_Init with the same results)? This would definitely help my overall understanding of this process. I just don't understand why it would work near perfectly with one but not with the other.
Thanks for any information.

You shouldn't do a 'new' on the GridView, just populate it's DataSource and call DataBind. The object that the page was referencing is being overwritten in your function call so it has nothing to display.

Related

WebHierarchicalDatagrid Shows No Data on Initial PageLoad

I have a WebHierarchicalDatagrid where I manually create the columns in my PageLoad() event. None of my columns shows on initial page load, even though the datasource has data that matches the columns. (I DO set and bind after the columns are created/added to the grid). If I refresh (PostBack), then they show. I have cleared and reset the grid many ways.
If I define columns in markup then the initial page load works of course, but I need to dynamically create columns based on my user roles and if I clear and recreate the desired columns in my page load, page prerender, etc I get a viewstate error.
Seems that you cannot use markup and codebehind to define a grid. Would not be a problem, but I have a custom (user control) pager template defined in markup and I spent days trying to get create that in codebehind given I cannot just point the codebehind pager creation to use an existing user control. Total catch-22.
Infragistics grids are just too tweaky to deal with anymore. If you stay on the straight and narrow, they are good, but stray off the path and you are in big trouble!
AutoGenerateBands and AutoGenerateColumns should be set to false. Also, I don't know whether you are using GridView to configure the grid or not, although I wanted to let you know that WebHierarchicalDataGrid.Columns collections is relevant to the root band of the columns defined at designed time or from the markup. As for the columns that are auto generated, they could be accessed from WebHierarchicalDataGrid.GridView.Columns.
As I understand you are creating the columns from Page_Load event, try to do that on WHDG_Init. I am just curious what would be the result.
protected void WebHierarchicalDataGrid1_Init(object sender, EventArgs e)
{
WebHierarchicalDataGrid1.DataSource = new TestData().GetData();
WebHierarchicalDataGrid1.DataKeyFields = "ID";
WebHierarchicalDataGrid1.Columns.Add(CreateNewBoundDataField("ID", "ID"));
WebHierarchicalDataGrid1.Columns.Add(CreateNewBoundDataField("Name", "Name"));
WebHierarchicalDataGrid1.Bands.Add(CreateNewBand("ChildBand_0", "Child", "ChildID"));
WebHierarchicalDataGrid1.Bands["ChildBand_0"].Columns.Add(CreateNewBoundDataField("ChildID", "ChildID"));
WebHierarchicalDataGrid1.Bands["ChildBand_0"].Columns.Add(CreateNewBoundDataField("ID", "ID"));
WebHierarchicalDataGrid1.Bands["ChildBand_0"].Columns.Add(CreateNewBoundDataField("Address", "Address"));
WebHierarchicalDataGrid1.Bands["ChildBand_0"].Behaviors.CreateBehavior<Filtering>();
}
public static BoundDataField CreateNewBoundDataField(string columnName, string headerText)
{
BoundDataField boundDataField = new BoundDataField();
boundDataField.DataFieldName = columnName;
boundDataField.Key = columnName;
boundDataField.Header.Text = headerText;
return boundDataField;
}
public static Band CreateNewBand(string key, string dataMember, string dataKeyField)
{
Band band = new Band();
band.AutoGenerateColumns = false;
band.Key = key;
band.DataMember = dataMember;
band.DataKeyFields = dataKeyField;
return band;
}

How to Display Selected row from Datagrid view into text boxes using C#

I'm storing all the user registration on user_reg database table and importing them to data grid view and I use a separate windows form to input the data. When the data is imported from database to data grid view my name column is shown as one(first name, middle name and last name are joined.) My problem is that, when I try to import the data from data grid view to my windows form the Name coloumn is not showing properly on the Windows Form(it displays as one, but my from has 3 separate text boxes.) I have done this =>
private void Update_Users_Load(object sender, EventArgs e)
{
long totalRow = 0;
MySqlDataAdapter sda = new MySqlDataAdapter("SELECT `user_reg`.`user_id`, `user_reg`.`first_name`, `user_reg`.`middle_name`, `user_reg`.`last_name`, `user_reg`.`address`, `user_reg`.`sex`, `user_reg`.`nic_number`, `user_reg`.`contact_number`, `user_reg`.`email_address`, `user_reg`.`user_type` FROM `sarasavi_library`.`user_reg` WHERE `user_id`='" + borrower_id + "';", conn);
DataSet ds = new DataSet();
sda.Fill(ds, "user_reg");
totalRow = ds.Tables["user_reg"].Rows.Count - 1;
txt_uid.Text = ds.Tables["user_reg"].Rows[0].ItemArray.GetValue(0).ToString();
txt_fname.Text = ds.Tables["user_reg"].Rows[0].ItemArray.GetValue(1).ToString();
txt_mname.Text = ds.Tables["user_reg"].Rows[0].ItemArray.GetValue(2).ToString();
txt_lname.Text = ds.Tables["user_reg"].Rows[0].ItemArray.GetValue(3).ToString();
txt_address.Text = ds.Tables["user_reg"].Rows[0].ItemArray.GetValue(4).ToString();
com_sex.Text = ds.Tables["user_reg"].Rows[0].ItemArray.GetValue(5).ToString();
txt_nic.Text = ds.Tables["user_reg"].Rows[0].ItemArray.GetValue(6).ToString();
txt_contact.Text = ds.Tables["user_reg"].Rows[0].ItemArray.GetValue(7).ToString();
txt_email.Text = ds.Tables["user_reg"].Rows[0].ItemArray.GetValue(8).ToString();
com_memtype.Text = ds.Tables["user_reg"].Rows[0].ItemArray.GetValue(9).ToString();
}
But my problem is that it doesn't run but give me the error
The name borrower_id does not exist in the current context.
borrower_id is the name from the data grid view, I can't figure out a way to include it. Any ideas on how to include this?? For better understanding I will attach an image screenshot
The name borrower_id does not exist in the current context.
This exception indicates that the compiler does not know what "borrower_id" is. That is because (based on the code snippet) you never declared it anywhere before using it. The only instance I see is when you used it to construct your query.
Unless "borrower_id" is a property (which is why you omitted its declaration from your snippet), you need to declare it before using it in order for the compiler to know what it is.

Can we use same datatable for pagemethod and webmethod in ASP.NET?

I am trying to create a new webpage where i need to display almost 10 different gridviews and charts.
Gridviews are binded on pageload event and charts are displayed using jquery-ajax method (using amcharts as well as highcharts) by calling WebMethod.
Initially i implemented the page in a way that after executing same set of stored procedures for gridviews(for showing grid view data) and webmethods(for drawing charts).So same sps are executed twice for this page(one for grid and another for chart).There are 10 sps required to execute for fetching the data.
So for improving the page performance i have created static datatable like this
static DataTable Report1;
and binded the gridview like this.
private void gvbindReport1()
{
try
{
Report1 = new DataTable();//refreshed datatable
DataSet ReportDS1 = objmvbl.GetReportGraph(ClientID, date_From, date_To);
if (ReportDS1.Tables.Count > 0)
{
Report1 = ReportDS1.Tables[0];//bindinding data to static datatable
}
GdReport.DataSource = Report1;
GdReport.DataBind();
}
catch (Exception ex)
{
Log.Errlog("Error Occured in gvbindReport1 : " + ex.Message.ToString());
}
}
and inside the webmethod i have used the same datatable for drawing the chart
like this
[System.Web.Services.WebMethod]
public static string GetDataReport1()
{
System.Web.Script.Serialization.JavaScriptSerializer serializer = new System.Web.Script.Serialization.JavaScriptSerializer();
List<Dictionary<string, object>> rows = new List<Dictionary<string, object>>();
Dictionary<string, object> row;
try
{
//processing for the data inside static datatable
if (Report1.Rows.Count > 0)
{
foreach (DataRow dr in Report1.Rows)
{
row = new Dictionary<string, object>();
foreach (DataColumn col in Report1.Columns)
{
row.Add(col.ColumnName, dr[col]);
}
rows.Add(row);
}
}
}
catch (Exception ex)
{
Log.Errlog("Error Occured in GetDataReport WebMethod of Report Page : " + ex.Message.ToString());
}
return serializer.Serialize(rows);
}
with this i am able to show both grid and charts.
Now Please tell me that, is this a correct approach to deal with webmethods? i have read that webmethod have no relation to the page and all.Please Tell me the drawbacks of this method.
If this is wrong,Please suggest a better way to improve the page performance?
No, this is not the correct method. Since you have declared the DataTable as static (a static variable has application scope and cannot be instantiated) all
users will get the same result (last updated values).
You can realize this in concurrency testing.
Please check the following scenario:
Consider dtbl is the static dataTable which is initialized on the home page, and you create another instance of `datatable on the index page (both are in page load as given below).
Home
public static DataTable dtbl;
protected void Page_Load(object sender, EventArgs e)
{
if (!Page.IsPostBack)
{
dtbl = new DataTable();
dtbl.Columns.Add("id");
dtbl.Columns.Add("name");
for (int i = 0; i < 10; i++)
{
DataRow dr = dtbl.NewRow();
dr["id"] = i.ToString();
dr["name"] = i + 1;
dtbl.Rows.Add(dr);
}
}
}
Index page
protected void Page_Load(object sender, EventArgs e)
{
if (!Page.IsPostBack)
{
home.dtbl = new DataTable();
}
}
Now put a breakpoint in each page load and run the application,
Open both the pages in separate tab.
Refresh the home page and check whether the columns are showing
Now go to the next tab (index) and refresh it (a new instance is created for dt). It will affect the data table now you will get the new data table at home also.
So if these two processes/pages are concurrently executed the latest value will get for both the pages. That's why I am saying it will realize this in concurrency testing.
You can make use of a session in this case. Consider the following code:
Home
protected void Page_Load(object sender, EventArgs e)
{
if (!Page.IsPostBack)
{
dtbl = new DataTable();
dtbl.Columns.Add("id");
dtbl.Columns.Add("name");
for (int i = 0; i < 10; i++)
{
DataRow dr = dtbl.NewRow();
dr["id"] = i.ToString();
dr["name"] = i + 1;
dtbl.Rows.Add(dr);
}
if (((DataTable)Session["MyDatatable"]).Columns.Count < 0)
{
Session["MyDatatable"] = dtbl;
}
else
{
dtbl = (DataTable)Session["MyDatatable"];
}
}
}
First off, do not use, as a general rule of thumb, static variables in an web application. These act as global variables and are not instantiated with each request.
I wouldn't also suggest you using DataTables all the way up to your UI layer. Instead, work with strongly-typed objects.
Make a Model of the object you are trying to bind.
Like for example if you have a table called person that has the following fields.
Id | first_name | last_name | audit_ts
You can create an object as such:
public class Person
{
public int Id {get;set;}
public string FirstName {get;set;}
public string LastName {get;set;}
}
Now in a separate functions, in some class you can call your stored procedure from the database and then cast your table rows in the person table into the list of Person Object.
Now, instead of calling your stored procedure twice to get the same data, which only reduces your application's performance, what you can do is to instead of binding your grid view in your code behind at Page_Load event. Simply bind the HTML table after you make the call to your webmethod which I believe is in your code-behind. You can refer to this post regarding how to bind your HTML table with JSON object returned by your Ajax call.
This way, you are making one call to the server and to the database to use the same data to bind your table as well as your charts.
This is a good use case for the little used Cache Object Many users understand ViewState and SessionState, however the Cache object is not as widely utilized, and although the concept is very similar, it is much more flexible.
If your page is calling 10 stored procedures twice (once for your grids and a second time for your charts) then lets improve the performance by roughly 100% by eliminating the extra calls with the Cache Object
Have one call to the stored procedures in a separate method that populate your data tables cache object, which is then reused throughout your application.
private void loadReport1IntoCache()
{
//...load your data from DB into the Report1 variable here
//this line is new, and it saves your data into a global Cache variable
//with an absolute expiration of 10 minutes
Cache.Insert("Report1", Report1, null,
DateTime.Now.AddMinutes(10d),
System.Web.Caching.Cache.NoSlidingExpiration);
}
Then, when you are inside your other methods, you can use the Cache variable instead of calling stored procedures again. For example:
[System.Web.Services.WebMethod]
public static string GetDataReport1()
{
//first load the application variable before performing your other work
DataTable myCachedReport1Data = (DataTable)Cache["Report1"];
//did the Cache expire?
if (myCachedReport1Data == null)
{
//if so refresh it
loadReport1IntoCache();
//and then assign the variable the contents of the refresh and proceed
myCachedReport1Data = (DataTable)Cache["Report1"];
}
//other work here, utilizing the myCachedReport1Data variable
}
and for your grid binding:
private void gvbindReport1()
{
try
{
DataTable myCachedReport1Data = (DataTable)Cache["Report1"];
//did the Cache expire?
if (myCachedReport1Data == null)
{
//if so refresh it
loadReport1IntoCache();
//and then assign the variable the contents of the refresh
myCachedReport1Data = (DataTable)Cache["Report1"];
}
GdReport.DataSource = myCachedReport1Data ;
GdReport.DataBind();
}
catch (Exception ex)
{
Log.Errlog("Error Occured in gvbindReport1 : " + ex.Message.ToString());
}
}
Now, you will have to do a few things not mentioned here. You should consider when you want your Cache data to expire (the example given is 10 minutes). Also you should consider if you want it to be an Absolute Number of minutes (Absolute Expiry) or a number of minutes since last access (Sliding Expiry). In your case, probably absolute expiry, but only you know that. Then you will set the expiration when you are setting the variable contents.
See the Cache documentation here:
https://msdn.microsoft.com/en-us/library/6hbbsfk6.aspx
Adding Cache data:
https://msdn.microsoft.com/en-us/library/18c1wd61.aspx
Retrieving Cache data:
https://msdn.microsoft.com/en-us/library/xhy3h9f9.aspx
Looking at the code sample that you have given (and the parameters date_from and date_to that you are passing to GetReportGraph()) I assume:
you have 2 input fields where user is specifying the date range and then submitting the data (causing postback), based on which you are filtering the records and showing in grid as well as chart.
as different users would be providing different date ranges, you don't want to show the same data to all users.
as the data is filtered, its not going to have thousands of records.
I'm not sure what functionality of grid view you are using. Is it used only to show read only tabular data? If yes, you can consider the approach given by #Nabin Karki Thapa. If not check the alternate approach below:
After you have got the data table and bound it to grid view, immediately serialize it to JSON and register it as a script block (define a JS variable and assign the serialized JSON as it's value).
On the client side, while charting, instead of invoking webmethod, to get the JSON object use the JS variable that you have registered. This way you will avoid the call to web method (AJAX) and extra stored procedure call altogether.

User control does not retain private values, returns values set in Page_Load

I have a control defined like:
public partial class MyControl: System.Web.UI.UserControl
{
static int ControlID;
static DataTable table;
protected void Page_Load(object sender, EventArgs e)
{
if (!Page.IsPostBack)
{
ControlID = 0;
table= new DataTable();
}
}
public void Save()
{
//ControlID is 0
//table is empty DataTable
}
public void SetControlID(int id)
{
ControlID = id; //This DOES set id correctly
SqlConnection conn = new SqlConnection(ConnectionString);
SqlCommand command = new SqlCommand(SelectCommand, conn);
command.Parameters.AddWithValue("#ControlID ", ControlID);
SqlDataAdapter adapter = new SqlDataAdapter(command);
adapter.Fill(table);
//table is correctly filled with the correct data
}
}
This control is added to my aspx page like:
<uc:MyControl ID="myCont" runat="server" />
In Page_Load is the call:
myCont.SetControlID(1); //This correctly calls the `SetControlID` method and
//seems to fill the `table` and set `ControlID` to 1.
However there is a "Save" button on the page that calls:
myCont.Save();
In this method, checking ControlID and table shows them as still being 0 and an empty DataTable respectively.
Why do ControlID and table not retain the values that were set in the SetControlID method?
EDIT: Removing the static property of the 2 variable makes no difference.
EDIT #2: I'd just like to point out I have used this method in numerous other pages and everything is working fine. What is so different with this one?
In ASP.NET, lifecycle of a Page class is for duration of processing request. That is when request comes in, the class for the page is instantiated, viewstate is deserialized to restore state (if it is Postback), an event is ran (like button click), response generated, and object is then destroyed. Thus any member that does not save its state into viewstate (such as int) will not be retained across activations.
Also, a suspect in your code are that ControlID and tabe are both declared static. That means those values will be shared amongst all callers of the page (and every time a fresh page is requested, table will be overwritten for all users). That doesn't seem right.
static variables are not page level (instance level).. EVERY time the page is loaded for any other user etc your static table will be reset to an empty table. Very bad bad bad idea.
In order to preserve the data, you have to use ViewState.
public partial class MyControl: System.Web.UI.UserControl
{
public int ControlID
{
get
{
if(ViewState["ControlID"]==null)
return 0;
return int.Parse(ViewState["ControlID"].ToString());
}
set
{
ViewState["ControlID"] = value;
}
}
....
}
I should write this as a comment to the answer that you have accepted. But, I don't have enough reputation points to comment :-)
I have accepted this as it is what worked. I don't understand how other pages, that use similar logic, work without using ViewState.
When I say other pages, I mean over numerous applications I have built
before, not just this application. -anothershrubery
I would also recommend to use viewstate in this case as static members are shared across all web request sessions, so, potentially you may see unwanted behavior.
As you were wondering why your code didn't work, I'll try to give you a reasoning:
Refer ASP.NET page life cycle here... From this you could observe that User Control's load event processed after Page's load event. So, program flow goes like this:
In Page's Page_Load, you are calling myCont.SetControlID(1) which sets ControlID & table
Then User Control's Page_Load happens, in which you are resetting/initializing ControlID & table.
At later point myCont.Save() gets called, in which you would see reset values for ControlID & table.
Hope this helps!

Dropdown Not Rendering After Databinding

I'm binding to a dropdown. It works on the initial load. On subsequent loads (postbacks) it doesn't refresh the items in the dropdown.
using (DataView dv = dtProductGroup.DefaultView)
{
dv.ApplyDefaultSort = false;
dv.Sort = "KVIGroupName ASC";
ddlGroup.ClearSelection();
ddlGroup.Items.Clear();
string strAll = Localization.GetResourceValue("_strddlStatusLBAll");
ddlGroup.DataValueField = "KVIGroupId";
ddlGroup.DataTextField = "KVIGroupName";
ddlGroup.DataSource = dv;
ddlGroup.DataBind();
ListItem item = new ListItem(strAll, "0");
ddlGroup.Items.Insert(0, item);
}
I've confirmed that on the postbacks the data is being bound to the dropdown and items are successfully added. But when the page renders the dropdown doesn't have any of the new values.
I see two possibilities: The control isn't rendering the new values or the values are being cleared. I'm at a loss of where to look for possible problems.
Edit
I discovered the problem. The dropdownlist was embedded in an Conditional UpdatePanel. Simply calling "UpdatePanel.Update();" solved the problem.
Upon Postback the viewstate is being reapplied + you said you're trying to load values again. I'd suggest letting viewstate carry all the weight on postback. Only load the values when the page is first hit by adding if (! IsPostBack) like so
using (DataView dv = dtProductGroup.DefaultView)
{
if (! IsPostBack) {
dv.ApplyDefaultSort = false;
dv.Sort = "KVIGroupName ASC";
ddlGroup.ClearSelection();
ddlGroup.Items.Clear();
string strAll = Localization.GetResourceValue("_strddlStatusLBAll");
ddlGroup.DataValueField = "KVIGroupId";
ddlGroup.DataTextField = "KVIGroupName";
ddlGroup.DataSource = dv;
ddlGroup.DataBind();
ListItem item = new ListItem(strAll, "0");
ddlGroup.Items.Insert(0, item);
}
}
Edit:
Also, your syntax ensures the DataView object referenced by dv is Disposed of when the code block exits. My second guess is this causes a side-effect that causes the problem.
using (DataView dv = dtProductGroup.DefaultView)
{
Instead leave out the using and write a regular declaratoin like the following (The DataView is going to be Disposed along with everything else when the page is done rendering so there's not really any need to do it yourself).
DataView dv = dtProductGroup.DefaultView;
See the MSDN documentation about 'using' and IDisposable for detailed info.

Categories