I'm working on a ASP .NET control (for Blackbaud's NetCommunity system) that needs to load data from the database, then update some controls' enabled state based on the data that has been loaded. From what I can see according to Microsoft's page lifecycle, the appropriate place to change this is during the Page_PreRenderComplete event, but this doesn't seem to work, so I'm guessing that I am wrong. However, I don't know for sure since I can't see where the actual database retrieval is taking place. I have two drop downs on this control that determine what data is shown in a grid below, and the grid needs to change its state based on the data inside. Do I need to figure out how to load the data early, and then make my state updates, or is there somewhere I can make the updates late and still have it render properly?
Page_PreRenderComplete is too late to manipulate a control. Normally, we use PreRender event.
However, if you want to manipulate controls located inside GridView, you want to use GridView's RowDataBound event in which you can find controls using FindControl.
For example,
protected void GridView1_RowDataBound(Object sender, GridViewRowEventArgs e)
{
if(e.Row.RowType == DataControlRowType.DataRow)
{
var textbox = e.Row.FindControl("MyTextBox") as TextBox;
}
}
Related
So I have an ASP.NET page with two controls:
a GridView which displays rows from a SqlDataSource, and in which the user can select a row;
a DetailsView in which the user can see and edit the values of the selected row.
The DetailsView can update the underlying data, but even though the page reloads, the GridView still displays the old data until I manually reload the page in the browser.
How do I ensure that the GridView displays the correct data after the update in the DetailsView?
P.S. It may be important to note that due to reasons outlined in this other question, I had to use a custom OnItemUpdating event with the DetailsView. This event performs the SQL update command (successfully), sets the DetailsView back to ReadOnly mode (out of Edit mode) and then cancels the event (e.Cancel = true). If this event canceling also somehow cancels the GridView updating, how do I manually tell it to update itself?
P.P.S.: I discovered this similar question, but the answer doesn’t work: it resets the entire page back to pristine state, which means the GridView loses its selected row and the DetailsView disappears. I don’t want that.
on page load:
YourGridViewID.DataBind()
If your OnItemUpdating event generates postback
protected void Page_Load(object sender, EventArgs e)
{
if (IsPostBack)
{
GridView.DataBind();//rebinding it with considering changes DetailView
}
}
If it doesn't work let me know.
In an effort to speed up my site, I am trying to disable the viewstate as I don't think I am using it everywhere. I have a master page setup with user controls loaded (using LoadControl) in default.aspx. My typical page setup would be:
Main.master -> Default.aspx -> ControlWrapper.ascx -> MyControl.ascx
I have put EnableViewState="false" in my Default.aspx page. Now when I try and read a value from a DropDownList in MyControl.ascx it comes back blank when the form is posted. First all, why is this? I thought I should still be able to read the value from the drop down list?
I then tried enabling the ViewState on that control and it didn't work.
I also tried enabling the viewstate on the Page_Init event of MyControl.ascx using Page.EnableViewState = True; but that didn't help either.
I guess I am misunderstanding the viewstate somewhat, can someone point me in the right direction please?
p.s I don't know if this information is relevant but I am adding the contents of the DropDownList dynamically in the Page_Load event. (Thinking about it, could this be the issues - Will test this now).
Thanks.
With viewstate turned off, the values you are loading in Page_Load are no longer in the list when you post back (until you reload them obviously). If you want to work without viewstate, you will need to set the selected item from the value in Request.Form.
protected void Page_Load(object sender, System.EventArgs e)
{
ddlItems.Items.Add(new ListItem("test1", "test1"));
ddlItems.Items.Add(new ListItem("test2", "test2"));
ddlItems.Items.Add(new ListItem("test3", "test3"));
if (Page.IsPostBack)
ddlItems.SelectedValue = Request.Form["ddlItems"];
}
When you've set ViewState to false the dropdown needs to get populated before page load - which means you probably should do it at page init. Something like this:
protected void Page_Init(object sender, System.EventArgs e)
{
ddlItems.Items.Add(new ListItem("test1", "test1"));
ddlItems.Items.Add(new ListItem("test2", "test2"));
ddlItems.Items.Add(new ListItem("test3", "test3"));
}
Then you should be able to read the value at load:
protected void Page_Load(object sender, System.EventArgs e)
{
someTextBox = ddlItems.SelectedValue;
}
A bit of background:
On this page: Microsofts page cycle
At the image with the page cycle there is the methods "ProcessPostData" and "LoadPostData" firing in between Init and Load. The post data for the drop down contains the selected value - but not the possible values, so when it loads the post data it is essential that the possible values are already there (or it won't be able to set the selected value). Also before the post data has been loaded the selected value has not been set.
If viewstate is enabled it saves and retrieves the possible values in between postbacks.
I will assume you're using .NET 4. View State is the method that the ASP.NET page framework uses to preserve page and control values between round trips.
The reason it didn't work for you when View State was turned off is because that control was rendered again when you performed a PostBack to the server, meaning you lost your selected value.
The reason it didn't work for you when View State was off for the page, but on for the control is because in order for that to work, the following conditions must be met:
The EnableViewState property for the page is set to true.
The EnableViewState property for the control is set to true.
The ViewStateMode property for the control is set to Enabled or inherits the Enabled setting.
ASP .NET View State Overview
When you did EnableViewState = false; on a page then you should not expect DropdownList.SelectedValue after postback.
It will be good if you Enable/Disable ViewState on particular controls rather than disabling whole view state by specifying it on page directive.
How can I prevent ASP.NET page from automatically binding data controls on the page? I want to increase performance and I want to do binding of each data control based on my own order.
Simple, don't setup data binding on the controls in the designer.
You would then have to bind the controls inside the code behind part of the page with code.
Not quite what the OP asked for but it is also possible to cancel the Select operation on the Datasource control by adding an event handler to the Selecting event.
public void DataSource_Selecting(object sender, ObjectDataSourceSelectingEventArgs e)
{
if (CancelSelect())
{
e.Cancel=true;
return;
}
}
I've created 2 controls to manipulate a data object: 1 for viewing and another for editing.
On one page I load the "view" UserControl and pass data to it this way:
ViewControl control = (ViewControl)LoadControl("ViewControl.ascx");
control.dataToView = dataObject;
this.container.Controls.Add(control);
That's all fine and inside the control I can grab that data and display it.
Now I'm trying to follow a similar approach for editing. I have a different User Control for this (with some textboxes for editing) to which I pass the original data the same way I did for the view:
EditControl control = (EditControl)LoadControl("EditControl.ascx");
control.dataToEdit = dataObject;
this.container.Controls.Add(control);
Which also works fine.
The problem now is getting to this data. When the user clicks a button I need to fetch the data that was edited and do stuff with it.
What's happening is that because the controls are being added programmatically, the data that the user changed doesn't seem to be accessible anywhere.
Is there a solution for this? Or is this way of trying to keep things separated and possibly re-usable not possible?
Thanks in advance.
Just keep a reference to the control as a variable in the page's code-behind. I.e.
private EditControl control;
protected void Page_Init(object sender, EventArgs e)
{
control = (EditControl)LoadControl("EditControl.ascx");
control.dataToEdit = dataObject;
this.container.Controls.Add(control);
}
protected void Button_Click(object sender, EventArgs e)
{
var dataToEdit = control.dataToEdit;
}
As long as you're creating the control in the right part of the page's lifecycle (Initialization) then ViewState will be maintained. I'm assuming dataToEdit is just a TextBox or something?
Controls created dynamically must be added on Init or PreInit event on the page.
You have to do it here because controls state is not yet loaded. If you add the the control AFTER you'll have them empty since the page already filled control page values early in the page life cycle.
More info about page life cycle here
Here's my solution:
In the page, I keep a reference to the control that is loaded programmatically.
In the control I created a GetData() method that returns an object with the data entered by the user.
When the user submits the form, the page calls the GetData() method on the control to access the data the user entered.
I am trying to build a server control that, depending on a "QuestionTypeId" display either a text box, date picker or Yes-No radio buttons.
I have my control displaying how I want it to, but when the submit button is pressed on the form, the text box, date picker or radio buttons that were generated in the RenderContents method are all null.
I have attempted to store the generated controls in view state, that stopped them being null, but the user inputs were not being stored.
I will post code if it is needed. Just ask.
I think you need to create (and add) the controls in CreateChildControls. This will mean you'll need to store the value of the QuestionTypeId in either Viewstate or ControlState (I'd argue that ControlState is applicable in this case, as your control can't work without this value).
When you add controls dynamically, you need to make sure they are recreated before the viewstate is restored.
I haven't done this in a while, but from memory I think you should recreate your controls in the OnInit method. This happens before postback data has been loaded and before the controls have their values set from viewstate.
It may be worth doing some reading on the asp.net page lifecycle:
http://msdn.microsoft.com/en-us/library/ms178472.aspx
You can create a user control and use server controls for textbox, datepicker, radiobuttons.
If you create a cusom server control then you have to add the posted data to your control properties. You can do this at your control OnInit event:
MyProperty = Request.Form("myControl");
A simpler method would be to create all the controls at design time and make the controls invisible based on you requirements.
Example code:
protected void Page_Load(object sender, EventArgs e)
{
txtBox.Visible = QuestionTypeID == 1;
chkBox.Visible = QuestionTypeID == 2;
}
If you do use dynamic controls you should do as David pointed out, save the value QuestionTypeID in ViewState or ControlState and then create the control you want based on that value.
(the control needs to be created every time the page loads even on a post back and they cannot be created later in the page life cycle then the Page_Load method if you want their ViewState persisted and recovered)
Example code:
protected void Page_Load(object sender, EventArgs e)
{
var questionId = ViewState["QuestionTypeID"];
if(questionId == /* Value to create TextBox */) {
var txt = new TextBox { ID = "txt" };
placeHolder.Controls.Add(txt);
} else if(questionId == /* Value to create Calender */) {
var cal = new Calender { ID = "cal" };
placeHolder.Controls.Add(cal);
}
/* Once the controls are added they will be populated with the posted values */
}
P.S.
It's always a good idea with dynamic controls to specify the ID.
You can save the added controls to member variables and use them elsewhere (after they are assigned)
You can subscribe to their events and if the user posted a new value your method will be called
I have followed your advice and done the following:
1) Question Type is stored in view state in my server control.
2) on CreateChildControls now creates a new instance of my control and adds it to a place holder on the page.
My problem now is that things seem to fire in bit of an odd order:
1)On initial load of page, create child controls is fired and the RenderContents method of my server control fires.
2)A button is clicked to load a new contact, this triggers create child controls and RenderContents is fired.
3)details are entered and save is pressed, this triggers Create Child Controls but RenderContents is not triggered and a NullReferenceException is generated by trying to access my control to get the value out. (If i skip the code that access my controls, RenderContents is called and renders.
Seconly, another issue is that when I try an set a value (onDataBind) I try to access the Text box that has been generated in my server control and get another NullReferanceExeption
Thoughts?