I have created a composite control with sample details as follows. Basically, the first time on page load the control sets a view state variable and the problem is that on post back (when the button is clicked), the ViewState variable is null. I have researched extensively and I am not able to find a solution. I checked all the Microsoft recommended articles and also from other developers. This approach seem to work for everyone and I can't figure out what I'm doing wrong. If anyone can help, I would really appreciate it.
PS: This code may not work as it is only for illustrative purposes. but this is exactly what i'm doing in my code.
Public class Test : CompositeControl
{
private Button btnTest = new Button();
public string TestViewState
{
get
{
string s = (string)ViewState["test"];
return (s == null) ? String.Empty : s;
}
set
{
ViewState["test"] = value;
}
}
private void set()
{
TestViewState = "test";
}
protected void Page_Load(object sender, EventArgs e)
{
if(!Page.IsPostBack)
set();
}
protected override void RecreateChildControls()
{
EnsureChildControls();
}
protected override void CreateChildControls()
{
base.Controls.Clear();
btnTest.ID = "btnTest";
btnTest.Click += new EventHandler(btnSubmitTest_Click);
if (!ChildControlsCreated)
Controls.Add(btnTest);
base.CreateChildControls();
}
protected override void Render(HtmlTextWriter writer)
{
btnSumbit.Render(writer);
}
protected void btnSubmitTest_Click(object sender, EventArgs e)
{
string test = TestViewState; // Viewstate value is null here!!!!!!
}
}
Are you sure that Page_Load is getting called? As far as I can remember that "notation" works only on pages and User Controls (didn't check that). Try overriding:
protected override void OnLoad(EventArgs e)
{
...
}
Test it with a Debugger.
Ok, the enableviewstate was disabled at the web.config level by another team member. Glad I found it. Thanks Arthur for confirming it worked for you.
Related
I have a composite control like:
class MyControl : CompositeControl {
private Control _control1;
private Control _control2;
public bool RenderControl2 { get; set; }
/* Constructor to initialize controls*/
protected override void CreateChildControls(){
if(RenderControl2){
Controls.Add(_control2);
}else{
Controls.Add(_control1);
}
}
}
This works fine in scenarios where the value of RenderControl2 is set during Page_Init().
protected void Page_Init(object sender, EventArgs e){
if (!Page.IsPostBack){
myControl.RenderControl2 = MyMagicFucntion();
}
/* Works also when in Postback, but not required since the control keeps it state and only need to change state in the scenario below.*/
}
However, now we would like to set the value as a result of an event
protected void otherDropDow_SelectedIndexChanged(object sender, EventArgs e) {
myControl.RenderControl2 = otherDropDown.SelectedValue == "My Magic String";
}
This does not work since the control already executed CreateChildControls by the time the event fired. (Well, it does work during the next postback... :( )
I have tried to move the logic to the OnDataBinding event of the control. But this seems to have no impact on how the control actually show on the page.
/* DOES NOT RESOLVE THE ISSUE */
protected override void OnDataBinding(EventArgs e){
base.OnDataBinding(e);
/* _renderControl2HasChanged is set when RenderControl2 changes value
*/
if(_renderControl2HasChanged)
if(RenderControl2){
Controls.Remove(_control1);
Controls.Add(_control2);
}else{
Controls.Remove(_control2);
Controls.Add(_control1);
}
}
Instead of making the decision which control to display in CreateChildControls, you could evaluate the flag in OnPreRender and only change the visibility of the child controls, e.g.:
protected override void CreateChildControls()
{
Controls.Add(_control1);
Controls.Add(_control2);
}
protected override void OnPreRender(EventArgs e)
{
_control1.Visible = !RenderControl2;
_control2.Visible = RenderControl2;
}
In addition, you should save the value of RenderControl2 in the control state as described there. This way it will be persisted across postbacks.
I've seen this problem a lot, and it's usually because the ViewState variables are being evaluated too early in the page lifecycle, or that EnableViewState is false, but I've checked for both of these and they are as I'd expect them to be.
My code:
public Int32 MyNumber
{
get
{
if (ViewState["MyNumber"] != null)
{
return (Int32)ViewState["MyNumber"];
}
return 0;
}
set
{
ViewState["MyNumber"] = value;
}
}
protected override void OnLoad(EventArgs e)
{
base.OnLoad(e);
if (!this.IsPostBack)
{
MyNumber = 23;
}
}
protected override void OnPreRender(EventArgs e)
{
base.OnPreRender(e);
var numberValue = MyNumber;
}
When OnPreRender is first called, numberValue is 23. Every subsequent call thereafter it's 0. Every time there's a postback it appears to get reset.
Question
How do I get the value to persist after a postback?
What I've Tried
Bringing the value assignment outside of the check for a postback, but then that defeats the purpose of using ViewState in the first place.
I was unaware there was also a ViewStateMode that would cause this behaviour if set to Disabled, which it was (on the master page).
I've now set that to Enabled on my control, and my ViewState values persist as I'd hope.
actually you have a missing order in your code block excaution and that makes the value reset by zero everytime between post packs :) ... in the onload Method you wrote the base.onload() method before your code block and that lead the Viewstate to be reset to 0 .
Please fellow that code correction:
public Int32 MyNumber
{
get
{
if (ViewState["MyNumber"] != null)
{
return (Int32)ViewState["MyNumber"];
}
return 0;
}
set
{
ViewState["MyNumber"] = value;
}
}
protected void Page_Load(object sender, EventArgs e)
{
Response.Write(MyNumber.ToString()); // Result now is 23 :)
}
protected override void OnLoad(EventArgs e)
{
// I replaced the order of the code here to get the value of the Viewstate in MyNumber property ..
if (!this.IsPostBack)
{
MyNumber = 23;
}
base.OnLoad(e);
}
protected override void OnPreRender(EventArgs e)
{
base.OnPreRender(e);
var numberValue = MyNumber;
}
Good Luck :).
In my application I'm using a usercontrol for two pages: AddInfo.aspx and EditInfo.aspx.
The thing is that I want different thing to happen when saving the info, depending on which page the user is at (ie. what he's actually doing).
So what i'm wondering is if there's any way to use an if statement to find out which page that right now is using the user control? In that case my problem could be solved.
protected void SaveButton_Click(object sender, EventArgs e) {
if (//The page using the usercontrol = Edit.aspx) {
// do this...
}
else {
// do that...
}
}
Thanks in advance
protected void SaveButton_Click(object sender, EventArgs e) {
if (this.Page is EditInfo) {
// do this...
}
else {
// do that...
}
}
Where EditInfo is your page's class.
You could also define a Behavior property on your user control and set it in your Xaml code according to what you want in which page. This would be a nice way to avoid the need to know where you are.
protected void SaveButton_Click(object sender, EventArgs e) {
if (Request.Url.AbsoluteUri.Contains("Edit.aspx")) {
// do this...
}
else {
// do that...
}
}
I have a custom class creating a dropdownlist control as below:
public class IHGridView : System.Web.UI.WebControls.WebControl
{
private string _dataSource = "not set yet";
public string DataSource
{
get { return _dataSource; }
set { _dataSource = value; }
}
}
EDIT:
protected override void OnInit(EventArgs e)
{
// VIewState is alive. When I select an option and submit, after postback it's selected value is the one I selected.
this.Controls.Add(_dropDownList);
}
or
protected override void CreateChildControls()
{
// VIewState is dead. When I select an option and submit, after postback it's selected value is the default one.
this.Controls.Add(_dropDownList);
}
So, now I come up with the result that I have to add control in "OnInit" void.
But, this "OnInit" is the first void that this class writes.
If I want to use a property like "DataSource" before, "OnInit" void...
How would I do that?
EDIT:
protected void Button1_Click(object sender, EventArgs e)
{
IHGridViewTest2.DataSource = "fired";
}
DataSource is set when the button in aspx page is fired.
why would you want to add _dropDownList twice? once in the OnInit should be enough, and OnInit is where it should be added if you want it in the control collection - thus having viewstate persisted and restored.
to access and bind _dropDownList for example, override the DataBind method - at which point all the properties will be available to you.
protected override void DataBind(){
base.DataBind();
_dropDownList.DataSource = this.DataSource;
_dropDownList.DataBind();
}
this is pseudo code, and has not been tested or validated
EDIT:
Call overriden DataBind method
protected void Button1_Click(object sender, EventArgs e)
{
IHGridViewTest2.DataSource = "fired";
IHGridViewTest2.DataBind();
}
Why DisplayUsers(); doesn't work?
My base page is:
public class adminPage : System.Web.UI.Page
{
protected override void OnLoad(EventArgs e)
{
if (User.Identity.IsAuthenticated == false) { Response.Redirect("~/Account/login.aspx?ReturnUrl=/admin"); };
if (!(User.IsInRole("admin") || User.IsInRole("super user"))) { Response.Redirect("/"); };
}
}
my class is
public partial class users : adminPage
{
protected void Page_Load(object sender, EventArgs e)
{
string sName;
adminGeneralData.GetToolData(2, out sName);
pageH1.Text = sName;
DisplayUsers();
}
protected void DisplayUsers()
{
DataSet ds = userData.GetUsersData();
userList.DataSource = ds;
userList.DataBind();
}
}
but DisplayUsers() doesn't work,
If I recall correctly, you'll need to call the base class's OnLoad event to register the Page_Load event properly:
protected override void OnLoad(EventArgs e)
{
if (User.Identity.IsAuthenticated == false) { Response.Redirect("~/Account/login.aspx?ReturnUrl=/admin"); };
if (!(User.IsInRole("admin") || User.IsInRole("super user"))) { Response.Redirect("/"); };
base.OnLoad(e);
}
Here are a couple of good reads:
OnLoad vs. Page_Load vs. Load event
When creating a web control should you override OnLoad or implement Page_Load
According to Performance Tips and Tricks in .NET Applications:
Avoid the Autoeventwireup Feature
Instead of relying on autoeventwireup, override the events from Page. For example, instead of writing a Page_Load() method, try overloading the public void OnLoad() method. This allows the run time from having to do a CreateDelegate() for every page.
In the code executed, there is no difference, but
AutoEventWireup should be enabled (usually in markup) for each page
Page_Load (and other events like this) uses automatic events subscription mechanism, which uses Reflection, what slightly hits performance
I personally recommend to override OnLoad(), just don't forget to call base.