How to persist search criteria and results of a c# .NET application page - asp.net

Scenario:
User submits search criteria and selects an item from search results on the same page, which navigates to a new page of details for the selected item.
When the User returns to the search screen, the search criteria & results (including selected page and sort-order) should be preserved from their last visit.
Related information:
All form submissions are POSTs.
Navigation back to the search screen may not be available from last browser history (e.g. more than one details screen may be encountered, or the user may navigate directly to the search screen from an alternative menu.)
Search results are provided using Telerik RadGrid control.
I'm looking for a generic solution that will be able to be applied to different search screens.
In some instances, the item may be DELETED from within the details screen, and should therefore not appear in the search results when the screen is next encountered.
Thoughts:
I've read a lot of suggested methods for addressing various parts of this scenario, but I'm still confused; no comprehensively "correct" solution jumps to the forefront.
I guess I'm asking for recommendations/approach rather than a whole solution spelled out for me (although that would be nice! ;-)
The .NET VIEWSTATE would seem to do exactly what I'm after (with the exception of #5) - Is there some way of leveraging off this so that viewstate can be used between pages, and not just between postbacks to the same page? (e.g. can I store/restore viewstate to/from a session variable or something? I haven't seen this suggested anywhere and I'm wondering if there's a reason why.)
Thanks in advance.

Thanks for all the advice.
For the benefit of others, here is a solution to this issue (no doubt there's room for improvement, but this works satisfactorily for the moment).
4 functions...
StoreSearchCookie - Persist the state/values of a panel of search criteria and a results grid in a cookie with a specified UID.
RestoreSearchCookie_Criteria - Read the cookie and re-populate the search criteira
RestoreSearchCookie_Results - Read the cookie and restore the grid state.
FindFormControls - Helper method to recursively find form-input controls in a container (pinched & modified from elsewhere on Stack Overflow)
NB...
I haven't addressed the multiple-tabs issue because our application doesn't allow them anyway.
RestoreSearchResults utilises GridSettingsPersister.cs available from Telerik website, (but I had to modify it to store the page number as well)
Usage is as follows...
protected void Page_PreRender(object sender, EventArgs e)
{
if (Page.IsPostBack)
{
// Store the state of the page
StoreSearchCookie("SomeSearchPage", pnlSearchCriteria, gridResults);
}
else
{
// Restore search criteria
RestoreSearchCookie_Criteria("SomeSearchPage");
// Re-invoke the search here
DoSearch(); // (for example)
// Restore the grid state
RestoreSearchCookie_Results("SomeSearchPage");
}
}
Code follows...
protected void StoreSearchCookie(string cookieName, Panel SearchPanel, RadGrid ResultsGrid)
{
try
{
HttpCookie cookieCriteria = new HttpCookie("StoredSearchCriteria_" + cookieName);
// 1. Store the search criteria
//
List<Control> controls = new List<Control>();
FindFormControls(controls, SearchPanel);
foreach (Control control in controls)
{
string id = control.ID;
string parentId = control.Parent.ID;
string uid = string.Format("{0}>{1}", parentId, id);
string value = "";
Type type = control.GetType();
bool isValidType = true; // Optimistic!
if (type == typeof(TextBox))
{
value = ((TextBox)control).Text;
}
else if (type == typeof(DropDownList))
{
value = ((DropDownList)control).SelectedValue;
}
else if (type == typeof(HiddenField))
{
value = ((HiddenField)control).Value;
}
else if (type == typeof(RadioButton))
{
value = ((RadioButton)control).Checked.ToString();
}
else if (type == typeof(CheckBox))
{
value = ((CheckBox)control).Checked.ToString();
}
else
{
isValidType = false;
}
if (isValidType)
{
cookieCriteria.Values[uid] = value;
}
}
cookieCriteria.Expires = DateTime.Now.AddDays(1d);
Response.Cookies.Add(cookieCriteria);
// 2. Persist the grid settings
//
GridSettingsPersister SavePersister = new GridSettingsPersister(ResultsGrid);
HttpCookie cookieResults = new HttpCookie("StoredSearchResults_" + cookieName);
cookieResults.Values["GridId"] = ResultsGrid.ID;
cookieResults.Values["GridSettings"] = SavePersister.SaveSettings();
cookieResults.Expires = DateTime.Now.AddDays(1d);
Response.Cookies.Add(cookieResults);
}
catch (Exception exception)
{
Logger.Write(exception);
}
}
protected void RestoreSearchCookie_Criteria(string cookieName)
{
try
{
HttpCookie cookieCriteria = Request.Cookies["StoredSearchCriteria_" + cookieName];
if (cookieCriteria != null)
{
foreach (string key in cookieCriteria.Values.AllKeys)
{
string value = cookieCriteria[key];
string[] ids = key.Split('>');
string parentId = ids[0];
string id = ids[1];
Control control = FindControl(parentId).FindControl(id);
Type type = control.GetType();
if (type == typeof(TextBox))
{
((TextBox)control).Text = value;
}
else if (type == typeof(DropDownList))
{
((DropDownList)control).SelectByValue(value);
}
else if (type == typeof(HiddenField))
{
((HiddenField)control).Value = value;
}
else if (type == typeof(RadioButton))
{
((RadioButton)control).Checked = Boolean.Parse(value);
}
else if (type == typeof(CheckBox))
{
((CheckBox)control).Checked = Boolean.Parse(value);
}
}
}
}
catch (Exception exception)
{
Logger.Write(exception);
}
}
protected void RestoreSearchCookie_Results(string cookieName)
{
try
{
HttpCookie cookieResults = Request.Cookies["StoredSearchResults_" + cookieName];
if (cookieResults != null)
{
string gridId = cookieResults.Values["GridId"];
string settings = cookieResults.Values["GridSettings"];
RadGrid grid = (RadGrid)FindControl(gridId);
GridSettingsPersister LoadPersister = new GridSettingsPersister(grid);
LoadPersister.LoadSettings(settings);
grid.Rebind();
}
}
catch (Exception exception)
{
Logger.Write(exception);
}
}
private void FindFormControls(List<Control> foundSofar, Control parent)
{
List<Type> types = new List<Type> { typeof(TextBox), typeof(DropDownList), typeof(RadioButton), typeof(CheckBox), typeof(HiddenField) };
foreach (Control control in parent.Controls)
{
if (types.Any(item => item == control.GetType()))
{
foundSofar.Add(control);
}
if (control.Controls.Count > 0)
{
this.FindFormControls(foundSofar, control); // Use recursion to find all descendants.
}
}
}

Related

Nested subscription to messages on xamarin forms

I'm new with Xamarin forms and don't know how to deal with this case. I've tryed to implement it in several ways but with no success.
I have a page where when user makes an action (write a text on a text box and send it with enter key) my app must make some checkings. Depending on the result of the checks, it could be necessary to show a modal page with a list of item to select. Ones user makes the selection process must continue with other checks. And here is my problem, because in this next checkings I have to show another page. User must make a selection/enter some date, and then continue to complete the proccess, but this page is not appear.
I'm using the messagingCenter to subscribe to the modal pages. First modal page appears and makes the selection well. Second modal page is never shown and then proccess never complets.
Here is some of my code:
NavigationPage navigationPage = new NavigationPage(new ListItemsPage(products));
Navigation.PushModalAsync(navigationPage);
MessagingCenter.Subscribe<ListItemsPage, Guid?>(this, "Select product", (obj, item) =>
{
try
{
if (item != null)
{
product = products.SingleOrDefault(x => x.Guid == item);
if (product != null) ProcessLine(product);
}
}
catch(Exception ex)
{
throw ex;
}
finally
{
MessagingCenter.Unsubscribe<ListItemsPage, Guid?>(this, "Select product");
}
});
On ListItemsPage I have this code whe item is selected:
private void MenuItem_Clicked(object sender, EventArgs e)
{
// some logic...
Navigation.PopModalAsync();
MessagingCenter.Send(this, "Select product", SelectedGuid);
}
SelectedGuid is a Guid type data and when debbugin is well selected.
Problems comes when goes to ProcessLine method.
private void ProcessLine(Product product) {
// make some logic...
NavigationPage navigationPage = new NavigationPage(new ControlUnitsPage(model));
Navigation.PushModalAsync(navigationPage);
MessagingCenter.Subscribe<ControlUnitsPage, ControlUnits>(this, "Select units, date and lot code", (obj, item) =>
{
try
{
if (item != null)
{
_date = item.Date;
_code = item.Code;
_units = item.Units;
Save(productLine, product, _units, _date,_code);
}
}
catch(Exception ex)
{
throw ex;
}
finally
{
MessagingCenter.Unsubscribe<ControlUnitsPage, ControlUnits>(this, "Select units, date and lot code");
}
});
}
ControlUnitsPage has the same structure as the last one page. First makes a PopModalAsync and then sends the message sending an instance of ControlUnits type.
private void Button_Clicked(object sender, EventArgs e)
{
//some logic...
Item = new ControlUnits() { Date = DateField.Date, Code = CodeField.Text, Units = int.Parse(SelectedUnits.Value.ToString()) };
Navigation.PopModalAsync();
MessagingCenter.Send(this, "Select units, date and lot code", Item);
}
I think problem is in the order of invoking method but dont know what is the properly order because I am not able to understand how pushmodal, popmodal methods work, whether or not I should use await with it if after that comes a subscription. I really don't know and I need help, please.
Thank you so much!
your Send and Subscribe calls both need to use matching parameters
if Subscribe looks like this
MessagingCenter.Subscribe<ControlUnitsPage, ControlUnits>(this, "Select units, date and lot code", (obj, item) => ... );
then Send needs to match
MessagingCenter.Send<ControlUnitsPage, ControlUnits>(this, "Select units, date and lot code", Item);

Sitecore: Show input option after using menu context item

I've added a menu context item to the TreelistEx. This menu item sends a messages that I later catch in a HandleMessage method.
In this method i create a new item ( template type and parent item are given in the source of the treelist field ).
All i need now is a way to ask the user for a name. But i haven't been able to find a simple way to do this.
class MyTreeListEx : TreelistEx, IMessageHandler
{
void IMessageHandler.HandleMessage(Message message)
{
if (message == null)
{ return; }
if (message["id"] == null)
{ return; }
if (!message["id"].Equals(ID))
{ return; }
switch (message.Name)
{
case "treelist:edit":
// call default treelist code
case "mytreelistex:add":
// my own code to create a new item
}
}
}
Does anyone have any suggestions on how to achieve this ?
Edit: added image & code + i'm using Sitecore 8 Update 1
I don't know which version of Sitecore you use but what you can try is SheerResponse.Input method.
You can use it like this:
using Sitecore.Configuration;
using Sitecore.Globalization;
using Sitecore.Shell.Applications.ContentEditor.FieldTypes;
using Sitecore.Web.UI.Sheer;
void IMessageHandler.HandleMessage(Message message)
{
...
case "mytreelistex:add":
Sitecore.Context.ClientPage.Start(this, "AddItem");
break;
}
protected static void AddItem(ClientPipelineArgs args)
{
if (args.IsPostBack)
{
if (!args.HasResult)
return;
string newItemName = args.Result;
// create new item here
// if you need refresh the page:
//SheerResponse.Eval("scForm.browser.getParentWindow(scForm.browser.getFrameElement(window).ownerDocument).location.reload(true)");
}
else
{
SheerResponse.Input("Enter the name of the new item:", "New Item Default Name", Settings.ItemNameValidation,
Translate.Text("'$Input' is not a valid name."), Settings.MaxItemNameLength);
args.WaitForPostBack();
}
}
This code will even validate your new item name for incorrect characters and length.

ASPxComboBox Filtering Large DataSet Using XpoDataSource (DevExpress)

I tried to Filter an ASPxComboBox that gets data using an XpoDataSource, note that restoring and filtering data from a small data set works fine , the issue start when I try to filter large dataset - about 70000 records- from datasource the ComboBox loading becomes very slow since XpoDataSource gets all data from database table .
So I created a criteria for the XpoDataSource to reduce number of records restored ,then the ComboBox Keeps repeating the top 10 records while scrolling down the ComboBox, I don't know where the problem is.
I realized that what I need is similar to the example in the following link
But using an XpoDataSource instead of SqlDataSource1 .
I don't know how to write a similar code for an XpoDataSource .
this is my code :
protected void cmbServices_OnItemRequestedByValue_SQL(object source, DevExpress.Web.ASPxEditors.ListEditItemRequestedByValueEventArgs e)
{
try
{
string criteria = "";
if (string.IsNullOrEmpty(e.Value.ToString()) || e.Value.ToString().Length < 3)
{
criteria = "1 = 2";
}
else
{
criteria =
string.Format("(( Code like '{0}%' OR ProductName like '{0}%') AND CustomerId = {1})", e.Value.ToString(), (cmbServicesActivities != null && cmbServicesActivities.Value != null) ? cmbServicesActivities.Value.ToString() : "0");
}
dsServices.Session = LookupsSession;
dsServices.Criteria = criteria;
cmbServicesDescription.DataSource = dsServices;
cmbServicesDescription.DataBind();
}
catch (Exception exc)
{
Debug.WriteLine(exc.Message);
}
}
The Following Example demonstrates the answer of my question
public partial class _Default : System.Web.UI.Page {
Session session = XpoHelper.GetNewSession();
protected void cmb_ItemRequestedByValue(object source, DevExpress.Web.ASPxEditors.ListEditItemRequestedByValueEventArgs e) {
MyObject obj = session.GetObjectByKey<MyObject>(e.Value);
if (obj != null) {
cmb.DataSource = new MyObject[] { obj };
cmb.DataBindItems();
}
}
protected void cmb_ItemsRequestedByFilterCondition(object source, DevExpress.Web.ASPxEditors.ListEditItemsRequestedByFilterConditionEventArgs e) {
XPCollection<MyObject> collection = new XPCollection<MyObject>(session);
collection.SkipReturnedObjects = e.BeginIndex;
collection.TopReturnedObjects = e.EndIndex - e.BeginIndex + 1;
collection.Criteria = new BinaryOperator("Title", String.Format("%{0}%", e.Filter), BinaryOperatorType.Like);
collection.Sorting.Add(new SortProperty("Oid", DevExpress.Xpo.DB.SortingDirection.Ascending));
cmb.DataSource = collection;
cmb.DataBindItems();
}

Better way to find control in ASP.NET

I have a complex asp.net form,having even 50 to 60 fields in one form like there is Multiview, inside MultiView I have a GridView, and inside GridView I have several CheckBoxes.
Currently I am using chaining of the FindControl() method and retrieving the child ID.
Now, my question is that is there any other way/solution to find the nested control in ASP.NET.
If you're looking for a specific type of control you could use a recursive loop like this one -
http://weblogs.asp.net/eporter/archive/2007/02/24/asp-net-findcontrol-recursive-with-generics.aspx
Here's an example I made that returns all controls of the given type
/// <summary>
/// Finds all controls of type T stores them in FoundControls
/// </summary>
/// <typeparam name="T"></typeparam>
private class ControlFinder<T> where T : Control
{
private readonly List<T> _foundControls = new List<T>();
public IEnumerable<T> FoundControls
{
get { return _foundControls; }
}
public void FindChildControlsRecursive(Control control)
{
foreach (Control childControl in control.Controls)
{
if (childControl.GetType() == typeof(T))
{
_foundControls.Add((T)childControl);
}
else
{
FindChildControlsRecursive(childControl);
}
}
}
}
Late as usual. If anyone is still interested in this there are a number of related SO questions and answers. My version of recursive extension method for resolving this:
public static IEnumerable<T> FindControlsOfType<T>(this Control parent)
where T : Control
{
foreach (Control child in parent.Controls)
{
if (child is T)
{
yield return (T)child;
}
else if (child.Controls.Count > 0)
{
foreach (T grandChild in child.FindControlsOfType<T>())
{
yield return grandChild;
}
}
}
}
All the highlighted solutions are using recursion (which is performance costly). Here is cleaner way without recursion:
public T GetControlByType<T>(Control root, Func<T, bool> predicate = null) where T : Control
{
if (root == null) {
throw new ArgumentNullException("root");
}
var stack = new Stack<Control>(new Control[] { root });
while (stack.Count > 0) {
var control = stack.Pop();
T match = control as T;
if (match != null && (predicate == null || predicate(match))) {
return match;
}
foreach (Control childControl in control.Controls) {
stack.Push(childControl);
}
}
return default(T);
}
FindControl does not search within nested controls recursively. It does only find controls that's NamigContainer is the Control on that you are calling FindControl.
Theres a reason that ASP.Net does not look into your nested controls recursively by default:
Performance
Avoiding errors
Reusability
Consider you want to encapsulate your GridViews, Formviews, UserControls etc. inside of other UserControls for reusability reasons. If you would have implemented all logic in your page and accessed these controls with recursive loops, it'll very difficult to refactor that. If you have implemented your logic and access methods via the event-handlers(f.e. RowDataBound of GridView), it'll be much simpler and less error-prone.
Action Management On Controls
Create below class in base class.
Class To get all controls:
public static class ControlExtensions
{
public static IEnumerable<T> GetAllControlsOfType<T>(this Control parent) where T : Control
{
var result = new List<T>();
foreach (Control control in parent.Controls)
{
if (control is T)
{
result.Add((T)control);
}
if (control.HasControls())
{
result.AddRange(control.GetAllControlsOfType<T>());
}
}
return result;
}
}
From Database:
Get All Actions IDs (like divAction1,divAction2 ....) dynamic in DATASET (DTActions) allow on specific User.
In Aspx:
in HTML Put Action(button,anchor etc) in div or span and give them id like
<div id="divAction1" visible="false" runat="server" clientidmode="Static">
<a id="anchorAction" runat="server">Submit
</a>
</div>
IN CS:
Use this function on your page:
private void ShowHideActions()
{
var controls = Page.GetAllControlsOfType<HtmlGenericControl>();
foreach (DataRow dr in DTActions.Rows)
{
foreach (Control cont in controls)
{
if (cont.ClientID == "divAction" + dr["ActionID"].ToString())
{
cont.Visible = true;
}
}
}
}
Recursively find all controls matching the specified predicate (do not include root Control):
public static IEnumerable<Control> FindControlsRecursive(this Control control, Func<Control, bool> predicate)
{
var results = new List<Control>();
foreach (Control child in control.Controls)
{
if (predicate(child))
{
results.Add(child);
}
results.AddRange(child.FindControlsRecursive(predicate));
}
return results;
}
Usage:
myControl.FindControlsRecursive(c => c.ID == "findThisID");
I decided to just build controls dictionaries. Harder to maintain, might run faster than the recursive FindControl().
protected void Page_Load(object sender, EventArgs e)
{
this.BuildControlDics();
}
private void BuildControlDics()
{
_Divs = new Dictionary<MyEnum, HtmlContainerControl>();
_Divs.Add(MyEnum.One, this.divOne);
_Divs.Add(MyEnum.Two, this.divTwo);
_Divs.Add(MyEnum.Three, this.divThree);
}
And before I get down-thumbs for not answering the OP's question...
Q: Now, my question is that is there any other way/solution to find the nested control in ASP.NET?
A: Yes, avoid the need to search for them in the first place. Why search for things you already know are there? Better to build a system allowing reference of known objects.
https://blog.codinghorror.com/recursive-pagefindcontrol/
Page.FindControl("DataList1:_ctl0:TextBox3");
OR
private Control FindControlRecursive(Control root, string id)
{
if (root.ID == id)
{
return root;
}
foreach (Control c in root.Controls)
{
Control t = FindControlRecursive(c, id);
if (t != null)
{
return t;
}
}
return null;
}
The following example defines a Button1_Click event handler. When invoked, this handler uses the FindControl method to locate a control with an ID property of TextBox2 on the containing page. If the control is found, its parent is determined using the Parent property and the parent control's ID is written to the page. If TextBox2 is not found, "Control Not Found" is written to the page.
private void Button1_Click(object sender, EventArgs MyEventArgs)
{
// Find control on page.
Control myControl1 = FindControl("TextBox2");
if(myControl1!=null)
{
// Get control's parent.
Control myControl2 = myControl1.Parent;
Response.Write("Parent of the text box is : " + myControl2.ID);
}
else
{
Response.Write("Control not found");
}
}

Cannot Access the Controls inside an UpdatePanel

What I am trying to do is accessing Page Controls at Page_Load, and make a database query, and make controls visible or not visible.
Here is the Code:
foreach (Control thiscontrol in ContentPlaceHolderBody.Controls) {
try {
if (thiscontrol.ID.Contains("TextBox") || thiscontrol.ID.Contains("Label")) {
string dummy = thiscontrol.ID;
bool IsValid = db.Roles.Any(a => a.controlName == dummy);
if (IsValid == false)
thiscontrol.Visible = false;
}
else if (thiscontrol.ID.Contains("UpdatePanel")) {
foreach (Control UPcontrols in ((UpdatePanel)thiscontrol).ContentTemplateContainer.Controls) {
if (UPcontrols.ID.Contains("TextBox") || UPcontrols.ID.Contains("DropDownList")) {
bool UPIsValid = db.Roles.Any(a => a.controlName == UPcontrols.ID);
if (UPIsValid == false)
UPcontrols.Visible = false;
}
}
}
}
catch { }
}
My Problem is with the UPcontrols! It should retrieve the controls within the UpdatePanel, but the thing is it doesn't do its job, except in the debug mode!
When I add a breakpoint, everything is OK, but when I run the web application, it doesn't find any components within the UpdatePanel...
Try this one:
ControlCollection cbb = updatepanel1.Controls;
ControlCollection cb = cbb[0].Controls;
initialize_Controls(cb);
public void initialize_Controls(ControlCollection objcontrls)
{
foreach (Control tb in objcontrls) {
if (tb is TextBox)
((TextBox)tb).Text = "";
if (tb is Panel) {
ControlCollection cbcll = tb.Controls;
foreach (Control tbb in cbcll) {
if (tbb is TextBox)
((TextBox)tbb).Text = "";
}
}
}
}
First find controls from updatepanel i.e ContentTemplate, then find controls from contentTemplate which contain all controls in it.
This seems like a very bizarre design. That is, using control IDs for such purposes is rather unusual.
Nevertheless, you need a recursive method here to do a deep walk of every control on the page. Your method will not work if the UpdatePanel is contained within another control.
Have a check on this article
http://www.codeproject.com/Articles/24178/The-magical-effects-of-the-UpdatePanel-control-in

Resources