Hidden Field altered through javascript not persisting on postback - asp.net

I have a web user control with a hidden field on it. When a javascript event (click) ocurrs, I am trying to set a value in the hidden field, so that the value can be retained on postback and remembered for the next rendering. The control is a collapsible panel extender that does not cause postback, uses jquery, and if postback occurs elsewhere on the page, it remembers if it is expanded or collapsed.
The problem is that the javascript executes, but does not actually change the value in the hidden field. If I use the dom explorer, the hidden fiend is still set to the default, and then when I debug, in the the next postback the hidden field is still set to the default as well.
I have also tried using the tried and true getElementById with no success.
No javascript errors occur.
ASCX code:
<input id="hiddenCurrentState" type="hidden" runat="server" />
Codebehind:
using System;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Text;
public partial class Controls_SubControls_CollapsiblePanelExtender : System.Web.UI.UserControl
{
public string HeaderControlId { get; set; }
public string BodyControlId { get; set; }
public string CollapseAllControlId { get; set; }
public string ShowAllControlId { get; set; }
public CollapsedState DefaultState { get; set; }
protected void Page_Load(object sender, EventArgs e)
{
if (!IsPostBack)
{
hiddenCurrentState.Value = DefaultState.ToString();
}
}
protected override void OnPreRender(EventArgs e)
{
BuildJQueryScript();
base.OnPreRender(e);
}
private void BuildJQueryScript()
{
StringBuilder script = new StringBuilder();
script.Append("$(document).ready(function(){\n");
//toggle based on current state
script.Append("if ($(\"#" + hiddenCurrentState.ClientID + "\").attr(\"value\")==\"Expanded\")\n");
script.Append("{\n");
script.Append("$(\"#" + BodyControlId + "\").show();\n");
script.Append("$(\"#" + hiddenCurrentState.ClientID + "\").val(\"Expanded\");\n");
script.Append("}\n");
script.Append("else\n");
script.Append("{\n");
script.Append("$(\"#" + BodyControlId + "\").hide();\n");
script.Append("$(\"#" + hiddenCurrentState.ClientID + "\").val(\"Collapsed\");\n");
script.Append("}\n");
//toggle on click
script.Append("$(\"#" + HeaderControlId + "\").click(function(){\n");
script.Append(" $(this).next(\"#" + BodyControlId + "\").slideToggle(500)\n");
script.Append(" return false;\n");
script.Append("});\n");
//collapse all
if (!String.IsNullOrEmpty(CollapseAllControlId))
{
script.Append("$(\"#" + CollapseAllControlId + "\").click(function(){\n");
script.Append(" $(\"#" + BodyControlId + "\").slideUp(500)\n");
script.Append(" return false;\n");
script.Append("});\n");
}
//show all
if (!String.IsNullOrEmpty(ShowAllControlId))
{
script.Append("$(\"#" + ShowAllControlId + "\").click(function(){\n");
script.Append(" $(this).hide()\n");
script.Append(" $(\"#" + BodyControlId + "\").slideDown()\n");
//script.Append(" $(\".message_list li:gt(4)\").slideDown()\n");
script.Append(" return false;\n");
script.Append("});\n");
}
script.Append("});\n");
Page.ClientScript.RegisterClientScriptBlock(this.GetType(), "CollapsiblePanelScript", script.ToString(), true);
}
}
public enum CollapsedState
{
Expanded = 0,
Collapsed = 1
}

I don't see where you are setting the value of the hidden field on the client side. I would expect to see a line something like the following in your collapse/show functions to actually change the value on the client when the panel is collapsed/expanded.
Collapse:
script.Append( " $(\"#" + hiddenCurrentState.ClientID + "\").val(1);\n" );
Show:
script.Append( " $(\"#" + hiddenCurrentState.ClientID + "\").val(0);\n" );

On every post back the entire page is rendered again, any values changed on the client side will not be persisted.
I recommend you store the state in a cookie. As you are using jQuery, the COOKIE library makes this a cinch.

Have you tried using <asp:HiddenField> instead of <input>?

Related

How to update GridView on UI before method ends

I am running a small app in SharePoint that produces the data needed for someone in our company. In short it pulls data from an Azure database into a data-table, and then converts that data-table to excel. This can take about a minute or two and sometimes, because we are in New Zealand and are using a remote server in the USA, they get timeouts and one of the worksheets won't load.
So what it does when it builds the excel is it iterates through a list of suppliers, and a list of financial weeks getting the data for each and creating a separate worksheet in excel per data-table. Ideally, what I would like to add a new row to a grid view that the user sees, as the report is being built, stating whether that financial week and supplier was successfully added, or not, as the excel report is creating in the back-end. This would allow the user to be more aware of the progress, and allow them to know if there has been a problem rather than guessing.
It's a lot of code so I will try to show you the relevant parts.
Method that pulls and creates excel
public void excelThreadCall()
{
DataTable updateDataTable = new DataTable();
gridView.DataSource = updateDataTable;
//Payments only download chosen Financial Week
using (XLWorkbook workbook = new XLWorkbook())
{
//gradeWeek = selectedGradeWeek.SelectedValue;
foreach (ListItem supplier in selectedSuppliers.Items)
{
if (supplier.Selected)
{
foreach (ListItem fWeek in selectedfWeeks.Items)
{
if (fWeek.Selected)
{
string checkEmptyTableSQL = #"SELECT COUNT(*) FROM FleshvGraded WHERE Supplier_Code = '" + supplier.Value + "' AND PO_Revision = " + fWeek.Value;
int rowCount = Convert.ToInt32(getVariable(checkEmptyTableSQL));
if (rowCount > 0)
{
foreach (ListItem report in selectedReports.Items)
{
//SQL Strings
string sqlIntakeDate = #"SELECT Week_Ending_Date FROM Fiscal_Calendar WHERE Fiscal_Week = LEFT(" + fWeek + ", 2) AND Fiscal_Year = CONCAT(20, RIGHT(" + fWeek + ", 2))";
string sqlPO = #"SELECT DISTINCT PO_No FROM FvGSummaryAll WHERE Supplier_Code = '" + supplier.Value + "' AND f_Week = " + fWeek.Value;
string sqlAllSerials = "SELECT * FROM FvGData WHERE Supplier_Code = '" + supplier.Value + "' AND f_Week = " + fWeek.Value
//variables
DateTime weekEnding = Convert.ToDateTime(getVariable(sqlIntakeDate));
DateTime weekStarting = weekEnding.AddDays(-5);
string fWeekString = fWeek.ToString();
string poNoString = getVariable(sqlPO).ToString();
string intakeDateString = weekStarting.Day + "/" + weekStarting.Month + "/" + weekStarting.Year + " to " + weekEnding.Day + "/" + weekEnding.Month + "/" + weekEnding.Year;
//adds summary variables to dictionary
Dictionary<string, string> summaryVariablesDict = new Dictionary<string, string>();
summaryVariablesDict.Add("f Week", fWeekString);
//other values added to Dict
//Adds WorkSheets based on above data
if (report.Selected && report.Value.Equals("allserials"))
{
string worksheetName = supplier.Value + " Data " + fWeek.Value;
DataTable dataTable = getDataTable(sqlAllSerials);
createWorkSheet(workbook, worksheetName, dataTable);
}
//Other Reports follow
**//what I hope to do - need this to show in the grid view immediatley not end of method
updateDataTable.Rows.Add(suppler, fweek, "successful");
gridView.DataBind();**
}
}
}
}
}
}
workbook.SaveAs(filePath);
}
}
So currently this exists in another class but it's no problem for me to move it to the aspx page, and so I have taken liberties to just show you what I need to do in this method. So if it doesn't make complete sense in that respect (i.e. I wouldn't declare the datasource for the grid in the method normally).
The problem I have is that it will wait until the end of the method before updating the grid view via the postback and then the user gets it all at once. I was hoping there is a way to update the gridview at each iteration or even every few seconds if we use a timer, but can't find a way to implement this.
So long story short, how can I update the gridview from this method where the results appear immediately on the users UI, and not wait until the end of the method.
I would do something along these lines:
When the page first loads, start a background thread to start building the spreadsheet.
When the page loads, call some JavaScript that kicks off a callback.
In the method in your code-behind that's called by the callback, check the status of the building process. Have that process maintain a list of strings, each representing the HTML for a row in a table.
Have the page (via JavaScript) perform a callback every few seconds. That callback will get the current list of rows. JavaScript on the page will receive the response and update the rendered table to include the new rows.
When the spreadsheet is done (or when a error occurs that aborts the creation process), show a success or failure message to the user.
If it would be helpful, I can provide some simple callback samples to get you going.
Edit: Added code sample:
Markup:
<%# Page Language="C#" AutoEventWireup="true" CodeBehind="Default.aspx.cs" Inherits="CallBackWebForm.Default" %>
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
<title></title>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.2.2/jquery.min.js"></script>
<script type="text/javascript">
var callbackFrequency = 2000;
// Callback javascript
// To make callback to server, call CallServer();
// Receive response from server after callback
function ReceiveServerData(arg, context) {
// Parse the JSON that we got from the server
args = JSON.parse(arg);
// Add rows to table
$.each(args.TableRows, function (index, value) {
$('#table1').append(value);
});
// If we're done, show a message
if (args.DoneLoadingSpreadsheet)
$('#doneDiv').show();
// Otherwise, start a timer to call back again
else
window.setTimeout(function () { CallServer(); }, callbackFrequency);
}
$(document).ready(function() {
// Start the callback loop
window.setTimeout(function () { CallServer(); }, callbackFrequency);
});
</script>
</head>
<body>
<form id="form1" runat="server">
<div>
Sample page with some progress-y stuff
</div>
<table id="table1">
<tr>
<th>Col 1</th>
<th>Col 2</th>
<th>Col 3</th>
</tr>
<!-- Rows inserted by Javascript will go here -->
</table>
<div id="doneDiv" style="display: none;">
All done!
</div>
</form>
</body>
</html>
Code-behind:
using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using System.Web.UI;
using Newtonsoft.Json;
namespace CallBackWebForm
{
public partial class Default : System.Web.UI.Page, ICallbackEventHandler
{
protected void Page_Load(object sender, EventArgs e)
{
// Setup Callback javascript so that page can initiate callbacks and receive callback responses
CreateClientSideCallbackFunction();
if (!Page.IsPostBack)
StartBuildingSpreadsheetTask();
}
#region Callback
private void CreateClientSideCallbackFunction()
{
var cm = Page.ClientScript;
// The Javascript function in the markup must exactly match the function name as entered below (ReceiveServerData)
var cbReference = cm.GetCallbackEventReference(this, "arg", "ReceiveServerData", "");
// The Javascript function to be placed in the markup which will be used to initiate the callback
var callbackScript = "function CallServer(arg, context) {" + cbReference + "; }";
cm.RegisterClientScriptBlock(this.GetType(), "CallServer", callbackScript, true);
}
/// <summary>
/// Called when CallServer(arg, context) is called in javascript on the page
/// </summary>
/// <param name="eventArgument">Not used, but must be passed</param>
public void RaiseCallbackEvent(string eventArgument)
{
}
/// <summary>
/// Called at the end of a callback; provides the response/result to the client
/// </summary>
/// <returns>JSON string representing an instance of the DataTransferClass</returns>
public string GetCallbackResult()
{
// Serialize the DataTransferObject, then delete all TableRows so we don't send them to the browser again
// Note: this is not currently thread-safe. You should add some sort of locking mechanism so the background thread
// doesn't modify the TableRows list while we're serializing it and deleting from it.
var dtoJson = JsonConvert.SerializeObject(DataTransferObject);
DataTransferObject.TableRows.Clear();
return dtoJson;
}
public class DataTransferClass
{
public bool DoneLoadingSpreadsheet { get; set; }
public List<string> TableRows { get; set; }
}
#endregion Callback
#region Background Task
// Sessions have unique IDs, but individual page views don't. So, create one for this page view.
private string ViewID
{
get
{
if (string.IsNullOrEmpty(ViewState["_viewID"] as string))
ViewState["_viewID"] = Guid.NewGuid().ToString();
return ViewState["_viewID"] as string;
}
}
// Store all DataTransfer data and token sources in static dictionaries so the background task can get to them
private static Dictionary<string, DataTransferClass> DataTransferDictionary = new Dictionary<string, DataTransferClass>();
private static Dictionary<string, CancellationTokenSource> TokenSourcesDictionary = new Dictionary<string, CancellationTokenSource>();
// Make the values in the dictionaries for this View easily accessible via Properties
private DataTransferClass DataTransferObject
{
get
{
if (DataTransferDictionary.ContainsKey(ViewID))
return DataTransferDictionary[ViewID];
else
return null;
}
set
{
if (DataTransferDictionary.ContainsKey(ViewID))
DataTransferDictionary[ViewID] = value;
else
DataTransferDictionary.Add(ViewID, value);
}
}
private CancellationTokenSource TokenSource
{
get
{
if (TokenSourcesDictionary.ContainsKey(ViewID))
return TokenSourcesDictionary[ViewID];
else
return null;
}
set
{
if (TokenSourcesDictionary.ContainsKey(ViewID))
TokenSourcesDictionary[ViewID] = value;
else
TokenSourcesDictionary.Add(ViewID, value);
}
}
private void StartBuildingSpreadsheetTask()
{
DataTransferObject = new DataTransferClass() { DoneLoadingSpreadsheet = false, TableRows = new List<string>() };
TokenSource = new CancellationTokenSource();
var token = TokenSource.Token;
(new TaskFactory()).StartNew(() => BuildSpreadsheet(ViewID, token), token);
}
private void BuildSpreadsheet(string viewID, CancellationToken token)
{
// Simulate work. Update DataTransferObject every 5 seconds, finish after 30 seconds (6 iterations with 5 second wait);
for (int i = 0; i < 6; i++)
{
// Work for 5 seconds
System.Threading.Thread.Sleep(5000);
// Update DataTransferObject with new row (don't use the 'DataTransferObject' property; it relies up the 'ViewID' property, which in
// turn relies upon ViewState, which isn't available from a background thread).
DataTransferDictionary[viewID].TableRows.Add("<tr><td>Val " + i + "</td><td>Val " + (i * 10) + "</td><td>Val " + (i * 100) + "</td></tr>");
}
// All done; update DataTransferObject
DataTransferDictionary[viewID].DoneLoadingSpreadsheet = true;
}
#endregion Background Task
}
}
A couple notes:
Add Json.Net NuGet package (Newtonsoft)
Note that the page class implements the ICallbackEventHandler interface
Edit 2: Updated suggested process at the top to match what I actually did in the code sample.

How to add a bindable property in my user control?

So I created an ASP.NET user control that is a simple wrapper around the bootstrap slider control. Now I want my control to have a public property named Value that I could bind to a property of my model, just like I do with asp:TextBox:
<asp:TextBox runat="server" Text="<%# BindItem.Name %>" />
Can I do the same with my user control?
<uc1:Slider runat="server" Value="<%# BindItem.Age %>" />
So for any future reader, here's a working version of bootstrap-slider's server-side control. You can drag-n-drop it onto your ASPX page. It has two properties Value and Value2 (for ranges). There is an event ValueChanged that will fire when user moves the position of slider. You can attach an event handler to this even just like you do for Button control's Click event. Last but not the least, I ensured that the control works within UpdatePanel too:
N.B. This control doesn't try to include the require JS or CSS file, because if user drops multiple instances of the Slider on the page, the output will probably include a copy of CSS and JS file for each instance, which is undesirable. I couldn't figure out how to link a CSS/JS file only if it hasn't already been included in the current page. So just make sure you include those two files in the parent page once for all.
Markup
<div id="<%= this.UniqueID + "_Div" %>">
<input runat="server" ID="_TextBox"
EnableTheming="false"
class="slider form-control"
style="vertical-align: middle; width: 100%;"
data-slider-min="<%# Min %>"
data-slider-max="<%# Max %>"
data-slider-step="<%# Step %>"
data-slider-value="<%# Value %>"
data-slider-precision="<%# Precision %>"
data-slider-orientation="horizontal"
data-slider-selection="after"
data-slider-tooltip="show"
data-slider-range="<%# IsRange.ToString().ToLower() %>" />
</div>
Code-behind
using System;
using System.ComponentModel;
using System.Web.UI;
namespace YourProjectNameSpace
{
public partial class Slider : System.Web.UI.UserControl
{
public bool IsRange { get; set; }
public float Min { get; set; }
public float Max { get; set; }
public float Step { get; set; }
public int Precision { get; set; }
public event Action ValueChanged;
[Bindable(true, BindingDirection.TwoWay)]
public float Value
{
get
{
if (IsRange)
{
string[] Vals = (this._TextBox.Attributes["value"] ?? "0,0").Split(',');
return float.Parse(Vals[0]);
}
else
return float.Parse((this._TextBox.Attributes["value"] ?? "0"));
}
set
{
if (IsRange)
{
string[] CurVals = (this._TextBox.Attributes["value"] ?? "0,0").Split(',');
this._TextBox.Attributes["value"] = value.ToString() + ',' + CurVals[1];
}
else
this._TextBox.Attributes["value"] = value.ToString();
}
}
[Bindable(true, BindingDirection.TwoWay)]
public float? Value2
{
get
{
if (IsRange)
{
string[] Vals = (this._TextBox.Attributes["value"] ?? "0,0").Split(',');
return float.Parse(Vals[1]);
}
else
return null;
}
set
{
if (IsRange)
{
string[] CurVals = (this._TextBox.Attributes["value"] ?? "0,0").Split(',');
this._TextBox.Attributes["value"] = CurVals[0] + ',' + value.ToString();
}
}
}
protected void Page_Load(object sender, EventArgs e)
{
if (IsPostBack)
{
if (Request["__EVENTTARGET"] == "_TextBox")
{
if (ValueChanged != null)
ValueChanged();
string MyDivName = this.UniqueID.Replace("$", #"\\$") + "_Div";
string SliderVal = Request["__EVENTARGUMENT"];
if (IsRange) SliderVal = '[' + SliderVal.Split(',')[0] + ',' + SliderVal.Split(',')[1] + ']';
Page.ClientScript.RegisterStartupScript(this.GetType(), "CreateSliderFor_" + MyDivName,
"$('#" + MyDivName + " > input').slider().slider('setValue', " + SliderVal +
").on('slideStop', function(slideEvt) { __doPostBack('_TextBox', slideEvt.value); });", true);
}
else
{
string MyDivName = this.UniqueID.Replace("$", #"\\$") + "_Div";
Page.ClientScript.RegisterStartupScript(this.GetType(), "CreateSliderFor_" + MyDivName, "$('#" + MyDivName + " > input').slider().on('slideStop', function(slideEvt) { __doPostBack('_TextBox', slideEvt.value); });", true);
}
}
else
{
string MyDivName = this.UniqueID.Replace("$", #"\\$") + "_Div";
Page.ClientScript.RegisterStartupScript(this.GetType(), "CreateSliderFor_" + MyDivName, "$('#" + MyDivName + " > input').slider().on('slideStop', function(slideEvt) { __doPostBack('_TextBox', slideEvt.value); });", true);
}
}
}
}
Usage
Put this at the top of the page, after #Page line:
<%# Register Src="~/Slider.ascx" TagPrefix="uc1" TagName="Slider" %>
Now place Slider control anywhere in the page:
<uc1:Slider runat="server" ID="Slider1" Min="10" Max="24" Value="<%# BindItem.Age %>" Step="0.1" Precision="1" IsRange="true" OnValueChanged="Slider1_ValueChanged" />
The event handler looks like this:
protected void Slider1_ValueChanged()
{
//do whatever you want with Slider1.Value or Slider1.Value2
}

ASP.NET custom controls - custom property doesn't hold the assigned value on postaback

I have a custom asp-net control that inherits from another one and its works as expected, though the properties are only set properly if i code them in the markup directly, so for instance if i need set a property at runtime that is some dynamic value, this value is never set or somehow lost.
Here's the markup code:
<!--related form-->
<fw:advancedformdisplay id="formDisp" runat="server" captchaenabled="true" EmailEnabled="true" EnableViewState="true" captchaprivatekey="xxxxxxxxxxxxxxxxxxxx" captchapublickey="xxxxxxxxxxxxx" captchatheme="white" SourceType="MenuItem" SourceMainId="Auto">
</fw:advancedformdisplay>
This is the code of the control:
[DefaultProperty("CaptchaEnabled"),ToolboxData("<{0}:AdvancedFormDisplay runat=server></{0}:AdvancedFormDisplay>"), Description("This is an enhanced FormDisplay control that inlcudes Googles Captcha control is enabled")]
public class AdvancedFormDisplay :SiteBuilder.WebControls.FormDisplay
{
bool _CaptchaEnabled = false, sendEmail = false;
string captchaErrorMessage = "The verification code entered is not valid. Please try again!";
RecaptchaControl captchaControl = null;
string captchaPrivateKey = "", captchaPublicKey = "", captchaTheme = "clean";
string originalFormHtml = string.Empty;
string afterText = string.Empty, beforeText = string.Empty;
Literal litHtmlForm = null;
string captchaErrorClass = "errorCaptcha";
public string EmailBeforeText
{
get { return beforeText; }
set { beforeText = value; }
}
public string EmailAfterText
{
get { return afterText; }
set { afterText = value; }
}
public string CaptchaErrorClass
{
get { return captchaErrorClass; }
set { captchaErrorClass = value; }
}
public bool CaptchaEnabled
{
get { return _CaptchaEnabled; }
set { _CaptchaEnabled = value; }
}
public bool EmailEnabled
{
get { return sendEmail; }
set { sendEmail = value; }
}
public string CaptchaErrorMessage
{
get { return captchaErrorMessage; }
set { captchaErrorMessage = value; }
}
/// <summary>
/// red,white,blackglass,clean
/// </summary>
public string CaptchaTheme
{
get { return captchaTheme; }
set { captchaTheme = value; }
}
public string CaptchaPrivateKey
{
get { return captchaPrivateKey; }
set { captchaPrivateKey = value; }
}
public string CaptchaPublicKey
{
get { return captchaPublicKey; }
set { captchaPublicKey = value; }
}
protected override void OnLoad(EventArgs e)
{
base.OnLoad(e);
}
public override void OnSaved(FormDisplayEventArgs e)
{
//If captcha control is enabled we need to adda bit of code to redirect form properly
if (CaptchaEnabled && e.Redirect && !e.SendMail)
{
//Do Stuff
}
if(sendEmail)
{
//Send email
}
base.OnSaved(e);
}
public override void OnSaving(FormDisplayEventArgs e)
{
if (CaptchaEnabled)
{
//Validate and do stuff
}
base.OnSaving(e);
}
}
And then in my asp.net page that is using control, created by markup code, in the Page_Load() i try to assign some values to some properties and and the values aren't set properly, meaning that if i have set for isntance, the property EmailBeforeText = "somthing" this value will not be assigned..
protected void Page_Load(object sender, EventArgs e)
{
//2: Get the language of menuitem - Based on current culture setting (for by dropdownbox - change logic)
try
{
currentCulture = Thread.CurrentThread.CurrentCulture.ToString();
// Redirect if domain does not match rootnode.
DomainChecker.CheckURL(this.Request, this.Response, currentCulture);
if (footerArticle != null)
footerArticle.SourceMenuId = Digimaker.Config.Custom.Get("FooterID_" + currentCulture).ToString();
}
catch
{
currentCulture = "en-GB";
if( footerArticle != null )
footerArticle.SourceMenuId = Digimaker.Config.Custom.Get("FooterID_" + currentCulture).ToString();
}
Any ideas what i'm missing here?
Thanks a lot for your reading!
Regards,
byte_slave
short answer: use viewstate to persist your custom values!
Understanding ASP.NET ViewState whitepaper (see example with NavigateUrl)
edit: as reading the white-paper is obviously a really hard thing:
Each control is responsible for storing its own state, which is
accomplished by adding its changed state to its ViewState property.
The ViewState property is defined in the System.Web.UI.Control class,
meaning that all ASP.NET server controls have this property available.
(When talking about view state in general I'll use lower case letters
with a space between view and state; when discussing the ViewState
property, I'll use the correct casing and code-formatted text.)
If you examine the simple properties of any ASP.NET server control
you'll see that the properties read and write directly to the view
state. (You can view the decompiled source code for a .NET assembly by
using a tool like Reflector.) For example, consider the HyperLink Web
control's NavigateUrl property. The code for this property looks like
so:
public string NavigateUrl
{
get
{
string text = (string) ViewState["NavigateUrl"];
if (text != null)
return text;
else
return string.Empty;
}
set
{
ViewState["NavigateUrl"] = value;
}
}
As this code sample illustrates, whenever a control's property is
read, the control's ViewState is consulted. If there is not an entry
in the ViewState, then the default value for the property is returned.
When the property is assigned, the assigned value is written directly
to the ViewState.

ASP:TextBox Value disappears in postback only when password

I have an asp.net textbox like this:
<asp:TextBox ID="PINPad" runat="server" Columns="6" MaxLength="4"
CssClass="PINTextClass"></asp:TextBox>
It is, as you might have guessed, the text box from an on screen PIN pad. Javascript fills in the values. The page is posted back every five seconds (using an update panel if that matters) to update various other unrelated items on the screen. This works just fine.
However, when I convert it to a password text box, like this:
<asp:TextBox ID="PINPad" runat="server" Columns="6" MaxLength="4"
CssClass="PINTextClass" TextMode="Password"></asp:TextBox>
Then whenever the page posts back, the text box is cleared out on the screen and the textbox is empty (though during the timer event, the value does make it back to the server.)
Any suggestions how to fix this, so that it retains its value during postback?
As a security feature, ASP.NET tries to disallow you from sending the password value back to the client. If you're okay with the security issues (i.e. it's either not really secure information or you're sure that the connection is secure), you can manually set the "value" attribute of the control, rather than using its Text property. It might look something like this:
this.PINPad.Attributes.Add("value", this.PINPad.Text);
protected void Page_Load(object sender, EventArgs e)
{
if (IsPostBack)
{
if (!(String.IsNullOrEmpty(txtPwd.Text.Trim())))
{
txtPwd.Attributes["value"]= txtPwd.Text;
}
if (!(String.IsNullOrEmpty(txtConfirmPwd.Text.Trim())))
{
txtConfirmPwd.Attributes["value"] = txtConfirmPwd.Text;
}
}
}
here is another way to do it:-
using System;
using System.Text;
using System.Web.UI;
using System.Web.UI.WebControls;
namespace WebControlLibrary
{
public class PWDTextBox : TextBox
{
public PWDTextBox()
{
this.TextMode = TextBoxMode.Password;
}
public string Password
{
get
{
string val = (string)ViewState["pwd"];
if (string.IsNullOrEmpty(val))
{
return "";
}
else
{
return val;
}
}
set
{
ViewState["pwd"] = value;
}
}
public override string Text
{
get
{
return Password;
}
set
{
Password = value;
}
}
protected override void OnPreRender(EventArgs e)
{
base.OnPreRender(e);
this.Text = Password;
}
protected override void AddAttributesToRender(HtmlTextWriter writer)
{
base.AddAttributesToRender(writer);
writer.AddAttribute(HtmlTextWriterAttribute.Value, this.Password);
}
}
}
The problem of losing the password in the postback can be avoid making use of Asynchronous JavaScript calls, lets describe a typical scenario for a Login page:
Lets say we have a Login page which allows the user to change the language of its labels when the user choose a language with a dropdownlist
a solution would be to invoke selectedIndexChanged event of the dropdownlist, make a postback which goes to the server and picks up the labels in the chosen language.
in this scenario the field password will be lost due to the security feature of ASP.NET which makes passwords fields not persisted between a postbacks.
This scenario can be solved if the postback is avoided making use of Asynchronous JavaScript Technology and XML (Ajax) calls.
Add a javascript function which will be invoked from the dropdownlist control, in this case this function is assigned to the Command property of the dropdownlist in code behind:
function ValueChanged(div)
{
var table = div.getElementsByTagName("table");
if (table && table.length > 0)
{
var t = table[0].getAttribute('type');
if (t != null && (t == "DropDown"))
{
var inputs = div.getElementsByTagName("input");
if (inputs && inputs.length == 2)
{
{
Translate(inputs[1].value);
}
}
}
}
}
The Translate function takes as parameter the selected option language in the dropdown control and performs the asynchronous call as shown bellow.
function Translate(lang)
{
var request = null;
if (window.XMLHttpRequest)
{
request = new XMLHttpRequest();
if (request.overrideMimeType)
{
request.overrideMimeType('text/xml');
}
}
else if (window.ActiveXObject)
{
request = new ActiveXObject("Msxml2.XMLHTTP");
}
if (request == null)
{
return;
}
var url = "GetLoginTranslations.aspx";
request.open('GET', url +'?lang=' + lang, true);
request.setRequestHeader("Cache-Control", "no-cache");
request.setRequestHeader("Pragma", "no-cache");
request.setRequestHeader("If-Modified-Since", "Sat, 1 Jan 2000 00:00:00 GMT");
request.onreadystatechange = function () { TranslateLabels(request); };
request.send(null);
}
the function Translate shown above performs the call and get the results in the specified .aspx page (in this case "GetLoginTranslations.aspx")
when the request is completed and the request.onreadystatechange is set to the function TranslateLabels this function will be executed.
on this way the postback is not executed as before in the event onSelectedIndexChanged of the dropdownlist control.
the TranslateLabels function would look something like :
function TranslateLabels(request)
{
if (request.readyState == 4)
{
if (request.status == 200)
{
if (request.responseXML)
{
var objRoot = request.responseXML.documentElement;
if (objRoot)
{
if (objRoot.nodeName == "strings")
{
for (var i = 0; i < objRoot.childNodes.length; i++)
{
var node = objRoot.childNodes[i];
var elem;
switch (node.getAttribute("id"))
{
case "lbl_login":
elem = document.getElementById("lbl_login");
if (elem)
elem.innerHTML = node.firstChild.nodeValue;
break;
}
///....
}
}
}
}
}
}
the request.responseXML contains the XML built in the page GetLoginTranslations.aspx and the structure of this XML is defined there.
the Page_Load() event in the GetLoginTranslations.aspx should look like:
protected void Page_Load(object sender, EventArgs e)
{
if (Request["lang"] != null)
strLang = Request["lang"];
//init response
Response.Clear();
Response.Cache.SetExpires(DateTime.Now);
Response.Cache.SetCacheability(HttpCacheability.NoCache);
Response.Cache.SetValidUntilExpires(true);
Response.ContentType = "application/xml";
Response.Charset = "utf-8";
XmlTextWriter xml = new XmlTextWriter(Response.OutputStream, System.Text.Encoding.UTF8)
{
Formatting = Formatting.None
};
xml.WriteStartDocument();
xml.WriteStartElement("strings");
xml.WriteStartElement("string");
xml.WriteAttributeString("id", "lbl_login");
xml.WriteString(GetTranslation("label_login", strLang));
xml.WriteEndElement();
// ... the other labels
xml.WriteEndElement(); //</strings>
xml.Close();
}
Some other considerations:
set the the property AutoPostback of the dropdownlist to false.
Happens both for view-model properties named 'Password' and 'PIN'. You can bypass the behavior by defining those as:
string Password ;
... rather than:
string Password { get; set; }
If you do so, features such the 'LabelFor' macro displaying 'DisplayAttribute.Name' no longer works, so you'd have to define those directly in the HTML.
Or you can simply name the fields something other than 'Password' or 'PIN'.

Hiding a link in asp.net

Duplicate:
Hiding a link in asp.net
Hi
this is the cs file of the masterpage...
using System;
using System.Data;
using System.Configuration;
using System.Collections;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Web.UI.HtmlControls;
namespace LevoContactManagement
{
public partial class Default : System.Web.UI.MasterPage
{
protected void Page_Load(object sender, EventArgs e)
{
BasePage page = (BasePage)Page;
if (page.CurrentUser != null)
{
lblCurrentUser.Text = "<strong>" + page.CurrentUser.FullName + "</strong> - " + page.CurrentUser.CompanyName;
if ((Session["CCFUser"] != null) && (bool.Parse(Session["CCFUser"].ToString()) == true))
{
ctrlLinkBar.AddLink("Issues Management", "AllIssues.aspx");
}
else
{
if (true) ctrlLinkBar.AddLink("Home", "Default.aspx");
if (page.CurrentUser.Permissions.Issues()) ctrlLinkBar.AddLink("Issues Management", "AllIssues.aspx");
if (page.CurrentUser.Permissions.Time()) ctrlLinkBar.AddLink( "Time Management", "TimeEntryForm.aspx");
if (page.CurrentUser.Permissions.Time()) ctrlLinkBar.AddLink("Time Filter", "TimeFilter.aspx");
if (page.CurrentUser.Permissions.SVN() && !(this.Page is _Default)) ctrlLinkBar.AddLink("SVN", "SVN.aspx");
if (true) ctrlLinkBar.AddLink("Profile", "ChangePassword.aspx");
if (page.CurrentUser.Permissions.Administration()) ctrlLinkBar.AddLink( "Administration", "Administration.aspx");
}
}
else lnkLogout.Visible = false;
}
protected void lnkLogout_Click(object sender, EventArgs e)
{
Session.Abandon();
FormsAuthentication.SignOut();
Response.Redirect("Login.aspx");
}
}
}
i need to make the link Time Filter hidden.
the cs file of LinkBar is
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Text;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
namespace WebControlLib
{
[ToolboxData("<{0}:LinkBar runat=server></{0}:LinkBar>")]
public class LinkBar : WebControl
{
struct Link
{
public string Title;
public string URL;
public override string ToString()
{
return "<a href='" + URL + "'>" + Title + "</a>";
}
}
private bool m_bIsVertical = false;
private List<Link> m_Links = new List<Link>();
public bool IsVertical
{
get
{
return m_bIsVertical;
}
set
{
m_bIsVertical = value;
}
}
public void Clear()
{
m_Links.Clear();
}
public void AddLink(string Title, string URL)
{
Link lnk = new Link();
lnk.Title = Title;
lnk.URL = URL;
m_Links.Add(lnk);
}
protected override void RenderContents(HtmlTextWriter output)
{
List<string> items = new List<string>();
foreach (Link lnk in m_Links)
items.Add(lnk.ToString());
string sep = IsVertical ? "</td></tr><tr><td>" : " | ";
output.Write(
#"
<table width='100%' class='linkBar'>
<tr>
<td>" + string.Join(sep, items.ToArray()) + #"</td>
</tr>
</table>
");
}
}
}
how do i go about it? i changed the master.designer.cs file as follows-->
public partial class Default {
protected System.Web.UI.HtmlControls.HtmlForm form1;
protected System.Web.UI.WebControls.Label lblCurrentUser;
protected System.Web.UI.WebControls.LinkButton lnkLogout;
public WebControlLib.LinkBar ctrlLinkBar;
public System.Web.UI.WebControls.ContentPlaceHolder LeftNav;
protected System.Web.UI.WebControls.ContentPlaceHolder ContentPlaceHolder1;
protected System.Web.UI.WebControls.ContentPlaceHolder BodyContent;
}
but the link still does not appear on the Design view of the masterpage, hence i cant find the id, therefore i cant hide it. What is an alternative to this?
I assume that you're talking about hiding the link to TimeEntryForm.aspx, and that you probably want to do this in only limited circumstances (which is why you don't want to just omit the line).
The link isn't actually in itself a control, so it won't have its own ID. It's a member of the List of links that belongs to the LinkBar control, and the LinkBar takes care of rendering them to the screen.
As you're adding these links to the LinkBar at run time, they won't display in the design view preview in Visual Studio - it will only display when you view the page in a browser.
I'd suggest that you get rid of the LinkBar, and just add the controls to the page as simple HyperLink controls. If you like, do this in the designer. Then you can set the visibility of each link in the code behind using the Visible property on those hyperlinks, like such:
hlTimeLink.Visible = page.CurrentUser.Permissions.Time();

Resources