Hi together,
we have some performance problems with the UltraWinTree. Populating data nodes collection takes extreme long. From 3 to 15 seconds, depending on the amount of data that is shown, of course.
To the application. It is a financial planning tool for employees manning guides. The image below shows a screenshot of application's tree and how we show that data to the user. As you can see, there are 4 levels of data. We show the positions that exists in a department. Employees can be attached to this positions. Then, every employee has a salary structure (structure is defined by the position and can very from position to position). So there are usually about 16-24 rows for salaries per employee (gray rows). Department sizes vary from 2 to 10 positions. Per Position there are about 5 employees. So we have about 10 * 5 * 24 = 1200 rows in really big departments. Smalls ones have about 200 rows.
Data is bound via data sets with tables and key constraints to the tree. We do all formattings per node in the InitializeDataNode event handler. Formatting takes about 200-300 ms. We can live with that. But populating the data nodes collection takes 3 to 15 seconds. With that, we can't live. To gain performance we set the SynchronizeCurrencyManager to false. We won about 200 ms.
Are there any other tricks to get more performance? Would be great if you can help here. Since the shown structure is the minimum structure we need in the application.
Thanks in advance!
Robin
Hello Robin,
Thank you for contacting Infragistics Support.
I followed the steps you suggested and was unable to reproduce the behavior you're describing. I have created a small sample solution with UltraTree with four level of nodes with approximately 5 – 6 thousand nodes. I have applied an appearance to each level in InitializeDataNode event handler. My UltraTree gets populated in less than a second.
Please let me know if you perform some other operations over the three nodes in your application. Are you apply stilling according to some other criteria than node level? If so what criteria are you using? Do you perform some calculations over the nodes before load them, e.g. summarize data, calculate data in some columns depending on other column and so on?
I have attached the sample project I used to test this. Please test this project on your PC; whether or not it works correctly may help indicate the nature of this issue.
If the project does not work correctly, this indicates either an issue possibly specific to your environment, or a difference in the DLL versions we are using. My test was performed using version 14.2.20142.2059 in Infragistics Controls 14 Volume 2. So could you please let me know the exact version of Infragistics components which you are using?
If the project does show the product feature working correctly, this indicates a possible issue in the code of your application. It will help if you can provide a small, isolated sample application that demonstrates the behavior you are seeing.
Or, if this sample project is not an accurate demonstration of what you're trying to do, please feel free to modify it and send it back.
Waiting for your feedback.
Wow, that rocks! Great! Thanks a lot for the reply and demo project, Milko! Yes, must be something in our code, sure enough.
Yes, we do it a little bit different to your demo project. We do also format the columns in OnColumnSetGenerated. And our styles are a little bit more sophisticated. Then, we need to allow cell editing depending on various criteria (e.g. is the employee valid/active in a month and so on). At the last node level, we have to differentiate between different line types (for salaries that can be edited, for vacation that is calculated etc., all with different formatting). Then, we do the formatting directly on the existing Appearance objects. We do not define and reuse our own Appearance objects. Maybe this costs a lot performance? I will try that in our application.
In order to show you how we format the tree I like to send you our TreeFormatter class. We extract all the formatting logic to this class. It's not a running application as you suggested, but maybe you can see critical mistakes we do. The method SetupManningGuideDataNode is called in the InitializeDataNode event handler for each node. You can see what we do with each node in this method. Commenting out this code gives a boost of about 50-60% but populating data nodes costs still about 1-3 seconds.
We do no calculations depending on columns. All data including calculated data is coming from the server. We have the data tables in the data set and simple bind it to the control. But as mentioned above, we do formatting based on columns. Please see the attached TreeFormatter class again. There is the method SetupColumnSet that is called in the ColumnSetGenerated event handler.
I changed the references in your project back to 12.2. The version we use currently in the production version. Your application stays amazing fast with it. So I think there is no issue in the version we are using. Must be our code.
So I would really appreciate any hint or advice from your side. We are fighting with that problem at some customers which are really dissatisfied with OUR application. We really want to make it faster and I can not believe that this won't be possible with your controls. :)
Many many thanks for the demo and in advance to your answer!
I forgot an important point. We use some hidden columns in the tree for IDs. So every month has its own ID column. So imagine you change a figure for one employee in e.g. march. Then we take the id for this particular value from the hidden id column for march. With that id we can save the particular value at our server.
We need this because every month has some more value. Consider planning with hours and cost per hours rather then with one nominal value. So the particular monthly value is a little bit more complex. That's why we have a business object for such a "PlanningValue". And the id I mentioned above is exactly for the PlanningValue object.
But this PlanningValue objects are not bound to the tree in any way. That's what's behind the (UI) scenes.
I've deactivated all formatting actions. For data nodes as well as for column sets. But it takes still 1-3 seconds, 5 seconds for a big department. Time goes somewhere and I don't know where... For now. :)
Thank you for your feedback.
I have checked your ManningGuideTreeFormatter class where you have put all the formatting logic. I saw that you set up the appearance of the cells on many places as well as their functionality. In order to speed up the loading of the tree please consider these two points:
Consider reusing of all the Appearances you are creating. Please check this article, more specifically point 2) Re-use Appearances http://ko.infragistics.com/community/forums/t/15306.aspx
If I understand correctly when first displayed all the nodes of your tree are collapsed. Please try to implement lazy loading of tree nodes. There is a sample in our samples showing how to achieve this. If during installation of Infragistics Controls you have install the samples it should be in Program Files, Infragistics (NetAdvantage) directory in this subfolder “\Windows Forms\Samples\Tree\CS\UltraTree Load On Demand CS”. If you implement this logic you will have not more than 20 nodes loaded in the same time, and this should be almost instantly.
Please let me know if you need any further assistant.
Hi Milko!
Thanks a lot for your answer and the tips! We are heavily working on it and evaluate your suggestions. Problem is, that we have to deliver asap to the customer. So we are working on the product while evaluating your suggestions. That's why I did not answer the last day.
Lazy loading sounds good. I'll have a look on it from now. Regarding this, I have some further question. In hope, that you can give me some further hints since I cannot find Best Practices in your documentation. Maybe you can guide me to the correct websites. So here are the problems:
1. The user works in the tree and edits values. There are rows (e.g. the employee benefits) that depend on the changed values. So we like to update the calculated rows while the user is typing. Do you have some best practices for requirements like that regarding the tree? How to update the data table rows and prevent the tree from complete rebuild?
2. The user can add employees to positions. We do that by adding the new rows to the appropriate data tables. But if we do so, the tree rebuilds completely. The user defined view (expanded/collapsed nodes) goes away because the tree is rebuilt. How can this be handled?
Hopefully, you have some solutions for me, I am looking forward to your answer!
I will respond to your last answer and mark it as solution, if it helps in some way. But for this, I have to try the lazy loading stuff now. :)
Many thanks in advance!
I am following up to ask whether or not if you have any additional questions with our UltraTree control? If so, please let us know so we can assist you further. Thank you and have a nice day.
If I understand you correctly when the user updates a single cell you are sending updated information for this cell through services. Then you perform some server side calculations and return all tables trough the service again. Can you tell me do you really need to send back all tables? Does your scenario allows to get only the updated rows? This should speed up the process.
Regarding freezing is it only the tree that freeze or entire form is frozen? How long does it freeze? Does the tree start to respond again or it hangs at all? Depending on your scenario one possible reason for this freezing may be slow handling of the request you send to the server.
DataTable’s load method when called with LoadOption set to Upsert actually loads all new rows while retain the old ones. Is this your scenario? This will load again entire data set each time the user change the content of a cell. However I believe that if your scenario allows it, it is better to retrieve from the server only changed rows. Please let me know if this is possible in your application.
When I replace this code
boundTable.Load(updatedTable.CreateDataReader(), LoadOption.Upsert);
by this code
foreach (DataRow row in updatedTable.Rows) { var values = (from value in row.ItemArray select value.ToString()).ToArray(); UpdateInsertManningGuideRow(boundTable, values); }
private void UpdateInsertManningGuideRow(DataTable tableToUpdate, string[] values)
{ if (tableToUpdate == null) throw new ArgumentNullException("tableToUpdate");
if (values == null) throw new ArgumentNullException("values");
var id = values[1];
if (!tableToUpdate.Rows.Contains(id)) tableToUpdate.Rows.Add(values); else { var row = tableToUpdate.Rows.Find(id); row.ItemArray = values; } }
It works for me! There is no freeze anymore.
Ok, I extend your project in the way we do updates of the data tables. Please have a look into ultraTree1_AfterCellExitEditMode. So if you change values and tab through the cells or click somewhere else after editing, you can see an error that is not catchable in my code. It is not really the same problem I described above with the "freeze" but goes in that direction.
So maybe I should update data table rows manually, not with the DataTable.Load method...
Find the project attached.
Thanks a lot for your answer!
1. We do the calculations at the service backend and request the changed data again or return it directly by the services. Then we update the data tables via DataTable.Load method with LoadOption.Upsert. Works fine now! But the new problem is, that after upserting the changed data into the data tables, the user cannot leave the current row where he changed a value (and therefore a cell was in edit mode). No other row is clickable, row selection does not change. No events are fired from the tree anymore. Looks like a freeze of the tree except in the last current row. Maybe DataTable.Load is not a good idea? Do you know this problem somehow?
2. Adding new rows via DataTable.Load works pretty good and VERY FAST! :)
I try to extend your demo to show the problem from point 1. It's easier than to send you our code. Hard to extract an example from it.
Cheers!