Please consider this scenario: page with one ASPxPageControl with several TabPages each with one or more ASPxGridViews (bound to several LinqDataSources).
I want everytime a TabPage is clicked to refresh all its content, i.e. only that TabPage’s GridViews get refreshed, so I am using ASPxPageControl in callback mode and also reloadContentOnCallback = true.
However on each callback all LinqDataSources for all Gridviews get refreshed sending unnecessary queries to the database, consuming too much time on a complex page like this.
I realize that ASP.NET mandates that all controls be reconstructed in callbacks, but does that necessary mean that all “invisible” controls need to get their datasources refreshed ?
Do you have any suggestion on how to implement this kind of functionality?
ASPxPageControl - How to improve the TabPages loading speed
Answers approved by DevExpress Support
Pedro,
I have examined the application and found out that you faced the ASP.NET Page Life cycle issue:
When a page is rendered for the first time, the current tab index is 2. The page control preserves this value and it is always set to this value before the control initialization. When you switch the control to a different page, the control does not update its ViewState (because of callbacks), and thus the server always preserves the "2" index. However, when you open the third page again, the server sees that the initial value is still 2, a user opens the third page (the index is 2, again), and thus it does not see a difference. That is why the "changing" event is not raised.
I have slightly modified your application to make it work. Please take a look at the attachment.
Thanks,
Vest
Hi Vest,
Sorry for the late reply.
Thank you very much for your example everything works fine now.
Although i cannot use it becaus a session variable is not suitable for usage with more than one opened copies of the same page, i will try to adapt it.
Best regards,
Hello Pedro,
It is not necessary to use Session. You are free to use ASPxHiddenField and work with it using client-side code and the ASPxPageControl.JSProperties property, or you can use a standard hidden field, and read its values using Request.Params.
Thanks,
Vest
Hello Pedro,
I suggest that you set the ASPxPageControl.AutoPostBack property to false, and ASPxPageControl.EnableCallBacks set to true. In this case, ASPxPageControl will operate in the following manner. During the first response, only the active page's content will be loaded. When the active page changes, the new active page's content is loaded from the server only once - it is performed dynamically via a callback without refreshing the entire web page. When all pages are loaded to the client in this manner, the ASPxPageControl switches to the "client mode", and further work is performed without generating round-trips to the server.
As ASPxGridView caches data on the client side (if you do not disable this option), additional database queries should not be generated.
Thanks,
Marion
Hello Marion,
Sorry, but i believe you misunderstood the scenario I was talking about. Let me try to better explain it.
What I desire is for each TabPage content to refresh itself when it becames visible, so gridviews need/must equery the datasource, but only the visible TabPage's gridviews, not all the gridviews in the aspx page.
I already have AutoPostBack at false and EnableCallbacks at true, and I believe I understood the normal behaviour of ASPxPageControl like you explained here and I read at your online documentation.
Since the normal behaviour is not what I desire for this scenario, I researched your KB for a solution and found that if we set reloadContentOnCallback to true in the PageControl's client side ActiveTabChanging event, we can enforce each TabPage to refresh itself when it becomes visible (always, not only for the first time). So the PageControl is working correctly, according to my scenario.
Also, I also believe that in order for each GridView to correctly refresh when their parent TabPage becomes visible, its necessary to disable data caching. Is this correct ?
If so, how can we prevent all other non-visible GridViews from re-quering the data source? If at all possible…
Best regards,
Pedro
Hi Pedro,
Thank you for the report. I adjusted the ASPxPageControl as you explained and I do not see the problem you have mentioned. Please refer to the sample project attached.
Thanks,
Plato
Hi Plato,
Thank you very much for your quick reply.
In fact, your sample project works almost as expected.
But if you put a breakpoint in all Selecting functions, you can see that SqlDataSource3_Selecting gets called exactly once, so data is not refreshed on subquent Tab3 clicks.
Thats why I thought of adding EnableRowsCache="false" to all GridViews to try to solve this issue. But if you do this you can observe that OnSelecting event handlers are fired all the time, not only the one in the "visible" tab page. Is this a bug?
I also tried another modification to your sample project, adding EnableViewState="false" and ViewStateMode="Disabled" to the PageControl and all three GridViews. Now it seams to work as expected (with EnableRowsCache="true").
This has also the added advantage of reducing the amount of traffic between the browser and the server, i think, although I did not measure it…
Can you please confirm this findings on your side ?
Best regards,
Pedro
FYI, regarding that strange behaviour of SqlDataSource3_Selecting only gets called once, I just confirmed that changing ActiveTabIndex to another tab, that tab also stops refreshing its gridview, unless we disable viewstate.
Regards,
Pedro
Plato,
I am sorry to report another issue. In your sample project just add the following to ASPxGridView2
<SettingsBehavior AllowSelectByRowClick="true" AllowFocusedRow="true" EnableRowHotTrack="true" />
Now try it to run the project, switching tabs, the go back to the second tab e click on the gridview2 to "select" another row, then try to switch tabs and see what happens…
I got "A primary key field specified via the KeyFieldName property is not found in the underlying data source. Make sure the field name is spelled correctly. Pay attention to the character case".
Regards,
Pedro
Hello Pedro,
Thank you for your response.
>>Can you please confirm this findings on your side ?
It is necessary to disable the row data caching option (to force explicit data fetching) by setting the ASPxGridView.EnableRowsCache property to "false".
I have attached a modified project. Please check how the "Selecting" events work in mode.
>>I am sorry to report another issue.
Our team is dedicated to addressing all your concerns. In order to better serve you and track multiple questions from your inquiry, we have taken the liberty of separating the issues you addressed. For quick and efficient responses to your concerns, we kindly request that future inquiries address one issue at a time. Your time and cooperation are appreciated.
For now, I have created a separate issue on your behalf:
Q229484 - ASPxGridView - "A primary key field specified via the KeyFieldName property …" exception is thrown when Selection is enabled
Thanks,
Mike
Hi Mike sorry for the late reply.
I just run the project you attached. In fact turning EnableRowsCache to false in GridView3 forces data fetching when TAB3 is clicked.
Now, if you check the number of calls to SqlDataSource*_Selecting functions, you will see that whatever TAB you click, SqlDataSource3_Selecting is always called.
So I as understand, in order for the gridviews located in the TAB indicated by ActiveTabIndex property to work we have to disable row caching but that gets us to the undesired effect of always fetching data from the DB for that gridview even if they are not visible on page…
So, I believe that your suggestion does not yet solve the problem in the scenario we are discussing, i.e. “Optimizing speed by fetching data from DB only for the visible gridviews inside an ASPxPageControl”.
Regards,
Pedro
Pedro,
In ASP.NET the page hierarchy should be created each time you perform a request to the server. Modern database servers can handle the same requests efficiently (e.g. if you enable caching), so I think that you do not need to worry much about this. Because of the ASP.NET page life cycle, our grid always requests data from the database when you click a tab.
If you wish, you can try to control this process using the solution from the How to create ASPxGridView after the page has been loaded example. However, I have to warn you that you will get an unexpected result if you try to get values from empty grids (e.g. your code uses some records).
Thanks,
Vest
Hi Vest,
I know that fact about ASP.NET. The speed improvement scenario I was talking about is only relevant if you have a ASPxControl with many TabPages and each of them with one or more Gridviews. Even if the database engine is running in an optimized fashion, it could take many seconds to load a complex page like this if all the gridviews in all tabpages try to query DB.
So far as I understand, in you example, the main idea is to try to circumvent this by placing each TabPage content inside a CallbackPanel and setting Visible property of all gridviews to false, until each TabPage is clicked and a callback is sent on the init event of each CallbackPanel, correct?
If I set Visible="false" in a gridview, it will not fetch data from DB ?
I am going to try to implement your suggestion into my code, and I will get back to you on this.
Congratulations on your excellent support center operation.
Thanks,
Pedro
Pedro,
Our page control is able to display pages using callbacks. For example, when callbacks are enabled, you can create user controls in tabs dynamically.
Please check this example to see this approach in action: How to create and load an active tab's content on a callback. From the description of your scenario I think this example should suit you well.
P.S. We appreciate your compliments on our Support Services :)
Thanks,
Vest
Hi Vest,
I've just changed the sample project attached to this question to include some of the suggestions of both your examples. I am attaching the modified project (Q348532a).
Please have a look if I missed something because it's still has the problem of not calling SqlDataSource3_Selecting (because TabPage index 2 is the ActiveTabIndex of the PageControl).
Let me make a summary of the changes:
- All GridViews are set to Visible="False"
- Both OnLoad and OnActiveTabChanged events for the ASPxPageControl are handled on the server by calling a function that sets the active tab GridView's visibility to "True".
The behavior is as expected for the first and second tab pages (i.e. SqlDataSource1_Selecting and SqlDataSource2_Selecting are called only when the corresponding tab page is clicked).
But, again, SqlDataSource3_Selecting is called only once (the first time the page is loaded), and although reloadcontentOnCallback is set to true when clicking the third tab page, the ActiveTabChanged event is not fired.
If you set ASPxPagecontrol's ActiveTabIndex property to another tab index, the behavior is the same for that new tab index (it's Sql Selecting is called only once).
I believe this is the main issue here, I am not sure that the 3rd TabPage content is being properly reloaded.
Previously, you have suggested setting EnableRowsCache to false in the misbehaved GridView, but that only has the effect of always calling the corresponding Sqlxxxx_Selecting, and therefore is not the solution.
Regards,
Pedro
Good news, I just devised a hack that allows the sample project to work as expected.
Just add an addicional empty TabPage with ClientVisible="false" (a 4th tab page) and set ActiveTabIndex="3". This only works if you do not enable SaveStateToCookies. (Could not find a way to make work with cookies)
It would be nice if you could fix this in a later realase of ASPxPageControl, though.
Best regards,
Pedro
Hello Pedro,
Thank you for sharing your results with me. I see that you have solved the issue. If I am misled, please tell me about this.
I see that cookies prevent your project from operating properly. Would you please clarify how to reproduce the issue with your sample?
Thanks,
Vest
Hi Vest,
Sorry for the late reply.
Please do the following to my sample project:
1 - (HACK) Add an additional empty TabPage with ClientVisible="False" after the last tab
2 - (HACK) Set PageControl's property ActiveTabIndex to "3" (the new tabpage's index)
3 - Now test the project. As you can see the *_Selecting functions get called only when each corresponding TabPage is clicked (the behaviour I was looking for).
4 - Now set PageControl's property SaveStateToCookies to "true", re-run the project. The tabpage saved to cookies is no longer refreshed on page load, perhaps I am missing something.
So, in conclusion, I believe we can close this question. The scenario we were discussing is now working (exception made for save state to cookies).
Should I create an issue for that problem with ActiveTabIndex not working (the one i solved with the hack) ?
Regards,
Hello Pedro,
I have examined the application and found out that when cookies are used, your code does not work correctly, because the page control restores its active tab index later than the Load event is raised.
If you put a breakpoint in the PreRender event, you will see the new tab index value.
I assume that your code will work fine if you load the active tab hierarchy in the PreRender event handler (this code will be executed only once, because during callbacks such event is not raised).
Thanks,
Vest
Hi Vest.
Thank you very much for your input. Moving my code to PreRender solved the cookies problem, but now the hack of adding a 4th client invisible TabPage does not work.
I believe you should really try to figure out why the ActiveTabChanged event is not fired when we click the TabPage corresponding to "initial" ActiveTabIndex (or index saved into cookies). Do you want me to create an issue with this ?
Best regards,
Pedro
Pedro,
Please pardon me, but I am a bit stuck on your project. Could you please clarify what should I do with the application to reproduce the issue with an empty tab?
Do I need to add a new tab and then check that the TabIndex is changed properly?
Thanks,
Vest
Vest, no problem, I just attached another version of the project with 2 "default" pages.
- Default.aspx works fine with my hack.
- DefaultNW.aspx does not.
To confirm pleace breakpoints in *_Selecting functions of both pages.
As I understand the problem is that in DefaultNW the ActiveTabChanged event is not called for the "inicial" TabPage (the one specified in the propertie ActiveTabIndex - try changing it).
Best regards, and please excuse my english…
Pedro
Pedro,
Thank you for the updated project. I will examine it soon.
Please excuse me for any possible delay.
Thanks,
Vest