I'm having problems with a custom control (which inherits from a user control) - my LoadControlState isn't getting fired.
Well, to be precise: it gets fired normally, but when i override the page's LoadPageStateFromPersistenceMedium and SavePageStateToPersistenceMedium functions, it no longer gets fired.
Are there any typical reasons for the LoadControlState not getting fired that i should look into? Are there any preconditions for when it does get fired?
Thanks
For what it's worth, here's how i'm overriding the Save/LoadPageStateFromPersistenceMedium functions. Basically, it stores the viewstate in the users session, to make the postbacks faster:
// Inspired by: http://aspalliance.com/72
const string ViewStateFieldName = "__VIEWSTATEKEY";
const string ViewStateKeyPrefix = "ViewState_";
const string RecentViewStateQueue = "ViewStateQueue";
const int RecentViewStateQueueMaxLength = 5;
protected override object LoadPageStateFromPersistenceMedium()
{
// The cache key for this viewstate is stored in a hidden field, so grab it
string viewStateKey = Request.Form[ViewStateFieldName] as string;
if (viewStateKey == null) return null;
// Grab the viewstate data using the key to look it up
return Session[viewStateKey];
}
protected override void SavePageStateToPersistenceMedium(object viewState)
{
// Give this viewstate a random key
string viewStateKey = ViewStateKeyPrefix + Guid.NewGuid().ToString();
// Store the viewstate
Session[viewStateKey] = viewState;
// Store the viewstate's key in a hidden field, so on postback we can grab it from the cache
ClientScript.RegisterHiddenField(ViewStateFieldName, viewStateKey);
// Some tidying up: keep track of the X most recent viewstates for this user, and remove old ones
var recent = Session[RecentViewStateQueue] as Queue<string>;
if (recent == null) Session[RecentViewStateQueue] = recent = new Queue<string>();
recent.Enqueue(viewStateKey); // Add this new one so it'll get removed later
while (recent.Count > RecentViewStateQueueMaxLength) // If we've got lots in the queue, remove the old ones
Session.Remove(recent.Dequeue());
}
Since .NET 2.0, it is recommended to put your state persistent logic in a custom class derived from PageStatePersister. So you may try taking that approach.
What are you returning from your LoadPageStateFromPersistenceMedium method implementation? It should probably be an instance of the System.Web.UI.Pair initialized with both the ViewState and the ControlState data:
return new Pair([Restored ControlState], [Restored ViewState]);
The following code fixed it:
PageStatePersister pageStatePersister;
protected override PageStatePersister PageStatePersister
{
get
{
// Unlike as exemplified in the MSDN docs, we cannot simply return a new PageStatePersister
// every call to this property, as it causes problems
return pageStatePersister ?? (pageStatePersister = new SessionPageStatePersister(this));
}
}
Related
I created an extension for CRCaseMaint, and added the event CRCase_RowSelecting. Here is the code I am currently using:
protected virtual void CRCase_RowSelecting(PXCache sender, PXRowSelectingEventArgs e)
{
CRCase row = e.Row as CRCase;
if (row == null) return;
PXDatabase.ResetSlot<List<CRCase>>("OriginalCase");
List<CRCase> originalCaseSlot = PXDatabase.GetSlot<List<CRCase>>("OriginalCase");
if (originalCaseSlot.Count == 0)
{
originalCaseSlot.Add(sender.CreateCopy(row) as CRCase);
}
else
{
originalCaseSlot[0] = sender.CreateCopy(row) as CRCase;
}
}
When I first open a case, this event will fire a couple times, and the last time it fires, the current case is correctly stored in e.Row, so this code works great. When I click Save, I have a RowPersisting event that compares the case stored in the originalCaseSlot with the updated case. At the end, it sets the original case slot to the updated case. This also works well.
However, when I make another change without leaving the case, and click save, e.Row on the RowSelecting event now has the next case stored on it rather than the current case. Since I am not touching the next case in any way, I am surprised that this is happening.
My question is, should I be using a different event instead of RowSelecting, or is there something else I am missing?
Thank you all for your help.
Sometimes when the primary record gets updated or the user clicks on a form toolbar button, the framework selects 2 records from database: the current primary record and the next one. This is why RowSelecting is invoked 2nd time for the next CRCase record.
Honestly, using PXDatabase Slots to store user session-specific records is not a good idea. PXDatabase Slots are shared among all user sessions and should only be used to cache frequently used data from database, which is not prone to frequent updates. This makes the main purpose of PXDatabase Slots to reduce number of database queries to widely and very often used configurable data, like Segment Key or Attribute configurations.
With that said, using the RowSelecting handler is definitely a step in the right direction. Besides, the RowSelecting handler, you should additionally define a separate PrevVersionCase data view to store the original CRCase record(s) and also override the Persist method to report about changes. The Locate method used on PXCache objects searches the cache for a data record that has the same key fields as the provided data record. This approach allows to compare changes between the originally cached and modified CRCase records having identical key field values.
public class CRCaseMaintExt : PXGraphExtension<CRCaseMaint>
{
[Serializable]
public class CRPrevVersionCase : CRCase
{ }
public PXSelect<CRPrevVersionCase> PrevVersionCase;
protected virtual void CRCase_RowSelecting(PXCache sender, PXRowSelectingEventArgs e)
{
CRCase row = e.Row as CRCase;
if (row == null || e.IsReadOnly) return;
var versionCase = new CRPrevVersionCase();
var versionCache = PrevVersionCase.Cache;
sender.RestoreCopy(versionCase, row);
if (versionCache.Locate(versionCase) == null)
{
versionCache.SetStatus(versionCase, PXEntryStatus.Held);
}
}
[PXOverride]
public void Persist(Action del)
{
var origCase = Base.Case.Current;
var origCache = Base.Case.Cache;
CRPrevVersionCase versionCase;
if (origCache.GetStatus(origCase) == PXEntryStatus.Updated)
{
versionCase = new CRPrevVersionCase();
origCache.RestoreCopy(versionCase, origCase);
versionCase = PrevVersionCase.Cache.Locate(versionCase) as CRPrevVersionCase;
if (versionCase != null)
{
foreach (var field in Base.Case.Cache.Fields)
{
if (!Base.Case.Cache.FieldValueEqual(origCase, versionCase, field))
{
PXTrace.WriteInformation(string.Format(
"Field {0} was updated", field));
}
}
}
}
del();
if (origCase != null)
{
PrevVersionCase.Cache.Clear();
versionCase = new CRPrevVersionCase();
Base.Case.Cache.RestoreCopy(versionCase, origCase);
PrevVersionCase.Cache.SetStatus(versionCase, PXEntryStatus.Held);
}
}
}
public static class PXCacheExtMethods
{
public static bool FieldValueEqual(this PXCache cache,
object a, object b, string fieldName)
{
return Equals(cache.GetValue(a, fieldName), cache.GetValue(b, fieldName));
}
}
I am working on a web app;ication based on asp.net with c#,I have two methods specified below.
public partial class ClerkReception_CreateRecords : System.Web.UI.Page
{
string patid;
protected void ddryear_textchanged(object sender, EventArgs e)
{
string month = "";
if (ddrmonth.SelectedItem.Text == "Jan")
{
month = "01";
}
else if (ddrmonth.SelectedItem.Text == "Feb")
{
month = "02";
}
else if (ddrmonth.SelectedItem.Text == "Mar")
{
month = "03";
}
string year;
year = ddryear.SelectedItem.Text;
string locid = Session["Location"].ToString();
patid = locid + month + year;//Ex:AT112013
myConnection obj = new myConnection();
//string result = obj.fnDisplayManualRecords(year, month, locid);
string result = obj.fnDisplayManualRecords1(patid);
txtlast.Text = result.ToString();
if (ddrmonth.SelectedItem.Text != null || ddryear.SelectedItem.Text != null)
{
txtlast.Visible = true;
lbllast.Visible = true;
BtnProceed.Visible = true;
}
}
This is a method used when a item is selected from dropdownlist,where the patid returns the value.
I need to access the same value of patid inside a another method shown below,Hence I declared the patid as global variable so that I can access the value in any method.But its giving null.How to retieve the vale from one method to another method?
protected void BtnProceed_Click(object sender, EventArgs e)
{
string x = patid;//shows null
using (SqlConnection cn = new SqlConnection(ConfigurationManager.ConnectionStrings["ConStr"].ConnectionString))
{
using (SqlCommand cmd = new SqlCommand("select top 1(SUBSTRING(patientid,9,4)) as MaxpatientID from Patient_Data where PatientID like '"+patid+"%' order by PatientID desc;", cn))
{
try
{
cn.Open();
using (SqlDataReader rdr = cmd.ExecuteReader())
{
//int Patcount;
if (rdr.Read())
{
int Patcount = int.Parse(rdr["MaxpatientID"].ToString());
// if(Patcount == 0)
}
}
}
catch (Exception ex)
{
// handle errors here
}
}
}
}
}
The global variables are created / intialized between postback in asp.net and they do not retain the values between postback as http is stateless protocol, you need to use ViewState for that. You can read more about ViewState and Stateless protocol over here.
To set value in ViewState
ViewState["patid"] = locid + month + year;//Ex:AT112013;
To get value from ViewState
string patid = ViewState["patid"].ToString();
View State
View state's purpose in life is simple: it's there to persist state
across postbacks. (For an ASP.NET Web page, its state is the property
values of the controls that make up its control hierarchy.) This begs
the question, "What sort of state needs to be persisted?" To answer
that question, let's start by looking at what state doesn't need to be
persisted across postbacks. Recall that in the instantiation stage of
the page life cycle, the control hierarchy is created and those
properties that are specified in the declarative syntax are assigned.
Since these declarative properties are automatically reassigned on
each postback when the control hierarchy is constructed, there's no
need to store these property values in the view state. You can read
more about viewstate here.
Welcome to the world of post backs, each post back recreates the page (class) variables, so you need to save it before post back or it will be gone.
Use a cache object, such as Session to maintain values between post back and page navigation. Session gives you the power to store and retrieve objects across multiple pages in your application, including just one if you are continually posting back to it.
You can use Session, like this:
Storing value in Session:
Session["ValueToKeep"] = "My important information";
Retrieving value from Session:
// Make sure it is in session cache before we try to get it
if(Session["ValueToKeep"] != null)
{
string valueINeed = Session["ValueToKeep"].ToString();
}
Note: All items stored in Session are Objects thus the usage of .ToString() on the Session item. An item is boxed as an object when inserted into Session, but must be unboxed (cast) when retrieved.
You class level variables are re-created on postback. You will need to persist them somewhere that continues across requests.. such as ViewState, Session, etc.
The best way to interact with two methods / functions / event-function in side class is just declaring its accessible modifiers to public and you can call any object of that class after initialize some value to it.
public void ddryear_textchanged(object sender, EventArgs e) {....}
public void BtnProceed_Click(object sender, EventArgs e) {....}
create one variable inside class like string x; and initialize it in constrictor { x="some text "; } this is how code works...
There are many ways to get the value of global parameter,
one way is to define the parameter as static
Each time when pressing tree node then selectegNodeChanged event triggered and page is reloaded.So, i lost some data stored in Dictionary,ArrayList...How to prevent those data losing?
So, I stored those Dictionary and ArrayList as "static".It is now resolved my problem.
Is it good way to do that?
No. Do not use static. Try to store these in ViewState or Session instead.
You can consider ViewState if it is not a large amount of data.
static will be accessible across Session and is not a good practice.
You can create properties and avoid code duplication like shown below.
public ArrayList TreeNodeDataList
{
set
{
ViewState["TreeNodeDataList"] = value;
}
get
{
if (ViewState["TreeNodeDataList"] == null)
{
return (new ArrayList());
}
else
{
return (ViewState["TreeNodeDataList"] as ArrayList);
}
}
}
Now, when you want to re-assign data, read TreeNodeDataList property. If count of that ArrayList is 0, fetch from DB, else use it.
Hope I am clear enough.
I have a parent control that has an instance of a HiddenField child control. I am using CreateChildControls() to add it. Everything works client side including the values being added to the field. However, on postback, the reference to the field is null
here is the code
protected override void CreateChildControls()
{
assignedListField = new HiddenField();
assignedListField.ID = ClientID + "_HiddenAssignedList";
assignedListField.EnableViewState = true;
Controls.Add(assignedListField);
base.CreateChildControls();
}
public IList<DlpItem> GetAssignedItems()
{
//assignedListField = FindControl(ClientID + "_HiddenUnassignedList") as HiddenField;
var TmpAssignedItems = new List<DlpItem>();
var list = assignedListField.Value;
var items = list.Split(new string[] { "#" }, StringSplitOptions.RemoveEmptyEntries);
foreach (var item in items)
{
var mix = item.Split(new string[] { "," }, StringSplitOptions.RemoveEmptyEntries);
var text = mix[0];
var id = int.Parse(mix[1]);
TmpAssignedItems.Add(new DlpItem(text, id));
}
return TmpAssignedItems;
}
I have tried simply relying on the ViewState ... then also attempted using FindControl(). Neither works, it comes up as a null reference ... any input on what is going here?
As #Sebastian said, if you need to use any of the controls, they may be null because they are not available. However, you can call EnsureChildControls to create the control collection and ensure its there. This does not include the loading of ViewState.
However, you can't rely on viewstate if you are having client-side operations affect the data. What you need to do is have your control implement IPostBackDataHandler. In LostPostData, there you need to check for the hidden variable. Using postCollection[ClientID + "_HiddenAssignedList"], you can get the string value posted to the server, and process the results.
HTH.
It is most likely that the CreateChildControls() has not yet been called when the ViewState is loaded (probably in your case ControlState is more usefull).
See http://msdn.microsoft.com/en-us/library/aa719775%28vs.71%29.aspx for the execution lifecycle.
You could store (and load) the state using the LoadViewState (SaveViewState)-method ( http://msdn.microsoft.com/de-de/library/system.web.ui.control.loadviewstate.aspx ) and store the values in fields 'till CreateChildControls() gets called.
Edit at bottom with solution
I've seen a similar question to this posted before and have tried the suggestions, but I must be missing something. My basic problem is this: I have a select box where the user can select a filter which may or may not have constraints built into it (requires the user to input further data so the filter knows how to filter). Since it's unknown just how many constraints will exist for the filter, I'm trying to load them in dynamically and add them to a placeholder panel that I have. The correct number of constraints load just fine and dandy, but when the user inputs text and hits submit, after the page reloads none of the values persist. Here's the appropriate code (I can provide more if needed):
I have these as class variables for my Web Part:
Panel constraintPanel;
HtmlInputText[] constraints;
Label[] constraintLabels = null;
Inside an override CreateChildControls I initialize the panel:
constraintPanel = new Panel();
I build in the dynamic input boxes in an overridden OnPreRender (Note: I've heard people say to do it in OnInit, OnLoad, or OnPreRender, but OnPreRender is the only one that doesn't make the whole Web Part error out):
protected override void OnPreRender(EventArgs e)
{
buildConstraints();
base.OnPreRender(e);
}
private void buildConstraints()
{
if (!viewSelect.SelectedValue.Equals(INConstants.NoFilterOption))
{
string[,] constraintList = docManager.GetFilterConstraints(viewFilterSelect.SelectedValue);
if (constraintList != null)
{
this.constraints = new HtmlInputText[constraintList.Length / 2];
this.constraintLabels = new Label[constraintList.Length / 2];
for (int constraintCount = 0; constraintCount < constraintList.Length / 2; constraintCount++)
{
Label constraintLabel = new Label();
constraintPanel.Controls.Add(constraintLabel);
constraintLabel.Text = constraintList[constraintCount, 0];
this.constraintLabels[constraintCount] = constraintLabel;
HtmlInputText constraint = new HtmlInputText();
constraintPanel.Controls.Add(constraint);
constraint.ID = "constraint_" + constraintCount;
constraint.MaxLength = 12;
constraint.Style.Add("FONT-FAMILY", "Verdana");
constraint.Style.Add("FONT-SIZE", "11");
this.constraints[constraintCount] = constraint;
}
}
}
}
And then finally inside an overridden RenderWebParts I have (note: I've also tried looping through the arrays constraints and constraintLabels to render the controls, but it made no difference):
...
constraintPanel.RenderBeginTag(output); // not sure if this is needed
if (constraints != null && constraints.Length > 0)
{
foreach (Control tempControl in constraintPanel.Controls)
{
if (tempControl is Label)
{
output.WriteLine("<tr>");
output.WriteLine("<td width='2%' nowrap><font class='search-header'>");
tempControl.RenderControl(output);
output.WriteLine(" ");
}
else if (tempControl is HtmlInputText)
{
tempControl.RenderControl(output);
output.WriteLine("</td>");
output.WriteLine("<td width='*' nowrap></td>");
output.WriteLine("</tr>");
}
}
}
constraintPanel.RenderEndTag(output); // not sure if this is needed
...
I appreciate any help, as this is truly driving me crazy.
Edit with solution:
I've been able to get it working. I needed to override the OnLoad event and wrap my calls from there in a try-catch block. For some reason the initial page load throws an exception when trying to run, which causes the entire page to not display. I also forgot to add my constraintPanel to the Controls list.
Here's the code in OnLoad for information's sake:
protected override void OnLoad(EventArgs e)
{
base.OnLoad(e);
try
{
viewsBuildConstraints();
}
catch (Exception)
{
}
}
Try marking your webpart with the INamingContainer interface and make sure to give all controls an ID. Furthermore, HtmlInput COntrols do not have a viewstate i believe, which would cause them to "forget" the input after a postback. Could you try using actual TextBox controls?