In this article we will explain how our team tackled an extremely demanding project involving the creation of a graphical interface using Windows CE and .NET Compact Framework on a low performance hardware. We will have a look to all the optimizations and fine tunings we’ve deployed in order to have smooth transitions between different screens, yet guaranteeing a rich content interface as requested by our customers.
We were asked to create a rich graphical interface for a new generation, wireless remote boiler controller. The device was meant to interact via radio with the heating system showing, in the meantime, the situation of the heating controller. The main hardware characteristics were:
The application itself was very complex: it had to guarantee the intercommunication with the heating system so that any change applied on the boiler itself was reflected on the remote controller and vice versa. Since the heating system was highly configurable, many different screens were needed to fine tune it. The screens had to show what was the system state by using an highly sophisticated graphics made of custom backgrounds, numbers with shades, updatable images and so on.
Since the beginning it was clear for us it was not possible to use the custom CF controls due to the high customization that was requested. Moreover there were technical problems that couldn’t be overcome by using custom controls, for example the impossibility to use transparency to let controls such as labels show the exact background application layout.
So our first important task was to redefine all the needed controls. Redefine a control didn’t only mean handling the graphical behavior (even if that was an hard task, especially for critical controls such as listboxes), it also meant let the controls be scalable (in order to use controls inside other controls) and memory saver
Using Windows Forms
The first software implementation was carried out using Windows Forms. Every screen provided by the customer began a form. When the application accessed a form (in the OnPaint() method) an off-screen bitmap was prepared with the requested layout. After displaying the newly created bitmap in the very same method we “attached” our controls in order to let the user interact with the application. The creation of off-screen bitmaps was time consuming (yet remaining acceptable for the customer) but, on the other hand, allowed to smooth the transition from a screen to the following one.
The main problem was related to the scarce amount of RAM at disposition. Considering the operating system and the application size (along with lots of images provided by the customer), at the launch of the application only 1.5 MB of RAM were free. Since every time the application accessed a screen the off-screen bitmap took around 0.5 MB of RAM for the creation, it means that the garbage collector was continuously called during the application’s life. And, sometimes, it failed to free enough memory, leading to an Out Of Memory exception.
The point was: where the application crashed? There was not a specific point where the application went in OOM exception but for sure it always happened when the user was moving to a new screen. Thus, the problem was uniquely related to the creation of off-screen bitmaps. And since we couldn’t afford to use such a methodology we decided to completely change approach: the application had to be based just on a single form and all the screens had to become separate panels. We split the screen graphical layout in the most appropriate method and created as many panels as the number of screens. Panels are essentially controls container so every time we had to enter a new screen we just switched on the appropriate panel and switched off the previously displayed one.
Once the application had to access a new screen, instead of launching the OnPaint() method typical of Windows Forms we created specific functions that created graphical objects, added them to panels which were then added to the application control’s list and finally “switched on” the panel itself, showing the new screen. In a dual way, once we had to leave a screen the application called a specific function that “switched off” the panel before removing it to the application controls and destroying all the objects contained. Graphical objects played still a fundamental role: since the visualization of a new screen was not carried out by an off-screen bitmap anymore, it was extremely important to ease and speed up the drawing of each object in order to avoid a popping up effect of different controls once the containing panel was displayed. In order to achieve this goal, while still targeting a minimum memory consumption, we decided to use streams inside our graphical objects.
Streams let us achieve two different goals:
The latter point was important in order to reduce the performance impact of graphical components. Their graphical representation was related to different conditions (such as application state, user interaction, …) so, for example, a simple button could have up to four different backgrounds. In order to reduce as much as possible the logic behind the graphical handling we decided to add all the needed images to the object once it was created so that the object could be really independent and show the right background according to the present conditions without the need to handle the matter from the application. Since bitmaps would have been two memory demanding, every time we created objects we used streams.
The choice of using panels instead of classical Windows Forms and the creation of low memory consumption controls lead ultimately to the creation of an application able to cope with all the different hardware constraints, yet being powerful and fast enough to the customer’s point of view.
A new project (and an hardware upgrade)
The good results reached with the application lead to e new project requested by the same customer. Same kind of application, same environment, an hardware upgrade: the device was provided with the double amount of memory, 64MB.
The complexity of the project was even greater than the previous one: now the device had to deal with a different number of actuators instead of a single boiler, so the logic behind the application and the number of screens was considerably greater. Due to the memory improvement we were confident to be able to create an application based on Windows Forms, nevertheless we decided to keep using the panel solution due to the experience we had gathered in the previous project.
The presence of 64 MB of memory let us use again some off-screen bitmaps to draw the most complicated controls, such as the listbox. Still, we had to focus on a performance problem: sometimes it took too much time to draw a very complicated control, leading to a popping effect which had to be solved.
In this second project there were screens where different big, graphically complicated objects had to be displayed on the same screen (this means: on the same panel). Off-screen bitmaps helped to smoothly create the graphical background, still this operation needed some time and when more than a complicated object had to be displayed on the same panel an unavoidable popping effect appeared.
Since we had no way of reducing the time needed to create the off-screen bitmaps we found out a workaround by implementing a sort of object intercommunication. When an object created its off-screen bitmap, it raised an event notifying the containing panel that he was ready to show the bitmap; only when the last control had finished up creating its bitmap, the panel showed all the controls all together, because at that time all the controls representing the panel were ready to be drawn.
This solution slowed down the transition from a screen to a new one but when the new screen was showed it seemed like a full off-screen bitmap was created for the whole screen size. In this way we were still able to guarantee a smooth screen appearance, yet guaranteeing the minimum possible memory impact.