I have a form that I would like to reuse for both adding a new record and editing an existing record. I am able to successfully load the page with the relevant data when I select a record from a GridView and I am able to update the db record appropriately. However, my issue is trying to use the form for both executions. Here is my logic in code behind: (I assign a session variable when I click on the row in GridView and this does work successfully)
protected void Page_Load(object sender, EventArgs e)
{
resultOutput.Visible = false;//Output results as to whether or not a record was added successfully is automatically hidden at page load
//Checking to see if session variable has been created
if (Session["editID"] != null)
{
//Create objects to get recipe data
dbCRUD db = new dbCRUD();
Recipe editRecipe = new Recipe();
//Grabbing session ID and assigning to a variable in order to remove the session variable
var id = Convert.ToInt32(Session["editID"]);
Session.Remove("editID");
//Call method to retrieve db data
editRecipe = db.SelectRecord(id);
//Populate results to text boxes
recordID.Text = id.ToString();
addName.Text = editRecipe.Name;
addTypeDDL.SelectedValue = editRecipe.Meal;
addDifficultyDDL.SelectedValue = editRecipe.Difficulty;
addCookTime.Text = editRecipe.Cook_Time.ToString();
addDirections.Text = editRecipe.Directions;
//Change Button Text
submitRecord.Visible = false;
changeRecord.Visible = true;
//Change Title Text
addEditTitle.Text = "Edit Recipe";
}
}
protected void submitRecord_Click(object sender, EventArgs e)
{
//Variables for execution results
var modified = "";
int returned = 0;
//Creating the recipe Object to pull the values from the form and
//send the recipe object as a parameter to the method containing insert stored procedure
//depending on Add or Edit
Recipe recipe = new Recipe();
recipe.Name = addName.Text;
recipe.Meal = addTypeDDL.Text;
recipe.Difficulty = addDifficultyDDL.Text;
recipe.Cook_Time = int.Parse(addCookTime.Text);
recipe.Directions = addDirections.Text;
//Creating object to access insert method
dbCRUD newRecord = new dbCRUD();
//Checking to see if the page is loaded for edit or new addition
if (Session["editID"] != null)
{
//If recordID exists, recipe will be passed as to UpdateRecord method
recipe.Recipe_ID = int.Parse(recordID.Text);
returned = newRecord.UpdateRecord(recipe);
modified = "has been edited.";
Session.Remove("editID");
}
else
{
//If recordID does not exist, record will be passed to InsertRecord method (new recipe)
returned = newRecord.InsertRecord(recipe);
modified = "added";
}
//Method returns 0 if successful, 1 if sql error, 2 if other error
if (returned == 1)
{
resultOutput.Text = "There was an sql exception";
resultOutput.Visible = true;
}
else if (returned == 2)
{
resultOutput.Text = "There was a non sql exception";
resultOutput.Visible = true;
}
else
{
resultOutput.Text = "\"" + addName.Text + "\" recipe " + modified;
resultOutput.Visible = true;
}
}
I have issues on the if(Session["editID"] != null) line - I am always moved to the else logic and the if logic never runs.
Here is my click method in the GridView:
protected void Grid_Recipe_SelectedIndexChanged(object sender, EventArgs e)
{
int index = Convert.ToInt32(Grid_Recipe.SelectedDataKey.Value);
Session["editID"] = index;
Response.Redirect("addRecord.aspx");
}
My question is how can I control execution during the submitRecord_Click event so that I call the appropriate method. Thanks!
Have you considered using
if(Page.IsPostBack)
{
code here
}
To detect whether you are posting back to the page? Then you could check your value of the item. I see no reason the code shouldn't be in the Session variable - have you tried putting a breakpoint in there to see if the code actually gets in there?
Also does your addRecord.aspx just add the record? If so, just add the record in this class, but use the PostBack check to see. Could you just make sure you are saving in the right context aswell:
// Outside of Web Forms page class, use HttpContext.Current.
HttpContext context = HttpContext.Current;
context.Session["editID"] = index;
...
int Id = (string)(context.Session["editID"]);
I was able to figure out my issue - which actually turned into two issues. First, I had to put my Page Load logic in a if(!IsPostBack) statement because I could not edit the page. Reason being is I was loading the originally posted data to the form on page load, which executed before my logic. Adding the if(!IsPostBack) control statement fixed this issue. From there, I'm still using a session variable to control code behind logic, only I made sure keep my session variable only between the form and the gridview. Basically, when the gridview loads and it is not a post back, the session variable is cleared. This let's me set a new session variable when I click on a row and then the session variable is cleared once I return to the grid to see the results. Thanks for the help!
Related
so i have an HTML table with dynamically added rows and ASP.NET text boxes. I have the rows and controls re-instantiated on page_load if the viewstate[dataonpage] = true, and I'm declaring it as true in the method that adds the rows and controls. (I need them to persist on other postbacks)
The problem is that I'm now I've added a CLEAR button that removes all of the html rows (excluding the headers) when it's clicked, and for some reason on button click it gets an index error, or if using Try/Catch it only removes half of the rows (every other row). I believe the problem is something to do with that the viewstate[dataonpage] is still "true", and the data is being re-added on page load. If i add viewstate["dataonpage"] = "false" into the clear button method, the same happens but at least this way on the second click it removes the second half of the rows.
I understand this happens because the button event handler isn't fired until after the page_load which is why it doesn't work on the first click. But what I don't fully understand is why even without this my clear button code doesn't clear all of the rows in the first place.
Any help on understanding why it doesn't work, and a work around will be greatly appreciated!
protected void Page_Load(object sender, EventArgs e)
{
if (Convert.ToString(ViewState["DataOnPage"]) == "true")
{
Getmarketdata();
}
}
protected void Getdatabtn_Click(object sender, EventArgs e)
{
ViewState["DataOnPage"] = "true";
Getmarketdata();
}
Below is method that creates adds table rows and controls:
public void Getmarketdata()
{
String url = "https://api.rightmove.co.uk/api/rent/find?index=0&sortType=1&maxDaysSinceAdded=" + Dayssinceuploadtext.Text + "&locationIdentifier=OUTCODE%5e" + Outcodetext.Text + "&apiApplication=IPAD";
Response.Write(url);
using (var webclient = new WebClient())
{
String Rawjson = webclient.DownloadString(url);
ViewState["VSMarketDataJSONString"] = Rawjson;
dynamic dobj = JsonConvert.DeserializeObject<dynamic>(Rawjson);
int NoOfHouses = dobj["properties"].Count;
Response.Write("<br />" + NoOfHouses);
for (int i = 0; i < NoOfHouses; i++)
{
System.Web.UI.HtmlControls.HtmlTableRow tRow = new System.Web.UI.HtmlControls.HtmlTableRow();
GeneratorTable.Rows.Add(tRow);
String RMlink = String.Format("https://www.rightmove.co.uk/property-to-rent/property-" + dobj["properties"][i]["identifier"].ToString()) + ".html";
HyperLink hypLink = new HyperLink();
hypLink.Text = dobj["properties"][i]["identifier"].ToString();
hypLink.Target = "_blank";
hypLink.NavigateUrl = RMlink;
using (System.Web.UI.HtmlControls.HtmlTableCell tb1 = new System.Web.UI.HtmlControls.HtmlTableCell())
{
tRow.Cells.Add(tb1);
tb1.Controls.Add(hypLink);
}
using (System.Web.UI.HtmlControls.HtmlTableCell tb2 = new System.Web.UI.HtmlControls.HtmlTableCell())
{
TextBox tbEPCe = new TextBox();
tRow.Cells.Add(tb2);
tb2.Controls.Add(tbEPCe);
String txtboxID = (("EPCETxtBox") + i);
tbEPCe.ID = txtboxID;
tbEPCe.Style.Add("background", "none"); tbEPCe.Style.Add("border", "1px solid black"); tbEPCe.Style.Add("border-radius", "2px");
}
using (System.Web.UI.HtmlControls.HtmlTableCell tb3 = new System.Web.UI.HtmlControls.HtmlTableCell())
{
TextBox tbEPCp = new TextBox();
tRow.Cells.Add(tb3);
tb3.Controls.Add(tbEPCp);
String txtboxID = (("EPCPTxtBox") + i);
tbEPCp.ID = txtboxID;
tbEPCp.Style.Add("background", "none"); tbEPCp.Style.Add("border", "1px solid black"); tbEPCp.Style.Add("border-radius", "2px");
}
using (System.Web.UI.HtmlControls.HtmlTableCell tb4 = new System.Web.UI.HtmlControls.HtmlTableCell())
{
TextBox tbBbl = new TextBox();
tRow.Cells.Add(tb4);
tb4.Controls.Add(tbBbl);
String txtboxID = (("BblTxtBox") + i);
tbBbl.ID = txtboxID;
tbBbl.Style.Add("background", "none"); tbBbl.Style.Add("border", "1px solid black"); tbBbl.Style.Add("border-radius", "2px");
}
}
}
}
Below is clear table rows method: (this is the one that isn't working)
public void ClearTableRows()
{
System.Web.UI.HtmlControls.HtmlTable Htmlgeneratortable = ((System.Web.UI.HtmlControls.HtmlTable)GeneratorTable);
int NoOfRows = Htmlgeneratortable.Rows.Count;
for (int j = 1; j < NoOfRows; j++)
{
try
{
Htmlgeneratortable.Rows.RemoveAt(j);
}
catch
{ }
}
}
I'm going to explain what's going on as you have the code written now; I don't have faith in my ability to provide an answer including the exact code changes to be made, so here is what is wrong with your current approach:
Your table, GeneratorTable exists for all clients. That doesn't mean every time someone navigates to your website a table is generated, it means that there is one table, and every client that logs in is getting that one table.
So if you add rows to it for one client, then send the table to another client, both clients will see the same table (with the rows added).
The problem is that emptying out a table is logic that has nothing to do with your back-end server. There's no reason for your server to be handling emptying a table, your server should only handle page navigations and AJAX calls pretty much, it shouldn't be changing how the webpage looks, because the server can only respond to each client one time.
What's the point in responding to a client with GeneratorTable and then updating GeneratorTable on the server? The client will never see the updates made to the table unless they're resent from the server.
You stated that you are new to this and need to learn about JS and client-side, this exercise should serve as an example of why you need to put certain code on the front-end and some code on the back-end, as there isn't really an elegeant way to do what you're looking to do with just the server.
I have a class that is a list of class objects.
[Serializable]
public class UserRequestOrders
{
private List<RTORequestOrder> m_thisUsersOrders = new List<RTORequestOrder>();
public List<RTORequestOrder> RequestOrders
{
get { return m_thisUsersOrders; }
set { m_thisUsersOrders = value; }
}
}
When I create an instance of this object I need to populate the list variable When m_thisUsersOrders with an existing list of requests (from a viewstate).
MyOrders.RequestOrders = (List<RTODataEntryObjects.RTORequestOrder>)ViewState["vsMyOrders"];
When the page posts back, I cast the viewstate into a list of RTORequestOrder objects, and try to set the RequestOrders property, I get the message that the property or indexer cannot be assigned to. I have a "SET" accessor for the property.
All posts that I have read on this topic state that I need to modify scope in the application settings, but I am not sure how that applies here. I don't have any application settings set.
I am hoping someone can help me understand how I can populate the list from an existing list.
EDIT: Nico, Thanks for your response! Below is the full code from the code behind page. "MyOrders" and "lst" are essentially the same thing. When I am working with "lst" everything works the way that I want it to. The reason that I want to use the "MyOrders" object instead is because I have a method that returns the list as a datatable with only the fields I need to display. (I didnt' show that code because the issue appears to be with the "SET" accessor.) "lst" has the same signiture as the "MyOrders.RequestOrders". Why can I cast the the viewstate into the lst object, but not the MyOrders object?
EDIT: Grant, thanks for your response as well. I don't know how to set breakpoints for ASP pages... :(
public partial class Default3 : System.Web.UI.Page
{
RTODataEntryObjects.UserRequestOrders MyOrders = new RTODataEntryObjects.UserRequestOrders();
List<RTODataEntryObjects.RTORequestOrder> lst = new List<RTODataEntryObjects.RTORequestOrder>();
void Page_PreRender(object sender, EventArgs e)
{
if (ViewState["vsMyOrders"] == null)
{
NewDataGrid.DataSource = (RTODataEntryObjects.UserRequestOrders)ViewState["vsMyOrders"]; // code to show the data in the grid when page loading
}
}
protected void Page_Load(object sender, EventArgs e)
{
NewDataGrid.EnableViewState = true;
NewDataGrid.DataSource = (RTODataEntryObjects.UserRequestOrders)ViewState["vsMyOrders"];
NewDataGrid.DataBind();
}
public void btnSubmit(object sender, EventArgs e)
{
int id = Int32.Parse(TextBox1.Text); // dynamically getting the data from the frontend.
string name = TextBox2.Text; // dynamically getting the data from the frontend.
if (ViewState["vsMyOrders"] != null) // if the view state is already having data then can update the list and assign again to the viewstate later.
{
lst = (List<RTODataEntryObjects.RTORequestOrder>)ViewState["vsMyOrders"];
MyOrders.RequestOrders = (List<RTODataEntryObjects.RTORequestOrder>)ViewState["vsMyOrders"];
}
RTODataEntryObjects.RTORequestOrder thisOrder = new RTODataEntryObjects.RTORequestOrder(id, name, User.Identity.Name, System.Environment.MachineName);
lst.Add(thisOrder); //
MyOrders.AddNew(thisOrder);
ViewState["vsMyOrders"] = MyOrders;
NewDataGrid.DataSource = (IList<RTODataEntryObjects.RTORequestOrder>)ViewState["vsMyOrders"];
NewDataGrid.DataBind();
}
}
I have a database table that I need to update records on. The code to add a new record works just fine, But when I go to update an existing record, not all the fields update with the new information in the form.
Here's the code:
private void updateExistingDSN()
{
//Update existing DSN
try
{
using (PathFinderDataContext pfdcContext = new PathFinderDataContext())
{
DSN oldDSN = pfdcContext.DSNs.Single(dsn => dsn.DSNID == int.Parse(Request["dsn"]));
oldDSN.Auth_AuthorizationID = int.Parse(Request["auth"]);
oldDSN.ServiceProvided_ServiceProvidedID = int.Parse(Request["sp"]);
oldDSN.EvidenceBPMU = short.Parse(ddlEvidenceBPMU.SelectedValue);
oldDSN.LocationOfVisit = txtLocationOfVisit.Text;
oldDSN.ChildrenPresent = txtNamesOfChildrenPresent.Text;
oldDSN.ParentPresent = txtNamesOfParentsPresent.Text;
oldDSN.OthersPresent = txtNamesOfOthersPresent.Text;
oldDSN.DescribeGoals = txtDescribeGoals.Text;
oldDSN.DescribeStrategy = txtDescribeStrategies.Text;
oldDSN.DescibeParentingSkills = txtDescribeParentingSkills.Text;
oldDSN.DescribeSafetyConcerns = txtDescribeSafetyConcerns.Text;
oldDSN.OtherInfo = txtOtherInfo.Text;
oldDSN.Schedule_Monday = float.Parse(txtMonday.Text);
oldDSN.Schedule_Tuesday = float.Parse(txtTuesday.Text);
oldDSN.Schedule_Wednesday = float.Parse(txtWednesday.Text);
oldDSN.Schedule_Thursday = float.Parse(txtThursday.Text);
oldDSN.Schedule_Friday = float.Parse(txtFriday.Text);
oldDSN.Schedule_Saturday = float.Parse(txtSaturday.Text);
oldDSN.Schedule_Sunday = float.Parse(txtSunday.Text);
oldDSN.DateSaved = DateTime.Now;
oldDSN.SavedBy_UserID = currentEmployee.EmployeeID;
pfdcContext.SubmitChanges();
}
Response.Redirect("~/pages/updateTimesheet.aspx?action=update&ProvidedServiceId=" + int.Parse(Request["sp"]));
}
catch (Exception ex)
{
errorMessage.Text = "<b>Error updating an existing DSN record!</b><br /><br />" + ex.ToString();
warnings.Visible = true;
}
}
The only field that updates is oldDSN.DateSaved, everything else just stays the same. No errors or exceptions thrown or anything. Acts like it works, but doesn't. Also, when I hardcode values to be updated, the record updates just fine. Any ideas?
In your Page_Load (where you put the information from your datasource into the TextBoxes, etc) you need to wrap the databinding code in a If(Page.IsPostBack) block.
So your code should look something like this:
protected void Page_Load (object sender, EventArgs e)
{
if(!Page.IsPostBack)
{
// Whatever you use to load the data from the database into
// your server controls goes here
loadData(); // example
}
}
This is why you're not getting the updated information - your markup elements are being reloaded from the database before your Update code has a chance to run.
I have defined a placeholder in my page like this;
<asp:PlaceHolder ID="attrPlaceHolder" runat="server"></asp:PlaceHolder>
I am populating this place holder from a database table using query string productId like this;
// obtain the attributes of the product
DataTable attrTable = CatalogAccess.GetProductAttributes(productId);
// temp variables
string prevAttributeName = "";
string attributeName, attributeValue, attributeValueId;
// current DropDown for attribute values
Label attributeNameLabel;
DropDownList attributeValuesDropDown = new DropDownList();
// read the list of attributes
foreach (DataRow r in attrTable.Rows)
{
// get attribute data
attributeName = r["AttributeName"].ToString();
attributeValue = r["AttributeValue"].ToString();
attributeValueId = r["AttributeValueID"].ToString();
// if starting a new attribute (e.g. Color, Size)
if (attributeName != prevAttributeName)
{
prevAttributeName = attributeName;
attributeNameLabel = new Label();
attributeNameLabel.Text = "<li class=\"txt\">" + attributeName + ":</li>";
attributeValuesDropDown = new DropDownList();
attrPlaceHolder.Controls.Add(attributeNameLabel);
attrPlaceHolder.Controls.Add(attributeValuesDropDown);
}
// add a new attribute value to the DropDownList
attributeValuesDropDown.Items.Add(new ListItem(attributeValue, attributeValueId));
}
However, when inside a button click event, when I loop through this place using visual studio debugging, I saw that the visual studio studio debugger first hit the "attrPlaceHolder.Controls" word in my foreach loop, then secondly comes to 'in' keyword (in foreach loop) but it isn't hitting the first two words (i-e 'Control cnt' in my foreach loop. Here it looks;
protected void ButtonBuyNow_Click(object sender, EventArgs e)
{
// Retrieve the selected product options
string options = "";
foreach (Control cnt in attrPlaceHolder.Controls)
{
if (cnt is Label)
{
Label attrLabel = (Label)cnt;
options += attrLabel.Text;
}
if (cnt is DropDownList)
{
DropDownList attrDropDown = (DropDownList)cnt;
options += attrDropDown.Items[attrDropDown.SelectedIndex] + "; ";
}
}
// Add the product to the shopping cart
ShoppingCartAccess.AddItem(productId, options);
}
Basically I need 'options' variable to be populated but it isn't hitting the foreach loop inside, therefore I am not able to get the 'options' variable populated.
This is a serious problem in my application. Please tell me why I can't get the inside the foreach loop.
NOTE:
please note that this isn't the complete code of my entire page. My rest of the code executes correctly.
why I can't get the inside the foreach loop
Because the list is empty.
Why is the list empty? (Would be the next logical question)
Because, at ASP.Net, dynamically created controls must be re-created at Page_Init in order to exist. When you create them at this stage, the page lifecycle will bind the viewstate and will be ready for use.
If you receive a postback (from the button, for example) and don't recreate them, they simply don't exist.
I have a GridView control which shows a list of all employees. When user selects any employee from this list, the record is shown on a Web Form with all input controls pre-filled with the values.
I want to know any good approach to do this. Should I bind all input controls to any SqlDataSource or should I re-populate all input controls by picking values from the DataSet.
First you add the select button on your GridView as:
<asp:ButtonField Text="Select" CommandName="ViewMe" ButtonType="Button" />
then you add the OnRowCommand="RowCommand" property on GridView to call this function when the button is clicked and on code behind the function:
protected void RowCommand(object sender, GridViewCommandEventArgs e)
{
// if the ViewMe command is fired
if (e.CommandName == "ViewMe")
{
// go to find the index on the grid view
int iTheIndexNow;
if (int.TryParse(e.CommandArgument.ToString(), out iTheIndexNow))
{
// Set and highlight the selected
TheGridView.SelectedIndex = iTheIndexNow;
// do we have the table data id ?
if (TheGridView.SelectedValue != null)
{
// now load the controls data using this id
LoadValuesFromDatabaseToControls(TheGridView.SelectedValue);
}
}
}
}
I prefer this way of command button because you can add more commands than the select, or edit, even the delete or copy... the just index change can be done for any reason (eg by changing page) and is also need again the select.
I use the subsonic 2 DAL for loading the data from the database. A sample code from my programs is:
void LoadValuesFromDatabaseToControls(string editID)
{
// Load it from database
AthUserMaiListName OneRow = new AthUserMaiListName(editID);
if (OneRow.IsNotExist)
{
// a custom control that show messages on top.
uResult.addMsg("Fail to load id " + editID, MsgType.error);
// close the view of the controls
dbViewPanel.Visible = false;
}
else // else we have the data and go for show them
{
// show this panel that contains the controls.
dbViewPanel.Visible = true;
// I keep my current edit id
lblID.Text = editID;
// just be sure that the select exist on DrowDownList
MyUtils.SelectDropDownList(ddlEventType, OneRow.CAddedFrom);
txtEmail.Text = OneRow.CEmail;
txtFirstName.Text = OneRow.CFirstName;
txtLastName.Text = OneRow.CLastName;
txtInsideTitle.Text = OneRow.CInsideTitle;
txtCompanyName.Text = OneRow.CCompanyName;
txtCreated.Text = DateTimeRender(OneRow.CreatedOn);
txtModified.Text = DateTimeRender(OneRow.ModifiedOn);
}
}
I used this code in my application-
A better approach would call to this mothod on gridview_select_index_change() event
private void PickValues(int SerialNum)
{
DataSet ds = new DataSet();
try
{
string Query = "SELECT * FROM tw_main WHERE sno = " + SerialNum;
ds = reuse.ReturnDataSet(Query, "Scheme");
//Add Scheme Code to new Session Variable
Session.Add("SerialNumber", ds.Tables[0].Rows[0]["sno"].ToString());
//Set Update Flag
TaskFlag = "UPDATE";
//Fill values of selected record on the Entry Form
if (ds.Tables[0].Rows[0]["schm_code"].ToString().Length > 0)
lblSchemeCode.Text = ds.Tables[0].Rows[0]["schm_code"].ToString();
ddlType.SelectedValue = ds.Tables[0].Rows[0]["schemetype"].ToString(); ddlDistrict.Text = ds.Tables[0].Rows[0]["dist_nm"].ToString(); ddlBlock.Text = ds.Tables[0].Rows[0]["block_nm"].ToString();
txtSchemeName.Text = ds.Tables[0].Rows[0]["schm_nm"].ToString();
txtPopulation2001.Text = ds.Tables[0].Rows[0]["population_2001"].ToString();
txtSupplySource.Text = ds.Tables[0].Rows[0]["supply_source"].ToString();
txtApprovalYear.Text = ds.Tables[0].Rows[0]["yr_approval"].ToString();
txtApprovalLetterNum.Text = ds.Tables[0].Rows[0]["approval_letter_num"].ToString();
txtApprovalAmount.Text = ds.Tables[0].Rows[0]["sch_apr_amt"].ToString();
txtWaitedLetterNum.Text = ds.Tables[0].Rows[0]["sch_waited_details"].ToString();
txtSchTransferLetterNum.Text = ds.Tables[0].Rows[0]["schm_trans_details"].ToString();
txtSchTransferDate.Text = ds.Tables[0].Rows[0]["schm_trans_date"].ToString();
txtOtherRemarks.Text = ds.Tables[0].Rows[0]["remarks"].ToString();
}
catch (Exception ex)
{
ScriptManager.RegisterClientScriptBlock(this.Page, this.Page.GetType(), "Warning", "alert('" + ex.Message.ToString() + "');",true);
}
finally
{
ds.Dispose();
gridSerialNo = 0;
}
}
EDIT
There might be better approach to do so but this works certainly fine.
The way I would perform this task since you want to create a data access layer is to create a class which has all the properties
Class:
public class tw_main
{
public string SchemeCode {get;set;}
}
DAL:
public class dal
{
public tw_main getSelectedValue(pass the parameters required by the method)
{
//your connection and query code
var twmain = new tw_main();
twmain.SchemaCode = ds.Tables[0].Rows[0]["schm_code"].ToString();
return twmain;
}
}
Web Page:
//depending upon where you add this a reference may need to be imported (using) to the namespace
var dalObj = new dal();
var tw = dalObj.getSelectedValue();
lblSchemeCode.Text = tw.SchemaCode;