|
|||||||||||||||||||||
|
|||||||||||||||||||||
|
Announcements
Chapters
Services
Feature Zones
|
IntroductionThis is the first of three articles about creating Windows controls. Article 1 (this) will explain how to design a good control, number 2 goes into coding details by creating a new control step-by-step and article 3 shows the Q&A aspects of controls and how to test and validate Windows controls. Here we go… Designing a ControlI will try to give you some tips how to design a better control. I have been doing GUI development for almost 10 years now and all of this writing is out of my own experience. Many controls, which are available on websites like CodeProject, are nice pieces of code but not useable at all. At a first glance they seem to fit perfectly into the project you develop and do all that you need. You download it and with a little tweaking here and there, you build it into your application and the Boss is happy that you finished the project in time. Unfortunately, this is seldom the end of the story when you develop a commercial application. Even if the new control passes your Q&A division (you have one, don’t you?), eventually the support team will report problems. The most commonly encountered problems with custom controls are:
All of this does really happen and it will happen to your customers. The biggest mistake a developer can make is testing the application on a developer’s computer. Never ever do this. Take the computer of the secretary, your aunt’s old 486, or even install a clean OS without the latest service packs and just choose a different display scheme. Also, test other OS languages – remember there is a world outside your office. Tip: most developers do not have the resources to cleanstall (clean-install) various operating systems on various computers, there is a simple, yet efficient solution: emulators like VM-Ware (http://www.vmware.com [^]). They have a reasonable price and are perfectly suited for a developer’s testing environment. These mistakes are the result of the fact that (almost) all these controls are developed for a specific purpose. End of the line. Few programmers go the extra mile to complete the design and deliver a fully functional control that will pass all tests. Even many commercial GUI libraries fail here. Further reading on controls and control design: http://msdn.microsoft.com/library/en-us/shellcc/platform/commctls/common/user.asp http://msdn.microsoft.com/nhp/default.asp?contentid=28000443 Choose Your Weapons WiselyWhen you intend to write your own control, stop before you write a single line of code and figure out if you really need to create a custom control or if it can be solved with a standard Windows control or a combination of them. Windows provides a rich variety of controls, which are suited for almost any purpose and they are 99% bug free. Your custom control will reach this level only after many, many hours of use, testing and debugging. Often it requires a change in design to achieve the required results with the standard controls, but normally it is worth the change. You gain stability and standard conformance. Your users do not need to learn how to use your new control; they are familiar with the Windows ones. If you think that your users must have a button with a different background color and another font, go out and ask your users. They may like the colorful appearance, but if you confront them with a standard solution, they will agree that the standard solution is better to use. Remember: Usability and customization seldom go hand in hand. Ok, so you decided you must have an extraordinary whatever-control? Fine, lets go ahead and figure out how to do it right. Figure out what is the target audience, the average user, of your application. This determines how you represent your data, how you label it, how it has to look.
Ok, back to our design. Now that you know your audience, you can make a clear decision on the representation. Always keep in mind, Joe Averageuser does not want a cool looking control, but one that is easy to handle, that he understand it without pressing F1 or reading the manual. If it satisfies these requirements and is still cool looking, then you are one-step further on your bosses raise list. A word about the keyboard: handle keys like Windows does – up is up and down is down. If you use the Enter key to select an item, your users will not understand it. MSDN has nice lists on keys and their suggested use. Before you start coding, make a mockup of your control. Fire up your favorite paint program and sketch the control. Place the bitmap in your application. See how it looks and ask others what they think of it. Do they understand what it is? How would they use it? If your dummy survives this simple test, you can – almost – start coding. “Captain, focus ahead”Now it is time to think about the controls usage, its navigation. In most cases, you know instinctively how to use it with a mouse. However, what about the keyboard? Many users (that includes me) like to use the keyboard for navigation, just because it is faster, just because you have to type something in a nearby field, or simply because the computer does not have a mouse attached. Keyboard navigation always includes showing a focus. The focus is a visual feedback to indicate which part of your control the user is interacting with. This is the user’s only clue what he is doing. Again, look at the Windows controls to see how a focus is shown. The button shows a dotted rectangle and changes border, an edit control shows a caret at the insert position, a menu shows a colored selection. Imitate this behavior. Joe Averageuser is used to it.
If you do not get it right at first, don’t worry – even Microsoft doesn’t always. (MS: How are we supposed to use the dropdown function of the “Open” button in Word XP’s File Open dialog?) Always ask others for their opinion about your control and if they understand it. Watch them using it. Don’t forget that you have to remove the focus if your control or the application looses focus. There are certain windows messages, which you should handle because they may change focus state or style. To make things more complicated, these messages vary on different Windows versions. Focus management means keeping some state variables around and up-to-date. This can be a tricky part of your controls logic. Depending on the complexity of your control you might even need to show focus in various forms – a caret when the user interacts with text, a dotted line on a button-type area, etc. Another important visual feedback for focus – mouse focus this time – is hot tracking. Hot tracking means changing the visual appearance of an item or area when the user moves (hovers) the mouse over it. A good example, are the buttons in the toolbar of Internet Explorer. Here, the search button normal and hot-tracked.
When you implement features like this, adhere to the Windows settings and capabilities of the installed OS and IE version. This will make your control appear much more “Windows like” because it acts and reacts like all other standard controls. The Windows API functions Ma’, why is it so big?Do you calculate the size of your control in pixel? Do you draw at certain locations? Don’t do it. When you place your control on a dialog, it is measured in dialog units. Dialog units scale depending on the display settings. When the user chooses a big default font, the dialog will be bigger and so will your control. Try it; select “Large Fonts” in the display properties. How does your control look now? Always calculate sizes and locations relative. If you are going to display simple iconic graphics, consider using a TrueType font. Windows also uses a font to display the symbols on any window. The Minimize, Maximize, Close icons but also the scroll arrows are just letters from a custom font. Outlook uses a custom font for the attachment, priority and other symbols. Why? Because it scales. Bitmaps don’t scale. And it is yet ten times easier to output text than display a bitmap or even WMF file. If you create items, which relate to Windows items, mimic
Windows. Use When you must display bitmaps or icons then be very careful with
transparency and background colors. Often programmers forget that the
background color of an icon is not gray. You can notice this in many
pre-Windows 2000 applications when Microsoft used a different gray for the
background of dialogs and windows. When you run these applications with altered
colors (or under Windows 2000/XP) then you notice that the icons have a
different gray. So, make the background color transparent and set the transparency color correctly. You can easily check if you got it right, by changing the color of the 3D Objects in the display settings. Never assume a color because the user will change it. Always use HWND, WPARAM and LPARAMYou cannot post virtual functions. How are programmers going to use your control? Through a class, which implements the control, you will answer. Not exactly the right answer. I know, it’s the common and
most convenient way. But just look at the Windows controls – MFC programmers
often forget that there is no function named It takes some effort to implement a message-based interface instead of a class-based interface. But it has several advantages:
Let us have a look at the conventional implementation: class CMyControl { // ... some declarations here void SetColor(RGB c) { m_color = c; Invalidate(); } RGB GetColor() { return m_color; } private: RGB m_color; }; That is all that is. End of story. While this is the easiest
and fastest implementation, it is also the most inflexible one. Even when you
declare the Now, the same in a message-based implementation: // in the main header file MyControl.h #define MC_SETCOLOR (MC_BASEMSG + 1) #define MC_GETCOLOR (MC_BASEMSG + 2)
// in the class definition file MyControl_impl.h class CMyControlImpl { // ... some declarations here void SetColor(RGB c) { SendMessage(MC_SETCOLOR, 0, (LPARAM)c); } RGB GetColor() { return SendMessage(MC_GETCOLOR); } // if this is using MFC we have here the message handler declarations afx_msg LRESULT OnMsgSetColor(UINT, WPARAM, LPARAM); private: RGB m_color; };
// in the class implementation file MyControl_impl.cpp BEGIN_MESSAGE_MAP(CMyControlImpl, CWnd) //{{AFX_MSG_MAP(CMyControlImpl) ON_MESSAGE(MC_SETCOLOR, OnMsgSetColor) ON_MESSAGE(MC_GETCOLOR, OnMsgGetColor) //}}AFX_MSG_MAP END_MESSAGE_MAP() LRESULT CMyControlImpl::OnMsgSetColor(UINT, WPARAM c, LPARAM) { m_color = (RGB)c; Invalidate(); } LRESULT CMyControlImpl::OnMsgGetColor(UINT, WPARAM, LPARAM) { return (LRESULT) m_color; } This is quite a lot more code to write. But any developer using this control will need only the first file MyControl.h which contains all message definitions. If your control is complex or you have enough time and motivation at hand, then you provide a wrapper class and/or macros. #define MyControl_SetColor (hwndMC, c) \ (int)SNDMSG((hwndMC), MC_SETCOLOR, (WPARAM)(c), 0L) #define MyControl_GetColor (hwndMC) \ (int)SNDMSG((hwndMC), MC_GETCOLOR, 0, 0L) class CMyControl { public: void SetColor(RGB c) { MyControl_SetColor(m_hWnd, c); } RGB GetColor() { return MyControl_GetColor(m_hWnd); } } This is exactly what MFC is doing (with a little more error
checking in-between) with all the standard Windows controls. Have a look at the Using this method, you gain all advantages mentioned before. It is more work because of all the definitions and extra header files. In the end, you cannot avoid this extra step when you want to create commercial quality controls. If you ever wrote controls, you will know the pain of debugging GUI code. With the message-based approach things get easier – attach Spy++ or even better Winspector[^] to your control and see the messages flow. You can easily spot any wrongly sent message, any out-of-range parameter and any return value. One last point to enhance your control: do not call exposed functions directly. Let me demonstrate this with our sample. Assume we have another function called Of course, for more complex functions you might need to define structures which are passed by pointer to the control. Even if you have
to pass more then 2 parameters you will need to squeeze them into a structure already or you manage to use Who Would “Print” a Control?Well, most probably not many people do this. I agree that “print” is not the best choice of words for the purpose. Something like “render” would have been more appropriate. By now, you may have realized that I am talking about the To implement these messages you only have to move your painting code into a separate function, which takes a DC as input parameter. The difference between Implementing these messages is required to support some of the newer Windows API functions like Terminal Services AwarenessTerminal services are no longer a buzzword but reality for many companies and all Windows XP users. They are available by default on any Windows XP installation just that they are now called Remote Desktop. Also available as Windows NT 4.0 Terminal Services and Citrix Terminal Server and probably under some other names. Do not underestimate how widely used this is in enterprises. This is definitely a feature you want to add to your windows control. There are few things to consider when you plan to make your control TS aware: the painting code must be optimized and mouse interaction should be minimized. By detecting TS sessions and handling the With careful planning, TS awareness can be added without breaking compatibility to Win9x systems. The Modern Architecture ThingyWith each new Windows version, Microsoft adds some new features to the existing controls, new windows messages, new styles. Most of these features are easy to implement if you plan your control carefully. One of these new things is the UI status feedback. You can turn it on or off in Display Settings / Appearance / Effects. It is titled “Hide underlined letters for keyboard navigation until I press the Alt key”. The context help of this item already reveals that there is more to it than they tell with this clunky title. This special feature is hidden behind three new windows messages: Another big thing is Windows XP’s Theme support. Fully supporting themes is lots of work, if your control uses default windows elements (buttons, scrollbars, dropdown indicators, etc) as parts of the window, you must implement Theme support. There is no way around. Otherwise, your control looks awful between all the themed controls in your customer’s application. Fortunately, there are a few helper classes on sites like CodeProject, which you can easily use in your implementation. ConclusionWriting good controls is hard work. Designing a controls interface takes time. A control that you created for your own application will never be a complete solution. It just implements what you needed. The next programmer using your control may have very different requirements to the interface or environment. If you intend to publish your control, review the interface and code carefully and add functionality that is missing. Any If your control uses constants or
That’s all for now. I will update this article based on your feedback and any further ideas. I hope you will read on when the next part of this series is written. | ||||||||||||||||||||