Sumary
I am currently working on scheduling application for manufacturing company. In my application, I got calendar with production plan as shown on this picture:
Most elements supports left/rightclick, shows tooltips when mouse enters and elements in calendar rows (those are working phases of production plan) also suppors drag and drop and synchronise with database (local, communication with Entity Framework).
Number of rows is based on number of projects in set time interval and thus dynamic. I would also like my number of columns (days) dynamic and let the user to set his own time interval, but it is not required.
The problem is that even though I think that the result is very good, I was (still am) quite inexperienced when started the project, and the result is slow (+- 3 seconds rendering for 20 rows - in practise, I am always working with no more than 25 rows). This is due to my bad design. Hovewer, I don't know how to achieve similar results to this with better porformance.
I am sure that rendering is the problem
I tried the profiler (the one with Visual Studio/Resharper) and confirmed that indeed, bad design of frontend rendering is the problem. SQL Queries are running around 20ms, methods that create and render rows 3 seconds. I also tried to simplify the data (no graphical elements, just strings with no functionality mentioned above) and put them into DataGrid - the result was no lag until around 70 rows, which is much more than I need.
Wasn't able to find solution
I spent 3 days googling and reworking my code, but found no solution really usefull for my case. The closest I get was this answer wpf DataGrid of UserControls, with putting usercontrols into datagrid. Hovewer, the result didn't look as good as the current state and, more importantly, it proved almost impossible for me to bind usercontrol into database entities correctly.
Here is the method that costs me most performance:
private void RenderFull()
{
// prepare columns
columns.Clear();
columnForDate.Clear();
ColumnJobHeader columnJob = new ColumnJobHeaderClassic(job);
columnJob.JobUpdated += ColumnJob_JobUpdated;
columnJob.JobDeleted += ColumnJob_JobDeleted;
columns[0] = columnJob;
DateTime processedDate = startDate;
while (processedDate <= endDate)
{
ColumnJobDay col = new ColumnJobDay(job, processedDate, startDate, endDate);
columns[columns.Count] = col;
columnForDate[processedDate] = col;
col.PhaseChanged += Handler_PhaseChanged;
processedDate = processedDate.AddDays(1);
}
// remove rendered columns
RemoveRenderedColumns();
// render new columns
RenderColumns();
}
This prepares all columns in database with projects (6+ row). They are then generated in this method (AbstractColumn is UserControl):
public override void RenderColumns()
{
foreach (KeyValuePair<int, AbstractColumn> pair in columns)
{
ColumnDefinition columnDefinition = new ColumnDefinition();
if (pair.Value is ColumnJobHeader)
columnDefinition.Width = new GridLength(200);
Columns.ColumnDefinitions.Add(columnDefinition);
pair.Value.SetValue(Grid.ColumnProperty, pair.Key);
Columns.Children.Add(pair.Value);
}
Wrapper.Child = Columns;
}
I know that generating view in code behind like this is bad for design AND performance and I can promise that I learned my lesson to never do it again. However, I have no idea how to achieve such an application with the correct design.
I appreciate any advice. Thank you in advance guys!