I have a customized Grid control that is inherited from Gridview that has search and pagination. Everything works great except this one little thing. Whenever I use the pager to go to the next page, then I use the search, the event that handles the search does not fire on the first click, it takes a second click to get it to fire. Any ideas?
The code for the control is here:
Grid.cs
Important Note
I am aware that it is frowned upon to post large blocks of code... But, the entire control's code is posted to give the whole picture of how it is built. The control itself is a bit on the complex side as it performs searching, sorting, and pagination all server-side; and this code is a completely custom control that just INHERITS the GridView.
I AM NOT looking for someone to write a fix for me, just an idea of why this one situation may be happening!
With that said, to break things down a little more with the code... The search form (text box and buttons) are created dynamically and added in the CreateChildControls method. The search form works perfectly when the Grid is initially loaded, but after using the pagination, the first click of the Search button does not fire the method assigned to the Search button's command event, but the second click does. It seems as though something in the postback is not recognizing the button's command event has been triggered...
A trace of the calls to the methods show:
1st Click - Everything from ViewState is loaded, no postback events are called.
2nd Click - Everything from ViewState is loaded, postback events called.
I'm looking for ideas on where to go from here, as I've been trying everything I can think of page life-cycle wise to see if I can get this functioning properly. The only thing that has worked is setting EnableEventValidation to false on the page that implements the Grid control, and performing the "initial" data bind on every "Page_Load" (not just "if !IsPostback"). But, for obvious reasons, that is not an acceptable solution.
Found the issue... Posting here for anyone who might make a similar mistake.
In CreateChildControls(), I was executing the base before adding the search form to the control. A simple switch around to calling the base after adding the search form, and getting rid of the condition "if (this.HasControls())" resolved it. From what I can see, calling the base after adding any child controls allows those child controls to have their events/handlers properly registered.
Related
In a MasterDetail like page on the detail part it is possible to edit within some TextBoxes. When I select a new item in the master part I have to check validity of all input. Also, when pressing the hardware back button.
My problem is, when I check with CanGoBack I get the answer true, then I call GoBack. Before calling GoBack I'm able to let the page check it's validity and returning false in case it is invalid. Then I do not call GoBack. So far so good. Unfortunately if a TextBox has still the focus from the last input, the LostFocus is only called after GoBack and so the Bindings from that control aren't written back to the model when I check the validity, since it is checked before GoBack.
Is it possible to forbid the GoBack going back somehow?
Determining the control with the focus and call it's BindingManger to update the bindings does not work, since the focus is already on the new selected item of the listview in the master part. And also since I use compiled binding, I have no BindingExpressions I could invoke manually.
Another try was to call Bindings.Update() before the validity check, which is available extra for compiled bindings. But this method works silly. It first writes all the values into the view and then after back to the viewmodel. Thus it overrides my input.
Does someone have an idea how to handle input validation on pages correctly and forbidding GoBack when they are invalid?
I'm trying to build a very specific search page for a project and I'm having lot of trouble dealing with multiple postbacks invoked by dynamically-generated controls on a single page.
The page has to work like this:
There is a single checkbox, "Detailed search", that causes a postback on checking/unchecking.
When detailed search is not active, a simple grid with contents and buttons is displayed. Nothing special.
When detailed search is active, N checkboxes must be generated from some dynamic data, that represent the sections where you want the search to happen. Below the checkboxes, an AJAX-enabled tab control will appear, initially with no tab pages.
When checking one of the section checkboxes, a postback will occur. After the postback, data will be searched in the section selected by the user, then a new tab page containing a grid view of results and the name of the section will be added to the tab control. If the checkbox is unchecked, the tab page will disappear from the control, again, after a postback.
Now, the issue is that pretty much everything has to be generated dynamically, and that pretty much everything is connected to something else.
First issue: dealing with the "Detailed search" checkbox. Sounds easy, doesn't it? My initial idea was to set Page.Viewstate["DetailedSearchEnabled"] to true or false during the check/uncheck event handler, then create controls dynamically checking the value of DetailedSearchEnabled during Page_Load.
Nope. The postback event-handling happens between Page_Load and Page_LoadComplete. It would take an additional refresh for things to work as intended.
<< Then I'll just generate the controls on Page_LoadComplete! >>
Nope. Those controls need event handling as well, and if they're generated after Page_Load they will not be wired up correctly.
A possible solution would be generating everything in advance, on Page_Load, and only hiding/showing controls on Page_LoadComplete. But that is inefficient, and one important point of this search page is that only the minimum amount of controls should be generated.
The difficulty of this task seems to come from the way event wiring and the page life cycle work.
Surely there must be a better way of approaching this problem.
First issue: dealing with the "Detailed search" check box.
The correct approach (if you want to use page post-backs) is as follows:
In the CheckChanged event handler, save the value of the Checked property to ViewState["DetailedSearchEnabled"]. If the value is true, add the dynamic check boxes to the page. If the value is false, find and remove them.
Override LoadViewState. After calling base.LoadViewState, re-create the dynamic check boxes and wire up their events if ViewState["DetailedSearchEnabled"] is true. Note that neither Page_Load nor Page_LoadComplete is the appropriate place to do this.
Yes, you should create the dynamic check boxes at two points in the page life cycle. I recommend a helper method.
In general, your event handlers should add or remove just the dynamic controls (if any) affected by those particular events, but LoadViewState should re-create all dynamic controls that existed from the previous page request. You must store enough information in view state for LoadViewState to do so.
My answer to this other question demonstrates how to add and remove dynamic controls. You may want to use it as a reference.
Sounds to me like you should be using a CheckBoxList control to handle your dynamic checkboxes. You can add an remove items to the CheckBoxList during your post back and not have to worry about dynamically adding/removing actual controls/events to the form.
Here is a link to the msdn:
https://msdn.microsoft.com/en-us/library/14atsyf5(v=vs.85).aspx
Here is some sample code:
Protected void Button1_Click (object sender, System.EventArgs e)
{
CheckBoxList.Items.Add(new ListItem("TextValue1", "Value1"));
CheckBoxList.Items.Add(new ListItem("TextValue2", "Value2"));
}
If all else fails, you could still fall back on the quick-and-dirty old-fashioned ASP way.
Use Response.Write or <%...%> to generate your dynamic controls as plain old HTML (simple form fields, e.g. <input type="checkbox" name="foo" value="1" />).
Make sure you have a form field for every piece of information you may need after the postback(s). If necessary, use hidden form fields to 're-post' values across subsequent postbacks.
After postback, retrieve the values of the controls with the Request object.
Use those values to adjust the generation of controls as you see fit.
You should be able to do all of this in Page_Load. The advantage is total freedom. The disadvantage is total freedom to make a big mess of your aspx. So you may want to migrate all this dirty code out of your aspx, and into a custom-made control, which you can then add to your aspx.
When generating your own HTML, be careful not to introduce XSS vulnerabilities. Use HtmlEncode where necessary.
As you suggested yourself, there is a better way to tackle it.
If I was in the same situation, I would create web methods for interacting with the page, and use client side to do the UI. I'm currently working mostly with angular JS, although it does come with a learning curve. You could use ng-hide/ng-show to bind to the checkbox event to display the detailed search. When the n number of checkboxes needs to be displayed, you can then just fill them in with ng-repeat, for each of the items you need to display, after a check/uncheck you can dynamically populate new controls etc. through web method calls if extra data is needed.
Pure ASP postbacks are quite clunky from my experience, and not really suited for building a maintainable dynamic UI.
Instead of making so many postbacks, it would be better to use jquery and ajax calls to load the controls as needed and then attach events to it or you can even use UpdatePnael for that. Help Links:
https://www.simple-talk.com/dotnet/asp.net/ajax-basics-with-jquery-in-asp.net/
http://encosia.com/using-jquery-to-directly-call-aspnet-ajax-page-methods/
This seems very simple, but I can find nothing on a web concerning the behaviour I want to add to my custom control.
My custom control is a textBox with a list of choices. When the text entered by the user is not part of the list, a popup will appear with the list allowing the user to select a correct choice.
Sometimes, there may be a default button on the container in wich the custom control has been added. If so, when the enter key has been pressed, if the text is wrong, The popup must been displayed. If there is no default button, on enter, nothing must happen even if the text is wrong.
To be able to create this behaviour, I must be able to detect the presence of a defaultbutton in the container, and it must be done inside the c# code of the cutom control.
I hope the description is clear enough.
Thanks in advance
Have you thought about implementing an MVVM approach and the Command pattern? So long as your view model knows what the choices are, you can bind the default button to a command. So long as the commands CanExecute handler returns false, i.e., an appropriate choice has not been entered/selected, the button will be disabled and won't respond to the user pressing enter.
Since I was unable to know what other controls I had from the custom control I chose to go like this:
I made a recursive function to find the first parent using FrameworkElement.Parent
Having the parent, I could take a look at every controls it contains.
As soon as I saw a button, I had to verify if IsDefault.
For this one, I used the VisualTreeHelper GetChildrenCount(DependencyObject obj_Parent) and GetChild(DependencyObject obj_Parent, int childIndex). Recursivity once again...
It works very well even though it means more code to execute.
I have a survey, where you have to click through several pages with questions. I use a "Next" and a "Previous" button for doing this. I use a session for keeping tabs on my position. However, this is a problem.
I use the button_click event to increment the page counter, but since this fires after the page_load event, nothing happens on the first click, and for every click thereafter everything is one page behind because the questions are rendered before the counter is incremented.
Is there any way to solve this without using the query-string?
Comment response 10:15:
My understanding is that the following happens:
The first page loads and the counter is not set. The counter is set to 1. The first page is displayed.
The user clicks the "Next" button, firing a postback.
The page loads, displaying the same set of questions, because the counter is still on 1.
The buttons Click-event is run, incrementing the counter. However, the page is still displaying the old questions.
After this everything is one page behind because the questions are rendered before the click-event is fired, incrementing the page counter.
I'm probably not seeing something obvious here :|
Why don't you use the out-of-the-box Wizard control? This is what it is designed for. They have solved all of this issues that you are likely to have and it is entirely customizeable.
Scott Gu's blog
MSDN Walkthrough
Like Pavel said in the comment posting some code to clarify the question might help in seeing where you could have gone awry. There could be several things amiss, first check to make sure your logic is sound in your code. Go through step by step and see if it doing what you are wanting. Setting up break points is also a good idea to find the issue. It could be just a variable out of place (this has happened to me many of times and caused a lot of hair pulling).
I got past it by using the LoadComplete event to set all the "dynamic" text and controls. Since the LoadComplete event of the Page object fires after the postback-events of the controls, this works as I want it to.
I know there is a similar problem on this forum, but the solutions did not really work for me. I am populating form controls with fields from a few different data sources, and the data shows up great.
I have an ImageButton control, which has an OnClick Event set to grab all of the data from the form. Unfortunately, when I click the button, it seems as though the page is reloading first, and THEN is executes the OnClick call. The data that was hand-entered, or hard-coded seems to be pulled fine from the controls it was entered in, but anything that was pulled from a datasource is not able to be read. Any ideas. this is the last hurdle in a project that I have been working on for 6 months.
Are you talking about drop downs or gridviews? When are you binding data, on page load?
Good design will have you bind your data upon page load but only in
if(!isPostBack){
dropdown.databind()
gridview.databind()
}.
Otherwise it will rebind every page load. If its not reloading you can get selected values from those controls if thats what your looking for.
An alternative is to set your data source and databind in your aspx page with a datasource object. That automates the above automatically.
Have you enabled viewstate on your controls? Posting code samples would go a long way to helping solve your issue.
When you click on a bottom in asp.net at first all the page Events take place like Page_Load and ... and then the event happens ( in this case Click).
But because everything is loading again, I think that you have a !isPostback in your code that you use to bind the data, you should remove that in order to get your data every time.
Or if it is not the solution please post some code and more description for the problem
Actually, it is hiddenfields, dropdowns, labels, and textfields. I just tried doing the binding in the init, and the load, but no dice. When I tried binding it on !isPostBack only, none of the fields showed up.
I think one of the main problems is that the dataset I am getting is from a method call to an API. I receive the data fine, but it comes in programmatically, then I have to do all of the control-setting programmatically as well. Would you like to see the code for ideas? Thank you for helping, no one is working today!
Jason