Chinaunix首页 | 论坛 | 博客
  • 博客访问: 5326491
  • 博文数量: 671
  • 博客积分: 10010
  • 博客等级: 上将
  • 技术积分: 7310
  • 用 户 组: 普通用户
  • 注册时间: 2006-07-14 09:56
文章分类

全部博文(671)

文章存档

2011年(1)

2010年(2)

2009年(24)

2008年(271)

2007年(319)

2006年(54)

我的朋友

分类: C/C++

2008-08-28 12:48:31


  • (To rebuild the demo projects see )

A resizable dialog! A resizable property sheet! A resizable Wizard 97 style dialog A resizable form view

Release Notes

This is not an official release. The code is sufficiently stable to be used, but the documentation is incomplete and there are some minor issues to be solved yet. This article is also not completely synchronized with the current code. Please see the end of the article for details about this release.

ResizableLib - What is it & Why?

This class library is an attempt to make the development of resizable windows a little easier for the MFC developer and much more pleasant for the user. A resizable window in this context is a window, not necessarily top-level, that once resized either by the user or the developer, is able to automatically resize and reposition its child controls appropriately.

To make a resizable window you can use this set of "low level" classes:

CResizableGrip Implements a size grip for use in top-level resizable windows
CResizableMinMax Implements size constraints and maximized window position
CResizableLayout Implements a layout manager to handle child windows resizing and repositioning
CResizableState
CResizableWndState
CResizableSheetState
Provide basic save/restore methods and implementation for generic windows and property sheets or wizard dialogs

For the most common MFC classes you can simply replace standard MFC classes with their "resizable" counterparts:

Uses CResizableGrip, CResizableMinMax, CResizableLayout, CResizableState
Uses CResizableLayout
Uses CResizableLayout, CResizableGrip, CResizableMinMax, CResizableState
Uses CResizableLayout
Uses CResizableLayout, CResizableGrip, CResizableMinMax, CResizableState
Uses CResizableLayout, CResizableGrip, CResizableMinMax, CResizableState
Uses CResizableMinMax, CResizableState
Uses CResizableMinMax, CResizableState
Uses CResizableMinMax, CResizableState

The main reason for this choice is to ease and speed up the development of new types of resizable windows, possibly extending my small set, and to avoid the repeated code of the first versions.

I also unified all the classes in a library project that you can easily add to your workspace, in order to use resizable windows. Obviously you can still add just the files you need to your project, but using the library you have a single place to update when new releases come.

Using the library

If you already have version 1.1 or later, you need only to replace the old files with the new ones and recompile. Some minor incompatibilities should arise, due to changed implementation, please refer to the updated documentation and to the comments in the source code.

As a stand alone project

To use the class library perform the following steps:

  • Unzip the library source and put the ResizableLib directory in a place of your choice. I suggest to use the same directory where you create your Projects (e.g. "C:\MyProjects") or your common path for 3rd-party libraries
  • Go to the FileView pane and right click on the root element, or choose the "Project" menu, then select "Insert Project into Workspace..."

The ResizableLib project in the workspace

  • Navigate to the place where you put the library, select the library Project file and check "Dependency of" specifying which Project will make use of the library

Insert ResizableLib into the workspace as a dependency of your project

  • Re-activate your Project in the FileView and open the Settings dialog (also from the "Project" menu)
  • Make sure your Project is selected in the tree view and that you have selected "All Configurations" in the combo box, then click on the "C/C++" tab
  • In the "Category" combo box choose "Preprocessor" and add the library path to the "Additional include directories" edit box

Include directories in the project settings

Now you are ready to start using the library or to rebuild your project if updating from the previous versions.

Note that this description will probably change for the next releases, because I'd like to distribute the library as standalone compilable project, not to be included in the workspace.

As single files, part of your project

You just have to add the necessary files to your project, paying attention to the class dependencies. For example, if you want to use the resizable dialog class you also have to include the low level classes.

There are some preprocessor directives in the library's "stdafx.h" include file to cope with the various Platform SDK versions of the header files and MFC (6.0). You may need to copy those lines to your project's "stdafx.h" file to make things work properly.

Each archive contains one directory with the VC++ project and workspace. Since each demo's workspace has a reference to the ResizableLib project, you should place all the extracted directories in the same parent directory, so the IDE won't complain about missing files.

Creating a resizable window [out-of-date]

A good example of use for the "low level" classes is the CResizableDialog class. However, for the lazy programmer, here is a small guide.

Add Layout Management

  • First of all, derive your class from CResizableLayout
    class CMyResizableWindow : public CBaseWnd, public CResizableLayout
  • Implement GetResizableWnd pure virtual function this way
    virtual CWnd* GetResizableWnd() { return this; };
  • Override the OnSize message handler to re-arrange child windows
    void CMyResizableWindow::OnSize(UINT nType, int cx, int cy) 
    {
      CBaseWnd::OnSize(nType, cx, cy);
      
      // ...
        
      ArrangeLayout();
    }
  • Override the OnEraseBkgnd message handler to reduce flickering during resize operations
    BOOL CMyResizableWindow::OnEraseBkgnd(CDC* pDC) 
    {
      EraseBackground(pDC);
    
      return TRUE;
    }
  • You may also need to override the OnDestroy message handler to clean up the layout if you want to use the MFC object to create the associated window multiple times (because the layout is still valid until object's destruction)
    void CMyResizableWindow::OnDestroy() 
    {
      RemoveAllAnchors();
    
      CBaseWnd::OnDestroy();
    }
  • You can now call AddAnchor or AddAnchorCallback to set the child windows' layout in one of the initialization functions, such as OnInitDialog or OnInitialUpdate.

Add Min/Max Size Handling

  • Derive your class from CResizableMinMax
    class CMyResizableWindow : public CBaseWnd, public CResizableMinMax
  • Override the OnGetMinMaxInfo message handler to provide min/max information
    void CMyResizableWindow::OnGetMinMaxInfo(MINMAXINFO FAR* lpMMI) 
    {
      MinMaxInfo(lpMMI);
    }

Add Size Grip Support

  • Derive your class from CResizableGrip
    class CMyResizableWindow : public CBaseWnd, public CResizableGrip
  • Implement GetResizableWnd pure virtual function this way
    virtual CWnd* GetResizableWnd() { return this; };
  • Override the OnCreate message handler to create the grip
    int CMyResizableWindow::OnCreate(LPCREATESTRUCT lpCreateStruct) 
    {
      if (CBaseWnd::OnCreate(lpCreateStruct) == -1)
        return -1;
      
      // ...
    
      if (!CreateSizeGrip())
        return -1;
    
      return 0;
    }
  • Override the OnSize message handler to update the grip position
    void CMyResizableWindow::OnSize(UINT nType, int cx, int cy) 
    {
      CBaseWnd::OnSize(nType, cx, cy);
      
      // ...
      
      UpdateSizeGrip();
    }

Add Save/Restore Support

  • Derive your class from CResizableState
    class CMyResizableWindow : public CBaseWnd, public CResizableState
  • Implement GetResizableWnd pure virtual function this way
    virtual CWnd* GetResizableWnd() { return this; };
  • Provide your own wrapper function that saves/restores the window state, especially if you need to add custom settings, or let the user call LoadWindowRect and SaveWindowRect

Class Reference [out-of-date]

CResizableLayout

CResizableLayout::AddAnchor

void AddAnchor(HWND hWnd, CSize sizeTypeTL, CSize sizeTypeBR = NOANCHOR)
void AddAnchor(UINT nID, CSize sizeTypeTL, CSize sizeTypeBR = NOANCHOR)

Adds a child window to the layout list and set the anchor type for the top-left and bottom-right corners of the control. During a resizing operation the control's corners are kept at fixed distance from the dialog point you specify: argument's cx member is the horizontal position, while cy is vertical, in percentage.

If you have overlapping controls, you should order calls to this function from the outer controls to the inner ones, to let the clipping routines to work correctly.

A set of useful constant values, provided also for compatibility, is defined:

const CSize
  NOANCHOR(-1,-1),
  TOP_LEFT(0,0), TOP_CENTER(50,0), TOP_RIGHT(100,0),
  MIDDLE_LEFT(0,50), MIDDLE_CENTER(50,50), MIDDLE_RIGHT(100,50),
  BOTTOM_LEFT(0,100), BOTTOM_CENTER(50,100), BOTTOM_RIGHT(100,100);
I think their meaning is self-explained. Using NOANCHOR for top-left corner is not allowed, so it will generate an 'assertion failed' in the Debug version.

CResizableLayout::AddAnchorCallback

void AddAnchorCallback(UINT nCallbackID)

Adds a placeholder to the layout with the given callback ID. This is useful when the control you want to add, or its "anchorage", can change at run-time.

You must override ArrangeLayoutCallback to provide layout information.

CResizableLayout::RemoveAnchor

BOOL RemoveAnchor(HWND hWnd)
BOOL RemoveAnchor(UIND nID)
Removes a window from the layout, given its handle or control ID.

There is not a corresponding function for callback items.

CResizableLayout::RemoveAllAnchors

void RemoveAllAnchors()
Removes all the windows from the layout. Usually called in OnDestroy.

CResizableLayout::GetAnchorPosition

BOOL GetAnchorPosition(HWND hWnd, const CRect &rectParent, 
   CRect &rectChild, UINT* lpFlags = NULL)
BOOL GetAnchorPosition(UINT nID, const CRect &rectParent, 
   CRect &rectChild, UINT* lpFlags = NULL)
Calculates the size and position of an anchored control for the given parent window's size. The returned flags should be used in a call to SetWindowPos. The return value is TRUE if the first argument identifies an anchored control, FALSE otherwise.

CResizableLayout::ArrangeLayout

void ArrangeLayout()

Adjusts the size and position of the child windows you added to the layout list with AddAnchor according to the size of the parent window. Usually called in your OnSize handler.

CResizableLayout::ArrangeLayoutCallback

virtual BOOL ArrangeLayoutCallback(LayoutInfo& layout)

Override this function to provide layout information for a given callback ID. When the function is called, the layout structure contains the callback ID for which layout information is requested. You have to fill in the rest of the structure. The return value is TRUE if the structure contains valid information, FALSE otherwise.

You should test the value of the nCallbackID structure member and pass the control to the base implementation if you didn't add that ID. If you don't provide layout information, the default implementation returns FALSE and no action is taken.

When this function is called, non-callback items are in their new position after resizing, but you can't assume the same for the previous callback items.

CResizableLayout::GetTotalClientRect

virtual void GetTotalClientRect(LPRECT lpRect)

Override this function to provide the client area the class uses to perform layout calculations, both when adding controls and when rearranging layout.

This function is used by the class, and should be used by derived classes too, in place of the standard GetClientRect. It can be useful for windows with scrollbars or expanding windows, to provide the total client area, even those parts which are not visible.

CResizableLayout::ClipChildren

void ClipChildren(CDC *pDC)

Not recommended when compatibility with WinXP themes is needed.
See EraseBackground, GetClippingRegion.

Excludes child windows from the clipping area of the given DC. Usually called in your OnEraseBkgnd to have a flicker-free resizing.

CResizableLayout::GetClippingRegion

void GetClippingRegion(CRgn* pRegion)

Obtains the clipping region for the current layout. Windows that needs the parent to paint the background, such as some types of Static controls or transparent windows, belongs to the region, while the others are clipped out.

You may use this region, for example, in calls to PaintRgn or FillRgn to paint the parent's background.

CResizableLayout::EraseBackground

void EraseBackground(CDC* pDC)

Paints the layout's clipping region using the default brush for the parent window. Usually called in your OnEraseBkgnd to have a flicker-free resizing.

CResizableLayout::InitResizeProperties

virtual void InitResizeProperties(CResizableLayout::LayoutInfo& layout)

Used to set the initial resize properties of an anchored control, that are stored in the properties member of the LayoutInfo structure.

// wether to ask for resizing properties every time
BOOL bAskClipping;
BOOL bAskRefresh;
// otherwise, use the cached properties
BOOL bCachedLikesClipping;
BOOL bCachedNeedsRefresh;
The various flags are used to specify whether the resize properties (clipping, refresh) can change at run-time, and a new call to the property querying functions is needed at every layout adjustment, or they are static properties, and the cached value is used when needed.

The default implementation sets "clipping" as static, calling LikesClipping only once, and "refresh" as dynamic, causing NeedsRefresh to be called every time. This should be right for most situations, but you can override this function if needed.

Note: The default implementation of this and the following overridable functions also sends a registered message to the anchored control, giving it the opportunity to specify its resize properties, which takes precedence if supported. See the files ResizableMsgSupport.* for details.

CResizableLayout::LikesClipping

virtual BOOL LikesClipping(const LayoutInfo &layout)

Used to determine if an anchored control can be safely clipped, that is it's able to repaint its background. The return value is TRUE if clipping can occur for this window, FALSE otherwise.

The default implementation tries to identify "clippable" windows by class name and window's style. Override this function if you need more control on clipping. Note that not clipped windows often tend to flicker.

CResizableLayout::NeedsRefresh

virtual BOOL NeedsRefresh(const LayoutInfo &layout, 
   const CRect &rectOld, const CRect &rectNew)

Used to determine if a child window needs repainting when moved/resized. The return value is TRUE if this window must be repainted, FALSE otherwise.

The default implementation tries to identify windows that need refresh by class name and window's style. Override this function if you need different behavior or if you have custom child windows.

CResizableGrip

CResizableGrip::CreateSizeGrip

BOOL CreateSizeGrip(BOOL bVisible = TRUE, BOOL bTriangular = TRUE, 
   BOOL bTransparent = FALSE)
You call this function to create the size grip, usually in your OnCreate message handler. You can specify the initial visibility, whether to use a triangular shape or a transparent background. The return value is non-zero if the grip was created successfully, zero otherwise.

CResizableGrip::SetSizeGripShape

void SetSizeGripShape(BOOL bTriangular)
Changes the grip's shape to a triangle or a rectangle.

CResizableGrip::SetSizeGripBkMode

BOOL SetSizeGripBkMode(int nBkMode)
Sets the background mode of the size grip. Accepts the same values as the SetBkMode function: OPAQUE or TRANSPARENT. The return value is zero if an error occurred, non-zero otherwise.

CResizableGrip::SetSizeGripVisibility

void SetSizeGripVisibility(BOOL bVisible)

Sets the grip's default visibility. The actual state depends on the current show count, that is modified by calls to ShowSizeGrip and HideSizeGrip.

CResizableGrip::ShowSizeGrip

void ShowSizeGrip(DWORD* pStatus, DWORD dwMask = 1)

Increases the current show count, if the visibility status is not already on. Changes to the actual grip's visibility are effective only after a call to UpdateSizeGrip.

The DWORD variable pointed to by pStatus, masked by dwMask, holds the visibility status with respect to a particular condition meaningful only to the caller. The initial value of this visibility status must be zero (off) to allow to temporarily show the grip, non-zero (on) to allow to temporarily hide the grip.

Note: A single DWORD variable can hold up to 32 conditions just by changing the associated mask.

CResizableGrip::HideSizeGrip

void HideSizeGrip(DWORD* pStatus, DWORD dwMask = 1)

Decreases the current show count, if the visibility status is not already off. Changes to the actual grip's visibility are effective only after a call to UpdateSizeGrip.

Every call to ShowSizeGrip should be matched by a call to HideSizeGrip, according to the condition the visibility status represents.

CResizableGrip::IsSizeGripVisible

BOOL IsSizeGripVisible()
Used to tell if the size grip should be actually visible, according to the current show count.

CResizableGrip::UpdateSizeGrip

void UpdateSizeGrip()
Recalculates the grip's position when the containing window is resized and shows or hides the grip as needed. You usually call this function in your OnSize handler or after a call to ShowSizeGrip or HideSizeGrip.

CResizableMinMax

CResizableMinMax::MinMaxInfo

void MinMaxInfo(LPMINMAXINFO lpMMI)
Updates the MINMAXINFO structure according to current min/max settings.

CResizableMinMax::SetMaximizedRect

void SetMaximizedRect(const CRect& rc)

Sets the rectangular area that the window will occupy when maximized. Default is the standard size and position set by the system (the workspace area of the screen).

CResizableMinMax::ResetMaximizedRect

void ResetMaximizedRect()

Reverts the effect of a previous call to SetMaximizedRect, that is maximizing the window will produce the standard behavior.

CResizableMinMax::SetMinTrackSize

void SetMinTrackSize(const CSize& size)

Sets the minimum size of the window when resized. This setting does not affect the behavior of a minimize operation, which always produce the expected result.

CResizableMinMax::ResetMinTrackSize

void ResetMinTrackSize()

Reverts the effect of a previous call to SetMinTrackSize, that is the standard minimum size is set by the system.

CResizableMinMax::SetMaxTrackSize

void SetMaxTrackSize(const CSize& size)

Sets the maximum size of the dialog when resized. Default is the standard size set by the system (the workspace area of the screen). Note that this setting affects the behavior of a maximize operation and maximized size is clipped to this value by the system.

CResizableMinMax::ResetMaxTrackSize

void ResetMaxTrackSize()

Reverts the effect of a previous call to SetMaxTrackSize, that is the standard maximum size is set by the system.

CResizableState

CResizableState::LoadWindowRect

BOOL LoadWindowRect(LPCTSTR pszSection, BOOL bRectOnly)

Loads the window's size, position and state from the given section in the application's profile settings (either the registry or a INI file). If bRectOnly is TRUE the window's minimized/maximized state is not restored.

CResizableState::SaveWindowRect

BOOL SaveWindowRect(LPCTSTR pszSection, BOOL bRectOnly)

Saves the window's size, position and state to the given section in the application's profile settings (either the registry or a INI file). You should use the same value you use in the LoadWindowRect function for the bRectOnly argument.

Present and Future - Alpha Release 1.4a

I've been working on the 1.4 version for months and yet I'm not able to release anything. This is very disappointing! I had to do something... that's why I'm releasing this incomplete alpha version. There are so many improvements in the library that I don't even remember - thank god I used CVS - and I believe it's better to release something instead of waiting another six month or worse.

I passed the last weeks to start documenting the code using Doxygen, but there are still so many things to do:

  • Complete the Doxygen documentation
  • Update the articles and talk about technical details, since the class reference will have its place in the Doxygen generated files
  • Test some of the most problematic new features (disabled in 1.4a) for the full release
  • Make better demo projects to show the features and help test them

It takes too long to do all of this by myself. I would really appreciate any little help the most experienced users of this library could offer me. Otherwise you'll just have to wait longer...

Known Issues

I try to do my best to fix bugs and/or to find work-arounds for new problems, but there's surely something I forgot or I didn't have the time to look at.

  • Wizard97-style resizable dialogs still have some minor drawing issues, especially with WinXP themes enabled, but I think I got them working pretty well.
  • Flickering has been greatly reduced but it's still there.

    Some controls, such as some types of Static, the GroupBox and transparent windows, relies on the parent window to paint the background. This causes the controls to be over-painted with the background color before they can redraw themselves. And that's precisely what flickering is.

    Improvements in version 1.4a include a new feature that reduces flickering when resizing occurs on the left or top edges of a window. This may cause issues in projects that used version 1.3, because controls that would stay on the top-left corner could be left not anchored. Now you have to call AddAnchor for all the child windows.

  • Standalone documentation not ready yet.

    Since version 1.1 and before I added a Docs directory to the project, with a basic Doxygen configuration file. But the source code needs Doxygen compliant comments to actually produce something more than the class hierarchy.

    In version 1.4a I started using the Doxbar addin for VC++ 6 and now some of the core classes are documented. Just run Doxygen in the library project directory.

To Do:

  • Experiment with double-buffering techniques to completely remove flickering.

    Oz Solomonovich helped me to solve the problems with WinXP themes and clipping regions. He also made me aware of a trick you can use to force windows to draw on a specific DC, possibly enabling a double-buffered repainting (the hard way).

    In version 1.4a I was trying to use XP native double-buffering, but it leads to many issues that need testing and time to be solved. This is disabled with a preprocessor macro by default, so that the code can still be used safely.

  • Test the new features with old Windows platforms.

    I tried to keep backward compatibility with all Win32 platforms, but I don't have old Windows versions installed anymore, so I can't be sure everything is ok.

    In version 1.4a I also introduced additional code and preprocessor directives to help tailor the code to specific platforms. The principle is, when you choose a target Windows platform with the usual "WINVER and friends" macros, the library produces code that enables features specific to that platform, but degrades gracefully when run on older platforms.

  • Update the demo projects to use XP visual styles and possibly make them nicer.

    Version 1.4a includes manifest files for XP visual styles.

  • Test the library with Unicode and add Unicode build configurations to the project.

    Version 1.4a includes Unicode build configurations. It needs testing though.

  • Start using Doxygen style comments and convert existing ones, also using the documentation in this page.

    Well, this has been started in version 1.4a and the official 1.4 version won't be released until it's complete.

  • Make the library project actually produce a standalone compiled library or possibly a DLL, just like any good class library, with different output names for the Debug and Release versions.

    I'm not sure about this, but I guess it would be easier to just use the library in some project. Also help in this direction is welcome.

Plans:

  • A WTL, and possibly SDK, version of the library

    Alexander D. Alexeev has contributed a WTL port of an old version, I think 1.1, that gave me some ideas for the library. I'd like to update his port to the new version and possibly avoid repeated code. This makes me thinking about an SDK port to use as a base implementation for both the MFC and WTL versions.

    I made experiments with a template version of the library and proved the above idea is feasible, but it's surely a long-term task. Definitely after the official 1.4 version is released.

  • A standalone reference guide for the library (using Doxygen).

    This is on it's way for the next official release.

Conclusion

This class library is an effort to make a more reusable solution to the problem of resizable windows. And it's a bigger and bigger an effort as development goes on. I still have ideas to improve this library, but definitely lack the time to do it. I hate to see those fixed size dialogs, or worse those resizable windows that flicker badly, so I really hope to see more resizable windows out there and that look good!

I don't want to say "use my library", because the same things can surely be done in a better way, but at least use something that works as good as ResizableLib.

Thanks to all the happy users and especially to all those people who sent fixes or bug reports. As you can see, there are always many things to do... so contributions are definitely welcome!

This library is now distributed under the terms of , that allows for use in commercial applications. You just can't sell this work as part of a library and claim it's yours. This also means that credits are not required, but they would be nice! :)

The CVS tree is now hosted on .

Updates

11 June 2001

  • Initial public release

15 July 2001

  • Changed grip implementation, now using a scrollbar child window
  • Overriding OnNcHitTest and OnPaint no more required
  • Grip is now triangular, allowing backgrounds different from simple gray
  • Added "callback anchors" to the layout class, for property sheets integration
  • Callbacks allow adding a variable window and anchor type to the layout
  • Added Wizard97, FormView and complete PropertySheet support
  • Updated documentation for new functions

18 Aug 2001

  • Improved clipping capability: overlapping controls handled properly if AddAnchors calls are in the correct order
  • Removed second loop in ArrangeLayout, now using SWP_NOCOPYBITS
  • Fixed a bug during initial style change to enable resizing, now client rect is preserved
  • Fixed bug with transparent toolbars
  • License changed to "Artistic License", CVS repository now on SourceForge

29 Oct 2001

Version 1.1 (CVS Tag: SF_1_1)
  • Fixed a bug with CTRL+Tab while navigating Wizard dialogs
  • Added static build configurations to all projects
  • Some cosmetic stuff

13 July 2002

Version 1.2 (CVS Tag: SF_1_2)
  • Changed layout implementation, now using 2 lists and a map.
  • Changed LayoutInfo structure: added class name, removed refresh flags
  • Now NeedsRefresh function is called every time the layout is updated and not once during AddAnchor
  • Improved anti-flickering for Static controls (needs testing)
  • Added functions to support dynamic windows and to retrieve a control's position and size in the layout
  • Changed grip implementation, now using a MFC derived class
  • Fixed background problems with WinXP themes (needs testing)
  • Updated documentation (this page)

27 Oct 2002

Version 1.3 (CVS Tag: SF_1_3)
  • Changed ArrangeLayout to update all controls at once, even callbacks
  • Changed LayoutInfo structure: added resize properties support
  • Changed grip functions, allowing more customization, added transparent background
  • Added grip, window's state save/restore, and min/max size handling
  • Added classes for frame windows (SDI, MDI) to handle min/max size constraints (not working with splitters yet)
  • Added initial support for custom windows by means of a registered message
  • Fixed overlapping group boxes, incorrectly clipping siblings controls (old code had not been removed)
  • Fixed grip, now handling system parameters changes
  • Fixed the problems with scrolling windows (FormView)
  • Updated documentation (this page), added a ChangeLog to this release

Today

Version 1.4 Alpha (CVS Tag: ???)
  • Alpha Release, Incomplete
  • Please help for the official release to see the light of day.

License

This article, along with any associated source code and files, is licensed under

阅读(2342) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~