CheckboxList selections cleared on linkbutton click (despite postback check) - c#

How are checkbox properties retrieved before they are cleared?
I'd like to count the number of checked boxes after clicking "Go!". Instead, after clicking, the boxes (even those that default to selected) are cleared and the count is zero.
Do I need another way of protecting the values from postback?
→
*.aspx
<%# Page Language="C#" Inherits="LunaIntraDB.sandbox" MasterPageFile="~/SiteMaster.master" %>
<%# MasterType VirtualPath="~/SiteMaster.master" %>
<asp:Content ContentPlaceHolderID="HeadContent" ID="HeadContentContent" runat="server">
</asp:Content>
<asp:Content ContentPlaceHolderID="MainContent" ID="MainContentContent" runat="server">
<asp:CheckBoxList ID="aCheckBoxList" runat="server" >
<asp:ListItem Value="DontCheck" runat="server">1</asp:ListItem>
<asp:ListItem Value="blah" Selected="True" runat="server">2</asp:ListItem>
</asp:CheckBoxList>
<asp:LinkButton ID="goButton" runat="server" Text="Go!" onclick="Clicked" />
</asp:Content>
*.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using MySql.Data.MySqlClient;
using System.Data;
using System.Collections;
namespace LunaIntraDB
{
public partial class sandbox : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
if (!IsPostBack) {
debugPrint("Load,beforeAdd");
aCheckBoxList.Items.Add("Added 1");
aCheckBoxList.Items.Add("Added 2");
aCheckBoxList.Items.Add("Added 3");
aCheckBoxList.Items[4].Selected = true;
debugPrint("Load,addedItems");
} else {
debugPrint("PostBack");
}
}
protected void Clicked(object sender, EventArgs e)
{
debugPrint("Button push");
}
/* Print Selected index and a count of selected checkboxes */
protected void debugPrint(String where) {
String count = aCheckBoxList.Items.Cast<ListItem>().Where(n => n.Selected).ToList().Count.ToString();
System.Diagnostics.Debug.WriteLine(where +": "+ aCheckBoxList.SelectedIndex.ToString() + " =>" + count + "/"+ aCheckBoxList.Items.Count.ToString() );
}
}
}
Console output
[0:] Load,beforeAdd: 1 =>1/2
[0:] Load,addedItems: 1 =>2/5
// check a bunch and click "Go!"
[0:] PostBack: -1 =>0/5
[0:] Button push: -1 =>0/5
Mono version
Mono JIT compiler version 3.2.3 (tarball Sun Sep 22 20:38:43 UTC 2013) Copyright (C) 2002-2012 Novell, Inc, Xamarin Inc and Contributors. www.mono-project.com
TLS: __thread
SIGSEGV: altstack
Notifications: epoll
Architecture: amd64
Disabled: none
Misc: softdebug
LLVM: supported, not enabled.
GC: sgen

Taken from http://www.strawberryfin.co.uk/blog/2012/08/22/dealing-with-dynamic-checkboxlists-losing-their-state-after-postback/
In Page_Load(object sender, EventArgs e):
setCheckBoxStates (aCheckBoxList);
elsewhere
public static void setCheckBoxStates(CheckBoxList cbl)
{
// if we are postback and using mono
if (HttpContext.Current.Request.HttpMethod == "POST" && Type.GetType("Mono.Runtime") != null)
{
string cblFormID = cbl.ClientID.Replace("_","$");
int i = 0;
foreach (var item in cbl.Items)
{
string itemSelected = HttpContext.Current.Request.Form[cblFormID + "$" + i];
if (itemSelected != null && itemSelected != String.Empty)
((ListItem)item).Selected = true;
i++;
}
}
}

Though #Will's answer in on the right track, it isn't conclusive of the entire issue.
For example, when a CheckBoxList is inside of Repeater control, the ClientID has a different format to indicate the CheckBoxList position inside the Repeater.
It's also safe to say that we don't really want to handle this issue for every use of a CheckBoxList, so I've created a custom control that extends the CheckBoxList.OnLoad event to resolve the issue outline above.
namespace YOURNAMESPACE.Controls
{
using System.Text.RegularExpressions;
using System;
using System.Web;
using WebControls = System.Web.UI.WebControls;
public class CheckBoxList : WebControls.CheckBoxList
{
protected override void OnLoad(EventArgs e)
{
base.OnLoad(e);
if (HttpContext.Current.Request.HttpMethod == "POST" && Type.GetType("Mono.Runtime") != null) {
string cblFormID = Regex.Replace(ClientID, #"_\d+$", String.Empty).Replace("_", "$");
int i = 0;
foreach (WebControls.ListItem item in Items)
item.Selected = HttpContext.Current.Request.Form[cblFormID + "$" + i++] == item.Value;
}
}
}
}

you should try EnableViewState="True" property at #page as follow:
<%# Page Language="C#" Inherits="LunaIntraDB.sandbox"
MasterPageFile="~/SiteMaster.master"
EnableViewState="True"
%>

Related

Calling Dynamically Created Textboxes in C# Visual Studio

In this post I wanted to figure out how to create dynamically created textboxes in C# Visual Studio.
Adding additional textboxes to aspx based on xml
However, when I try to call the ID of these dynamically created textboxes later in my code to figure out what text the user entered into them, I am getting an error that says these IDs do not exist in the current context. Does anyone know how I would be able to call these?
credit to Adding additional textboxes to aspx based on xml
Here is my entire code:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Xml.Linq;
namespace WebApplication4
{
public partial class WebForm15 : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
if (!Page.IsCallback)
{
//credit to https://stackoverflow.com/questions/44076955/adding-additional-textboxes-to-aspx-based-on-xml#comment75336978_44078684
const string xml = #"<Number>
<Num>1</Num>
<Num>2</Num>
<Num>3</Num>
</Number>";
XDocument doc = XDocument.Parse(xml);
int i = 0;
foreach (XElement num in doc.Root.Elements())
{
TextBox box = new TextBox
{
ID = "dynamicTextBox" + i,
Text = num.Value,
ReadOnly = false
};
divToAddTo.Controls.Add(box);
divToAddTo.Controls.Add(new LiteralControl("<br/>"));
i++;
}
}
}
protected void BtnGetValues_Click(object sender, EventArgs e)
{
IList<string> valueReturnArray = new List<string>();
foreach (Control d in divToAddTo.Controls)
{
if (d is TextBox)
{
valueReturnArray.Add(((TextBox)d).Text);
}
}
//valueReturnArray will now contain the values of all the textboxes
}
}
}
Here is aspx:
<%# Page Language="C#" AutoEventWireup="true" CodeBehind="WebForm15.aspx.cs" Inherits="WebApplication4.WebForm15" %>
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
<title></title>
<script src="https://code.jquery.com/jquery-1.12.4.min.js"></script>
</head>
<body>
<form id="form1" runat="server">
<div id="divToAddTo" runat="server" />
<asp:Button runat="server" ID="BtnGetValues" Text="GetValues" OnClick="BtnGetValues_Click" />
</form>
</body>
</html>
Figured it out!!! Here is what I found after scouring the internet for hours
Solution:
When using dynamic controls, you must remember that they will exist only until the next postback.ASP.NET will not re-create a dynamically added control. If you need to re-create a control multiple times, you should perform the control creation in the PageLoad event handler ( As currently you are just creating only for first time the TextBox using Condition: !IsPostabck ). This has the additional benefit of allowing you to use view state with your dynamic control. Even though view state is normally restored before the Page.Load event, if you create a control in the handler for the PageLoad event, ASP.NET will apply any view state information that it has after the PageLoad event handler ends.
So, Remove the Condition: !IsPostback, So that each time the page Loads, The TextBox control is also created. You will also see the State of Text box saved after PageLoad handler completes. [ Obviously you have not disabled ViewState!!! ]
Example:
protected void Page_Load(object sender, EventArgs e)
{
TextBox txtBox = new TextBox();
// Assign some text and an ID so you can retrieve it later.
txtBox.ID = "newButton";
PlaceHolder1.Controls.Add(txtBox);
}
Now after running it, type anything in text box and see what happens when you click any button that causes postback. The Text Box still has maintained its State!!!

User Control and constructor

i have built a user control with web controls in ascx page.
//ascx file
<%# Control Language="C#" AutoEventWireup="true" ClassName="Product"CodeFile="Product.ascx.cs" Inherits="Usercontrols_Product" %>
<link href="../StyleSheet/StyleSheet.css" rel="stylesheet" type="text/css" />
<div>
<asp:Panel ID="box" CssClass="productBox" runat="server">
<asp:Image ID="imgProduct" CssClass="productImage" runat="server" /><br />
<asp:Label ID="lblProductName" CssClass="productLbl" runat="server"></asp:Label><br/>
<asp:Label ID="lblProductPrice" CssClass="productLbl" runat="server"></asp:Label>
</asp:Panel>
</div>
in the cs file of user control i created acontructor which get another class reference.
whith the class i get in contructor i want insert data to child controls.
//ascx.cs file
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
public partial class Usercontrols_Product : System.Web.UI.UserControl
{
public EventHandler cmdDetailsClick;
protected Product thisProduct;
public Usercontrols_Product( Product pr)
{
thisProduct = pr;
imgProduct.ImageUrl = thisProduct.ImageUrl;
lblProductName.Text = thisProduct.CompanyName + "<br/>" + thisProduct.ProductName + "&nbsp" + thisProduct.Model;
lblProductPrice.Text = thisProduct.Price.ToString() + "&nbsp" + "israeli shekel";
}
public Usercontrols_Product()
{
}
protected void cmdContinue_clicked(object sender, EventArgs e)
{
//we send to event product class of sender
if (cmdDetailsClick != null)
cmdDetailsClick(thisProduct, EventArgs.Empty);
}
}
the problem is that now with spesial constructor web controls from aspx file are not being
initiallized. i tried initiallize them in code with new keyword but than they are with no attributes from ascx file. with no special constructor its works fine .but i need this special constructor. how can i initiallize this webcontrols whith their attributes from ascx file
The UserControl is instantiated by Page (i.e. constructor of user control is out of your control) - you can define one, but the default constructor will be called instead. If you want to pass data, there are numerous techniques how to do so; simplest way:
public Product Product {
set {
thisProduct = value;
imgProduct.ImageUrl = value.ImageUrl;
lblProductName.Text = value.CompanyName + "<br/>" +
value.ProductName + "&nbsp" + value.Model;
lblProductPrice.Text = value.Price.ToString() + "&nbsp" + "israeli shekel";
}
}
To clarify how the process work on behind:
.aspx (or .ascx) file are being compiled on runtime by IIS.
So for example if you have simple user control:
<%# Control CodeBehind="FooControl.ascx.cs" Inherits="TestApplication.FooControl" %>
<asp:Literal ID="litBar" runat="server" Text="Whatever" />
IIS will make something like this from your control (shortened):
namespace ASP {
public class foocontrol_ascx : global::TestApplication.FooControl {
private static bool #__initialized;
public foocontrol_ascx() {
((global::WebApplication4.FooControl)(this)).AppRelativeVirtualPath = "~/FooControl.ascx";
if ((global::ASP.foocontrol_ascx.#__initialized == false)) {
global::ASP.foocontrol_ascx.#__initialized = true;
}
}
private global::System.Web.UI.WebControls.Literal #__BuildControllitBar() {
global::System.Web.UI.WebControls.Literal #__ctrl;
#__ctrl = new global::System.Web.UI.WebControls.Literal();
this.litBar = #__ctrl;
#__ctrl.ID = "litBar";
#__ctrl.Text = "Whatever";
return #__ctrl;
}
private void #__BuildControlTree(foocontrol_ascx #__ctrl) {
global::System.Web.UI.WebControls.Literal #__ctrl1;
#__ctrl1 = this.#__BuildControllitBar();
System.Web.UI.IParserAccessor #__parser = ((System.Web.UI.IParserAccessor)(#__ctrl));
#__parser.AddParsedSubObject(#__ctrl1);
}
protected override void FrameworkInitialize() {
base.FrameworkInitialize();
this.#__BuildControlTree(this);
}
}
}
Notice the built class inherits from your code behind and takes care about the markup as well (see how it creates literal litBar and set text to "Whatever"). Also this class has only one constructor - default constructor (so it naturally calls default constructor from class in code behind). I.E. - you can define custom constructor for your control but you have to provide default constructor anyway (so the built can work with it) and the built class ignores it (as the IIS can have no clue what parameters you want to pass to your custom constructor).

ASP.NET Set Page Title from Code Behind

I am creating data driven pages using ASP.NET C# and want to dynamically set the page title using code behind
<%# Page Title="" Language="C#" MasterPageFile="~/FLMaster.master" AutoEventWireup="true" CodeFile="legal-expenses-insurance-news-item.aspx.cs" Inherits="legal_expenses_insurance_news_legal_expenses_insurance_news_item" %>
I have tried using the separate title tags lower down in the page but this didn't work either. Can anyone advise how to do this.
using System;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
namespace MyApplication
{
public partial class _Default : Page
{
protected void Page_Load(object sender, EventArgs e)
{
this.Title = "Title of my page";
}
}
}
You can modify the Page title like above from your aspx.cs (the code behind file).
If you want to do this directly in your .aspx file, try this:
<% this.Title = "Some Title" %>
This works if you correctly set your Language = "C#" in your #Page directive, which I see you did.
Page class reference from MSDN
The Page has a Title property:
protected void Page_Load(object sender, EventArgs e)
{
this.Title = "Title";
}
You should remove the Title="" from the aspx page. It will override the Title set in your code-behind
<%# Page Language="C#" MasterPageFile="~/FLMaster.master" AutoEventWireup="true" CodeFile="legal-expenses-insurance-news-item.aspx.cs" Inherits="legal_expenses_insurance_news_legal_expenses_insurance_news_item" %>
protected void Page_Load(object sender, EventArgs e)
{
if (!Page.IsPostBack)
{
this.Title = "Title";
}
}
I know that this is an old thread, but I found that Page.Title couldn't always be overriden, but Page.Header.Title could (mostly)... so my solution for dynamic title tags from the Master codebehind is:
if (Page.Header != null)
{
if (Page.Header.Title == null || !Page.Header.Title.Contains("COMPANYNAME"))
{
var otitle = Page.Header.Title;
if (otitle == null || otitle.Length==0) {
var textinfo = System.Threading.Thread.CurrentThread.CurrentCulture.TextInfo;
otitle = textinfo.ToTitleCase(this.Parent.GetType().Name.Replace("_"," ").Replace("aspx",""));
}
Page.Header.Title = "COMPANYNAME" + " - " + otitle;
}
Page.Header.Title = Page.Header.Title.Replace("COMPANYNAME", Auth.getSetting("companyName"));
}

Getting SelectedValue from a DropDownList

I am trying to create a basic macro that encapsulates a form and the ability to email the details entered into it. It works fine for Textboxes, but for some reason the DropDownLists take the first value in the list of options. I've been working on this for hours and seem to've tried everything, so hopefully someone can suggest a solution. I am using Umbraco 4.0.3 and unfortunately upgrade is not an option. My reduced code is as follows:
CorpRefundForm.ascx:
<%# Control Language="C#" AutoEventWireup="true" CodeFile="CorpRefundForm.ascx.cs" Inherits="umbracowebsitewizard_site.Usercontrols.CorpRefundForm" %>
<asp:DropDownList ID="frm_dropdown" runat="server" CssClass="linkselect" />
<br />
<button id="submitButton" runat="server" onserverclick="submitButton_Click">Submit</button>
CorpRefundForm.ascx.cs
using System;
using System.Net.Mail;
using System.Web.UI;
using System.Web.UI.WebControls;
namespace umbracowebsitewizard_site.Usercontrols
{
public partial class CorpRefundForm : UserControl
{
protected void Page_Init(object sender, EventArgs e)
{
if (!Page.IsPostBack)
{
frm_dropdown.Items.Add(new ListItem("Select one", ""));
frm_dropdown.Items.Add(new ListItem("One", "One"));
frm_dropdown.Items.Add(new ListItem("Two", "Two"));
frm_dropdown.Items.Add(new ListItem("Three", "Three"));
frm_dropdown.Items.Add(new ListItem("Four", "Four"));
}
}
public void submitButton_Click(object sender, EventArgs e)
{
SmtpClient mySMTPClient = new SmtpClient();
mySMTPClient.Send("[email removed]", "[email removed]", "Test", frm_dropdown.SelectedValue + frm_dropdown.Text + frm_dropdown.SelectedItem.Value + frm_dropdown.SelectedItem.Text);
}
}
}
CorpRefundForm.ascx.designer.cs:
using System.Web.UI.WebControls;
namespace umbracowebsitewizard_site.Usercontrols
{
public partial class CorpRefundForm
{
protected DropDownList frm_dropdown;
}
}
That probably happens since your first ListItem does not have a Value assigned. Try to assign a value, and see if it works.
frm_dropdown.Items.Add(new ListItem("Select one", "-1"));
Solved it! Turns out the jquery.linkselect-1.2.07.min.js library was doing something with the DropDownList that broke it. Took that out and it worked.

ASP.NET C# scope issue

OK, so I'm tyring to work with some simple stuff in ASP.NET and C# to get the hang of the super super basics, and I'm having trouble finding out how I can get this scope issue to work. At least, I think it's a scope issue! :)
So, here is my Default.aspx presentation markup:
<%# Page Language="C#" Inherits="project1.Tree" %>
<!DOCTYPE html>
<html>
<head runat="server">
<title>project1_test</title>
</head>
<body>
<form runat="server">
<p><asp:label runat="server" id="lblOutput" /></p>
<asp:TextBox ID="txtGrowBy" runat="server" />
<asp:Button id="btnGrow" runat="server" Text="Grow!!!" />
</form>
</body>
</html>
And here is my CodeBehind file:
using System;
using System.Web;
using System.Web.UI;
namespace project1
{
public partial class Tree : System.Web.UI.Page
{
public int height = 0;
public void Grow(int heightToGrow) {
height += heightToGrow;
}
protected void Page_Load(Object Source, EventArgs E)
{
Tree tree1 = new Tree();
string msg = "Let's plant a tree!<br/>";
msg += "I've created a tree with a height of " +
tree1.height + " metres.<br/>";
lblOutput.Text = msg;
}
public virtual void btnGrowClicked (object sender, EventArgs args)
{
txtGrowBy.Text = tree1.heightToGrow;
}
}
}
Now, I believe the issue lies with the fact that I'm not using a getter and sender, but I'm not 100% sure.
I take it you're variable height is not being maintained between postbacks?
This is because the web is stateless. Your variables are not maintained between postbacks unless you store the values in Session, ViewState or Hidden Fields. So you could do the following to maintain your height value between PostBacks:
ASPX:
<form runat="server">
<asp:HiddenField id="hd_Height" runat="server" />
</form>
Code Behind:
public int Height
{
get
{
return int.Parse(hd_Height.Value).ToString();
}
set
{
hd_Height.Value = value.ToString();
}
}
There's several immediate problems with your code; in the Page_Load method, you're creating a new Tree instance - you don't have to do this, as one was automatically created by IIS when the ASP.NET page was accessed. Use the this keyword in the Page_Load method to get access to that instance.
Further, nothing ever seems to call the Grow method; is this intentional? Shouldn't you be calling that from within the btnGrowClicked method?
And finally as GenericTypeTea points out, your height field won't be maintained between Postbacks. Easiest way to do this is with session state, eg:
private void PersistHeight(int height)
{
this.Session["height"] = height;
}
private int RestoreHeight()
{
return (int)this.Session["height"];
}
You could use viewstate as well (if Mono supports it); simply add the updated value to viewstate before giving control back to the browser. Then read from viewstate the original value saved in the last postback and incrementing the new value to it:
public int MyValue
{
get {return Convert.ToInt32(viewstate["myVal"]);}
set {viewstate["myVal"] = value;}
}
I hope that makes sense.

Categories