Fog Creek Software
Discussion Board




Optimizing massive data entry forms in Windows?

I am curious about programming techniques to optimize the use of many controls (dozens to hundreds) on one data entry window within Windows. This is for a Win32 desktop application, and will not be web-enabled in its present form.

The application platform is Delphi 7.

My client's data entry screens consist of dozens of handle based controls like edits, check boxes, radios, combos, etc, plus perhaps hundreds of label type controls. In Delphi there is a slight optimization of labels available - static labels with no input capability are derived from a non HWND based control hierarchy so are relatively "lightweight". But edit controls are ultimately derived from the built in EDIT. Etc.

The labels are so numerous because the on-screen forms are derived from hard copy forms (via PDF files) and we reproduce every detail of the official "base" form that we allow input to, including all captions, lines, shaded boxes, etc. Every such form can in turn contain a few dozen input capable controls.

What happens in "real life" is that a window laid out with this many controls tends to have noticibly slow scrolling and painting behavior. This is a commercial, mass market application so it must behave well on less than state of the art PCs.

The "best" approach I can come up with is to create a new window class that maintains a set of "virtual" control descriptions in its own internal array, instead of using the controls themselves, and paint to the screen directly from that array. Or, paint the control images to an offscreen bitmap and double buffer the painting to the screen. I would then have to maintain my own cache of UI controls and intercept all mouse, tab key, and other navigation actions and essentially emulate the Windows user interface functionality with regard to the notion of a focused control.

This seems insanely complex. Maybe there is some way of double buffering the surface of an ordinary Window (AKA Delphi form) itself?

Am I missing something? Any other ideas?

Bored Bystander
Wednesday, November 19, 2003

Group the controls sensibly on tabstrip tabs?

Put related functionality onto subforms and load on demand?

Justin
Wednesday, November 19, 2003

No, loading on demand won't work. The customer wants to see the entire official form laid out on the screen.

No technique of grouping the controls into containers avoids the problem that there are a sh*tload of controls.

I think what I need is an optimization of the painting behavior of the non focused controls... but how to do this escapes me.

Bored Bystander
Wednesday, November 19, 2003

Can you cheat by using one bitmap for all the text?

pdq
Wednesday, November 19, 2003

Just how many controls are we talking about?

I once had the immence displeasure of building a similar app.  It literally had thousands of controls.  There were put into scrolling containers which were then put into tabstrips and the set of tabstrips was selected with an outlook bar.

I never had any problems with the speed of repainting and scrolling.  I ran the app on a 233Mhz AMD K6 under Windows 98.  It was written in Visual Basic.  The only problem I had is that VB leaked GDI resources like an SOB  and eventually users ran out of "memory".  And under Windows 98, they had to reboot to reclaim it!

Almost Anonymous
Wednesday, November 19, 2003

pdq: that was my first thought. The only question is, would it look out of place on different version of Windows?

Of course, this also opens up the possibility of _really_ making the on-screen form look indentical to the paper form.

BTW - from past experience, it's usually a bad idea to try to make an on-screen form look like a paper form - the usability is totally different.

RocketJeff
Wednesday, November 19, 2003

Create your own windowless controls. When they get focus, they create a real underlying Windows control with a window that behaves properly. When they lose focus, they take a pixel-by-pixel screenshot of the real Windows control and then destroy the real windows control, displaying the screenshot in the meantime.

Joel Spolsky
Wednesday, November 19, 2003

I don't much about Delphi, but the first thing I would do is to ensure that I was using "Control Arrays" or in the C world - an array of similar window handles.  The next step would be to maintain two forms.  One that is displayed and one in the background.  A double buffer of sorts. For example in Visual Basic I would draw all of the controls onto two picture box controls.  A picture box is a "container control" so it can "contain" other controls.  Once I had my two picture boxes setup I would display one.  Allow the user to work with it.  When they make a change that requires a screen update.  You update the other picture box (the back buffer).  Then you simply make the visible picture box (the front buffer) invisible (i.e. picture.visible = false) and the invisible picture box (the back buffer) visible.  This may automatically give the "appearance of smoothness".  So a double buffer method would be my suggestion although I don't know how effective it would be.

Dave B.
Wednesday, November 19, 2003

I should add that you may want to also try not updating while the scroll bar is being scrolled (dynamically), but only when it is stopped. Although a double buffer may also take care of that.

Dave B.
Wednesday, November 19, 2003

I just did a quick count of one of our typical forms. The window has about 530 controls of either windowless or windowed type.

Joel S. said:
>> Create your own windowless controls.

That is basically what was about to do, BUT the main problem with this approach is that in Delphi (and no doubt all other Windoze languages) a windowless (no HWND) control can't get the focus and it can't accept keyboard input. So I would have to duplicate the focus tracking mechanism of Windows in my own stuff. Unless I'm not thinking right about it. Which seems so complex it's baroque. 

Dave B. said:

>> The next step would be to maintain two forms.  One that is displayed and one in the background.  ...  This may automatically give the "appearance of smoothness".

Dave, you may not "know much" about Delphi but the basic approach I would take would be quite similar.  That's good, thanks. This is a bit closer to what I was trying to come up with.  I'm trying to work with the Windows focus mechanism and not recreate it if at all possible...

My own mental stab at it would involve (somehow) "covering" up all controls except the currently focused one with a large bitmap that I would paint over them that displays the controls. There would be a transparent region in this painted image over only the currently focused control.

Hmm, thinking out loud... seems like I could accomplish this by playing with the invalidation rectangles.

Bored Bystander
Wednesday, November 19, 2003

FLTK does this kind of stuff very well, the way Joel described above - it can do with just one (1) window handle, and do all of the stuff internally. It's also extremely quick and optimizes drawing, so it could probably save you the need to replace bitmaps with controls.

I've tested it with 10,000 edit boxes in a flat (=no) hierarchy, on a 400Mhz PC, and it was reasonable (not blinding fast like it usually is; But definitely reasonable). With some hierarchy in structure, it could probably become snappy.

I think I saw FLTK bindings for Delphi and FreePascal a while back, but I'm not sure.

Ori Berger
Wednesday, November 19, 2003

Don't really need to worry about the focus with a double buffer.  When you flip buffers simply remember which control had the focus and then set it on the buffer you display.  Just make sure the tab order is the same for both buffers.

Dave B.
Wednesday, November 19, 2003

Good Lord! I was not going to open this link. Thank God I did. This is interesting.

Hello BB, I've nothing to say that will solve your problem, but I have to talk.


>I ran the app on a 233Mhz AMD K6 under Windows 98.  It was written in Visual Basic.  The only problem I had is that VB leaked GDI resources like an SOB  and eventually users ran out of "memory".  And under Windows 98, they had to reboot to reclaim it!

Almost Anonyamous, I've faced similar problems too in the past. On two occassions we had our VB applications fail because they were overloaded with picture boxes and other window-ed controls. And they were all snafu-ed especially on Win98. The first incident was with a start-up company that spent 3 years into creating an elaborate collaborative messaging client in Visual Basic with a server in Java and added all the goodies like mobile phone support with JMS and what-not. After three years of development on NT based systems, the company hired Siemens to test the application. Before an internal beta release, they tested on Win98 and IT DIDN'TWORK. The VB Client would start and in a matter of 10 seconds or less it would hog the CPU's ass and blacken the screen. You had no choice but to reboot. After the three-year-long development of this waste, the company closed down. Today, it is history. The first incident was of a "product" so there was no escape for them. The second incident, the guys were wiser. They were not making a product but were on a "project" where the client had only Win2K systems all over their offices. So, instead of a mourning at the loss of memory, in the second incident, the guys just added to the foremost few pages of the documentation that they did not design for Win98, and they lived happily ever after.



Joel writes:
>Create your own windowless controls. When they get focus, they create a real underlying Windows control with a window that behaves properly. When they lose focus, they take a pixel-by-pixel screenshot of the real Windows control and then destroy the real windows control, displaying the screenshot in the meantime.

Joel, I've had this problem once recently. In VB, as you'd know, the picture box control is windowed and has all the properties of a window, like a window handle, a device context, a background (brush), a pen, and basically all that follows from having a DC. So, it also receives a WM_SETFOCUS message. Whereas an Image control, is light-weight and does not exhibit any of the properties of a window. The advantage, it is light-weight but on the downside you do not get to set focus with the keyboard/Tab key if you wanted to toggle the image it was currently displaying. If we used a window-less control there then:

(1) How would I contain a window-ed control in a window-less control. I do not know if windowless/light-weight controls are control-containers too?
(2) If I did manage to contain a windowed control inside a windowless one, how would I receive a WM_SETFOCUS for the container, especially if it was windowless?
(3) If I do use a windowed control as a container, where's the economy?



>For example in Visual Basic I would draw all of the controls onto two picture box controls.  A picture box is a "container control" so it can "contain" other controls.  Once I had my two picture boxes setup I would display one.  Allow the user to work with it.  When they make a change that requires a screen update.  You update the other picture box (the back buffer).  Then you simply make the visible picture box (the front buffer) invisible (i.e. picture.visible = false) and the invisible picture box (the back buffer) visible.  This may automatically give the "appearance of smoothness".  So a double buffer method would be my suggestion although I don't know how effective it would be.

Dave, I do not know if I am right but I think that won't help BB. Hiding a control while it is still loaded in the memory isn't going to make a repaint fast. I've seen it on Win98 and it is hopeless. As long as you have an army of windows loaded, you're dead, whether you hide them or not.

Sathyaish Chakravarthy
Wednesday, November 19, 2003

You're right the double buffer method may not be the most efficient method, but you could also "scroll" a picture box within a picture box, like this:

http://www.sswltd.com/downloads/form%20drawing.zip

There are over 1000 controls on the "form".  Someone tell me if and how well it works on a 200MHZ computer (I don't have one.)  It may or may not work well.  (This is an interesting problem to solve heh.) (You need VB to run the download.)

VB is the limiting factor on the size of the form becuase it doesn't use the 32 bit scroll bars and is limited to 32768 on the max and value properties.

Dave B.
Wednesday, November 19, 2003

It works like a charm on my machine, but I have a 1.6 GHz system.

Sathyaish Chakravarthy
Wednesday, November 19, 2003

"http://www.sswltd.com/downloads/form%20drawing.zip"

I've a 400mhz machine.  It worked perfectly -- started up fast, scrolling was smooth.  Of course, this was running on Windows XP.

Almost Anonymous
Wednesday, November 19, 2003

Form_drawing.zip

on a 500mhz machine running Windows 98 - I know havn't upgraded yet.

I did start relatively fast and scrolled ok.  It did use up a heck of a lot of memory.  My System and User resources went from 60% free to 6% free.

Obviously I couldn't launch another copy.

DJ
Wednesday, November 19, 2003

It can be speeded up even more by setting the "AutoRedraw" property of picDisplay to "False" and settting the "ClipControls" property of picForm to "False".

I'd still be interested in hearing from someone with a 100 -200 mhz machine.  (Thanks to those who have tested it.)

Dave B.
Wednesday, November 19, 2003

Oh and sorry to hijack your thread BB but I though maybe  you could do something like this in Delphi.

Dave B.
Wednesday, November 19, 2003

>> " It did use up a heck of a lot of memory."

Yup.  It's like having one big picture 100 in. x 100 in.  and then viewing or scrolling over it with a small window say 4 in x 4 in.  So you have a huge bitmap taking up memory (plus 1000+ controls heh.)  This technique is used in games a lot.  The game world is drawn as a bitmap and then viewed through a smaller window as the games characters walk around the world.

Dave B.
Wednesday, November 19, 2003

THere used to be a terchnique for reducing the number of handles required by using a tabbed UI and removing the window handles from the controls on each tab as you left it.

I  know you said you want the all on one page but if it ain't practical to do....

The author of this suggests actually closing the controls on another tab and letting delf automatically recreate them.

http://www.mindspring.com/~cityzoo/tips/slimtab.txt 

theWeasel
Thursday, November 20, 2003

Them rich clients seem very easy to make :-)

son of parnas
Thursday, November 20, 2003

Maybe this - layout everything in a huge descendant of TStringGrid, set it to OwnerDraw, and respond to the OnDrawCell Event.   

The TStringGrid only uses one windows handle, and allows input by means of a TEdit that bounces around in response to the keyboard and mouse.  The DrawCell event handler is called only when it should be, in response to scrolling, or refocusing of the window, etc. 

One problem you will have to deal with is a sensible direction of the input focus.  Sone "cells" - which will not look like cells, just whitespace - will need to be read only, and you will have to shift focus to the "nearest" editable cell.

In my experience, you can do *anything* with a TStringGrid descendant using this method, and it is extremely fast. 
For example, I've drawn what look like buttons in cells, and responded to MouseUp for them. You just need to deal with the focus issues described above, and place the correct control in the focused cell.  Beyond that, of course you need something to move entered values into your domain class(es) when the focus shifts.

It is a bit of work, but performance will not be a problem. And much of the stuff you develop in the course of doing this should be reusable.       

Delphi Brain-Damaged
Thursday, November 20, 2003

*  Recent Topics

*  Fog Creek Home