Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

A generalized CUpdateUI based on a policy design

0.00/5 (No votes)
21 Jan 2002 1  
A policy based implementation of UI updating that accumulates and executes UI changes.

Introduction

Type list is one of more amazing things ever made in C++, weird thing! That's why I prefer C++ to any. Are you familiar with type lists? If not then you have to click here [1] and read. Andrei will explain you better and you'll never regret of it. I've overhauled the previous edition using the type list and was excited of how it made easy a lot of things.

There is the CUpdateUI class template in WTL that manages UI elements updating, and I like that. In most of cases it's what we need. The thing that made me thinking of another implementation is the way the CUIUpdate may be extended to have more functionality. You would have one option in deriving from CUpdateUI or CUpdateUIBase. Seems that would be mess and awkward. I thought it would be nice to have a CUpdateUI generalization based on a policy design. The benefits of such a model are:

  • it's extendable; to add new feature, we never should rewrite it wholly; 
  • it's adjustable; having put another policy in set of policies, we get more functionality; 
  • it has high performance. 

I attempted to do so.

Implementation

I thought it looks as that:

class CMainFrame :
    public CFrameWindowImpl<CMainFrame>,
    public CUIStates<StatePolicy1, StatePolicy2, ...>,
    public CUIUpdate<CMainFrame, UIPolicy1, UIPolicy2, ...>
{
    ...
};

The CUIStates is a class template that accumulate changes of UI which the CUIUpdate has to apply. Each StatePolicyNs supports a specific kind of change. The CUIUpdate shows changes that have been made in the CUIStates. It has a set of UIPolicyNs; each of them supports specific kind of user interface. Both the CUIStates and the CUIUpdate can have an arbitrary set of policies. You may specify up to 32 policies. If you took a look at the definition of the CUIStates you would see something like that:

struct nil {}

template
<
	typename S01 = nil, typename S02 = nil, typename S03 = nil,
	typename S04 = nil, typename S05 = nil, 
	...
	typename S31 = nil, typename S32 = nil
> 
class CUIStates : 
	...

What is last ellipsis (...)? It's a stuff that derives CUIStates from parameters SXX which are not nil. That's where the type list works and it's a kind of magic. It could be a subject for another article. Well, a compiler has lots of work to exclude from run-time an unused code. The CUIUpdate has been done in the same manner.

Here is the WTL wizard equal code that updates "Toolbar" and "Status Bar" menu's items:

class CMainFrame : 
    public CUIStates<CStyle>,
    public CUIUpdate<CMainFrame, CUICommand<CMainFrame> >,
    ...
{
    ...
    virtual BOOL OnIdle()     {
        UIUpdate();    // CUIUpdate method

        return FALSE;
    }

    BEGIN_MSG_MAP(CMainFrame)
        MESSAGE_HANDLER(WM_CREATE, OnCreate)

        // Does the same as CHAIN_MSG_MAP(CUpdateUI<CMainFrame>)

        // Updates popup menus and checks accelerators 

        CHAIN_MSG_MAP(CUICommand<CMainFrame>)

        COMMAND_ID_HANDLER(ID_VIEW_TOOLBAR, OnViewToolBar)
        COMMAND_ID_HANDLER(ID_VIEW_STATUS_BAR, OnViewStatusBar)
        ... 
    END_MSG_MAP()

    LRESULT OnCreate(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, 
                     BOOL& /*bHandled*/) {
        ...

        // CStyle methods

        UIAddStyle(ID_VIEW_TOOLBAR, CStyle::Enable|CStyle::Visible|CStyle::Checked);
        UIAddStyle(ID_VIEW_STATUS_BAR, CStyle::Enable|CStyle::Visible|CStyle::Checked);

        ...
        return 0;
    }
    LRESULT OnViewToolBar(WORD /*wNotifyCode*/, WORD /*wID*/, 
                          HWND /*hWndCtl*/, BOOL& /*bHandled*/) {
        bool bVisible = !::IsWindowVisible(m_hWndToolBar);
        ::ShowWindow(m_hWndToolBar, bVisible ? SW_SHOWNOACTIVATE : SW_HIDE);
        UISetCheck(ID_VIEW_TOOLBAR, bVisible);    // CStyle method

        UpdateLayout();
        return 0;
    }
    LRESULT OnViewStatusBar(WORD /*wNotifyCode*/, WORD /*wID*/, 
                            HWND /*hWndCtl*/, BOOL& /*bHandled*/) {
        bool bVisible = !::IsWindowVisible(m_hWndStatusBar);
        ::ShowWindow(m_hWndStatusBar, bVisible ? SW_SHOWNOACTIVATE : SW_HIDE);
        UISetCheck(ID_VIEW_STATUS_BAR, bVisible);    // CStyle method

        UpdateLayout();
        return 0;
    }
    ...
};

The CStyle policy supports flags of command IDs such as enable/disable, visible/hidden, checked/unchecked. The CUICommand supports updates of popup menus and handles accelerator keys. Thus, the CUICommand requires that the CUIState has the CStyle; but what would be if it had not? Nothing, the CUICommand just does nothing and I ensure you that the CUICommand doesn't have such an ugly code as:

if(Does_CUIState_Have_CStyle()) {
	// translates accelerators

}
else {
	// does nothing

}

All of job done by a compiler at compile-time, there's no such "if". Do you think it's not right? Then you are in bad place, please, close your browser.

If we wanted to manage text labels of items we should add a CText state policy and use its methods to be in control of UI changes:

class CMainFrame :
public CUIStates<CStyle, CText>,
public CUIUpdate<CMainFrame, CUICommand<CMainFrame> >,
...

To manage toolbar's buttons just rewrite:

class CMainFrame :
public CUIStates<CStyle, CText>,
public CUIUpdate<CMainFrame, CUICommand<CMainFrame>, CUIToolBar<CMainFrame> >,
...

To manage a status bar: CUIStatusBar, a rebar: CUIReBar.

If you had your own control you might implement CUIUpdate policy and thus add the control to that model. Moreover we may specify our own state policy and enlarge functionality of existing CUIToolBar, CUIStatusBar.

How to add new state and UI policies

The sample project shows how to implement state policy called CParts that manages number and widths of status bar parts and how to make CUIStatusBar extended to change the status bar according to changes in CParts.

State policy is supposed to have three function:

bool dirty(ui_type id) const
void clean(ui_type id)
void set_default_value()

dirty and clean support dirty flags; the same as in WTL's CUpdateUI. set_default_value changes values of state to default ones if state has them; it's made for easy MDI interface support. That's enough to compile it with CUIStates. The others methods are at discretion, they are going to be used at changes applying code of UI elements.

The requirement for UI policy is that it should have a following templated function:

template<class State>
void update_state(HWND hwnd, const State& state) {}

Thus, if an UI policy doesn't support specified kind of a state policy then it does nothing; if we wanted it to support, say, CText then it should have function as following:

void update_state(HWND hwnd, CText& state) {
...
}

For details, you should see a source code.

A couple of words about efficiency. Both the CUIStates and the CUIUpdate made by aggressively using of generic programming techniques; they have no virtual function. So performance is very high. For comparison, release size of binary generated by WTL wizard is the same. There're two implementations, specifying #include <map> before #include <wtlx/ui_update.hpp> we switch to STL library from CSimpleMaps and CStrings.

I think using such techniques will make a future of C++ libraries. What is WTL going to be? Will see, I just hope that isn't a MFC2. What is the reason of some things as CString? To make MFC programmers feel themselves comfortable? Well, well, well...

Acknowledgements

[1] Andrei Alexandrescu. Generic<Programming>: Typelists and Applications

[2] Andrei Alexandrescu. Generic<Programming>: Mappings between Types and Values.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here