Navigation Sidebar

 

Image 1

Table of Contents

Introduction

Recently, a client requested an Outlook 2003 style navigation bar for his WTL project. Not finding a suitable control on CodeProject in WTL, I decided to create one. I hope you find this control useful for your own projects; appropriate attribution of credit would be appreciated.

An Outlook 2003 navigation bar is equivalent to a tabbed window system that changes two views at once. It can accommodate any number of "tabs", although it works best with a relatively mediocre amount.

This project requires:

  • WTL 7.5 or later.
  • atlgdix.h from www.viksoe.dk.
  • GDI+ from Microsoft.

The Features

Outlook Style View Switching -- Outlook takes care of a tremendous number of details to appropriately handle tooltips, window resizing, sticky splitter, etc. I have tried to implement each of these capabilities, and in a few cases, improved on them.

Chevron Menu -- Like the Outlook version, there is a chevron menu (it is, however, not themed...this may be added in a future version) on the command bar. Buttons that don't fit on the command bar or as a big button are placed on it below the Navigation Pane Options.

Navigation Pane Options --The Navigation Pane Options dialog box lets you swap and inactivate buttons from your view in order to customize your layout to what you like best. The Navigation Pane Options is fully operational (except that the hotkey system works like all the other Windows listboxes of the same kind...and that's not a good thing), with hovering and appropriate selection. Its list view is custom, and can be accessed in ChecklistView.h.

Theme Support -- This program has basic theme support. Not much to say here except that it changes based on the Windows theme, which you may find useful for applying extra eye-candy to your program. Most of this is assimilated from Roger Headrick's theming code.

How to Use It

The Necessary Code

This code is to be put after you've made all the status bars and have updated the layout. First, we need to get the client rect, set our parameters that must be set before the window is created, and then create the window.

C++
RECT ClientRect;
GetClientRect(&ClientRect);

// Set the button parameters. You must set
// these before you create the navwindow.
// If you don't, don't expect it to work ;)

// The button height is the height
// of the big buttons and the command bar
m_navwindow.SetButtonHeight(31);
// The small button width is the width
// of the little buttons on the command bar.
m_navwindow.SetSmallButtonWidth(24);

// Drop in our control.
m_hWndClient = m_navwindow.Create(m_hWnd, ClientRect, 
               L"Nav_Main", WS_CHILD | WS_VISIBLE | 
               WS_CLIPSIBLINGS | WS_CLIPCHILDREN);

Then, we set the distance from the top that the buttons start popping back (so that you don't cover up anything important). This can be set at any place in the program, as it always defaults to zero if not set (in fact, you can edit the tabbed buttons so that it sets the pop-back differently based on what view it's showing). The following for loop is to define the windows and their HWNDs that are used by the tabbed buttons.

C++
// Set the distance from the top of the client
// rect of the window where the buttons start popping back. 
// If not set or set to 0, the buttons will pop back
// when the buttons are pushed past the top of the window.
m_navwindow.SetPopBack(150);

// For the sake of example, there is only one sample window for all the buttons. 
// If you want a different window for each button (you probably do), 
// you will want a different line and class for each window in your Mainframe.
// As these are for example, the following two lines
// and the for loop are to be erased and replaced with whatever you want.
button_view.resize(9);
button_view_top.resize(9);
for (size_t view_increment = 0; view_increment < 
            button_view.size(); view_increment++)
{
    button_view[view_increment].Create(m_navwindow, rcDefault, 
           NULL, WS_CHILD | WS_VISIBLE | 
           WS_CLIPSIBLINGS | WS_CLIPCHILDREN);
    button_view_top[view_increment].Create(
           m_navwindow.m_HorizontalSplitter, rcDefault, 
           NULL, WS_CHILD | WS_VISIBLE | 
           WS_CLIPSIBLINGS | WS_CLIPCHILDREN);
        
    std::wostringstream vertical_caption;
    vertical_caption << L"Vertical " << view_increment;
    button_view[view_increment].window_caption = vertical_caption.str();
    button_view[view_increment].is_horizontal = false;
        
    std::wostringstream horizontal_caption;
    horizontal_caption << L"Horizontal " << view_increment;
    button_view_top[view_increment].window_caption = 
                            horizontal_caption.str();
    button_view_top[view_increment].is_horizontal = true;
    button_view_top[view_increment].pop_back_amount = 
             m_navwindow.m_HorizontalSplitter.pop_back_y;
}
// Don't erase anything past here...it contains
// the rest of the parameters needed ;)

We then set the font of the buttons, and then, most importantly, create them. The first argument is the button's caption, the second the image path it uses as a big button, the third the image path it uses as a button on the command bar, and the last two, respectively, are the HWNDs to what will appear on the right hand side across from the vertical bar and what will appear on the top side across the horizontal bar.

Then, we set the splitter position of the vertical splitter to the chosen width, and the default buttons to what we want to see... a value of 5 means that we will be seeing the first four buttons added with AddButtonWithView on top of a command bar showing the other five. For an alternate example, if you SetDefaultButtons(4), you will see three big buttons on top of a command bar holding the other six.

C++
// Set the button font.
m_navwindow.SetButtonFont(L"Arial", 8, FontStyleBold);

// Add all of our buttons to our button view.
// Use a for loop if you have
// the necessary arrays of strings.
m_navwindow.AddButtonWithView(L"Print", 
            L"RolloutIcons\\Print_2.tif", 
            L"RolloutIcons\\Print_2_Small.tif", 
            button_view[0], button_view_top[0]);
m_navwindow.AddButtonWithView(L"Add", 
            L"RolloutIcons\\Add_2.tif", 
            L"RolloutIcons\\Add_2_Small.tif", 
            button_view[1], button_view_top[1]);
m_navwindow.AddButtonWithView(L"Edit", 
            L"RolloutIcons\\Edit_2.tif", 
            L"RolloutIcons\\Edit_2_Small.tif", 
            button_view[2], button_view_top[2]);
m_navwindow.AddButtonWithView(L"Delete", 
            L"RolloutIcons\\Delete_2.tif", 
            L"RolloutIcons\\Delete_2_Small.tif", 
            button_view[3], button_view_top[3]);
m_navwindow.AddButtonWithView(L"Search", 
            L"RolloutIcons\\Search_2.tif", 
            L"RolloutIcons\\Search_2_Small.tif", 
            button_view[4], button_view_top[4]);
m_navwindow.AddButtonWithView(L"Back", 
            L"RolloutIcons\\Back_2.tif", 
            L"RolloutIcons\\Back_2_Small.tif", 
            button_view[5], button_view_top[5]);
m_navwindow.AddButtonWithView(L"Forward", 
            L"RolloutIcons\\Forward_2.tif", 
            L"RolloutIcons\\Forward_2_Small.tif", 
            button_view[6], button_view_top[6]);
m_navwindow.AddButtonWithView(L"Save", 
            L"RolloutIcons\\Save_2.tif", 
            L"RolloutIcons\\Save_2_Small.tif", 
            button_view[7], button_view_top[7]);
m_navwindow.AddButtonWithView(L"Exit", 
            L"RolloutIcons\\Exit_2.tif", 
            L"RolloutIcons\\Exit_2_Small.tif", 
            button_view[8], button_view_top[8]);

// Set this to be where the vertical
// splitter will be located on your window.
m_navwindow.SetSplitterPos(242);
// Set this to be 1 higher than the buttons
// you want to show initially. If none at all,
// put 1. If not even the command bar, put 0.
m_navwindow.SetDefaultButtons(5);

Miscellaneous Notes

  • If you don't set the default buttons, it'll default to 0.
  • If you don't specify a larger image-path, there will just be text on the button, and it will align with the left-hand side of the button view.
  • If you don't specify a smaller image-path, the buttons will still show up on the command bar, but they won't be visible to the human eye without moving the mouse there.
  • The ButtonView_Tabbed is to be treated as a vertical splitter.
  • Icons won't be painted if the size of the bar is too small.
  • There are several pieces of this program that, while some are optional, some are absolutely needed. The absolutely needed pieces of code are located in the ButtonView Items and Required Items filters/folders. The ButtonView Tabbed version is there for practical use, and serves to demonstrate how you can use my code (through inheritance and some necessary overriding) and add features to it if you wish. However, if you choose to use a different class implementing the ButtonView template, you must supply support for a horizontal and vertical splitter, or else the ButtonView simply won't work.
  • If you wish to make a list of buttons that don't act as tabs and just do functions instead of opening views, overwrite the function OnSelect. It takes one integer for an argument, and it is the ID of the button, with 0 being the topmost one. You can then check and run various functions based on what i is.

Documentation

The following is the documentation of functions you may use from ButtonView_Tabbed. There are other functions, but they serve as internal functions. These are the ones you would use.

  • AddButtonWithView(std::wstring caption, std::wstring imagepath, std::wstring imagepath_small, HWND right_side, HWND top_side) -- Adds a button to the bottom of the 'list' of buttons. caption is self-explanatory, and carries onto the button's tooltip when it's on the command bar; imagepath and imagepath_small are self-explanatory, and right_side and top_side are the handles to the windows that will appear on the right side and above of the button view.
  • AddButton(std::wstring caption, std::wstring imagepath, std::wstring imagepath_small) -- ButtonView core function. Same as AddButtonWithView, but doesn't add with views. AddButtonWithView calls this and then adds a view in its own lines.
  • SetButtonFont(std::wstring type, Gdiplus::REAL size, INT style) -- Sets the style of the buttons (type is font, size is font size, and style is the add-on to the button, such as bold).
  • SetDefaultButtons(int i) -- Sets the number (i - 1, as command bar is considered a button under this setting) of buttons that you will see when the window starts up.
  • SetPopBack(int pop_back_amount) -- Sets the distance in pixels away from the top of the client window. Defaults to 0, the top of the client window, if not set.
  • SetButtonHeight(int i) -- Sets the pixel height of the big buttons and the command bar.
  • SetSmallButtonWidth(int i) -- Sets the pixel width of the buttons on the command bar. Chevron always stays at 24 pixel width.
  • OnSelect(int i) -- ButtonView core function. The function that gets called when a button is selected. Purposefully, this is blank and is to be overridden in every class that uses the ButtonView template. If you set the ButtonView's button_is_selected parameter equal to i, you will receive the selected tab effect on the Print button. If not, then clicking the button will just be like clicking a normal button...it'll flash when you click it, and then revert back to normal upon release.
  • Swap_Buttons(int i, int j) -- ButtonView core function. It is the function that is called when changes have been made to the Navigation Pane Options, where i is the old button and j is the new button. It is intentionally left blank by default, as the necessary swapping is done beforehand. You should override this and add the necessary lines for swapping the functions and views and stuff of the buttons. ButtonView_Tabbed overrides and replaces it with code to switch the view of the buttons.

Credits

I would like to thank Viksoe for his atlgdix.h for the off-screen drawing (no flicker for the buttons or my sample exe and its sample windows), Roger Headrick for his color theming code, Terry Denham for his snap splitter, and finally, Firefox and tom-cat.com for supplying the icons I was able to change and fit into my sample program. I also would like to thank anyone that I've forgotten to thank in name.

History

  • Version 1.0 -- Navigation sidebar released!
posted @ 2022-12-13 14:46  小风风的博客  阅读(27)  评论(0编辑  收藏  举报