65.9K
CodeProject 正在变化。 阅读更多。
Home

选项卡控件和分割器 - 混合使用

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.89/5 (12投票s)

2003年12月31日

4分钟阅读

viewsIcon

104699

downloadIcon

4016

WTL 类,可以在您的 SDI 应用程序中为子视图添加选项卡和分割。

Tab controls and Splitters - mixing together

引言

尽管有很多关于选项卡视图和分割器视图使用及实现的例子,但我找不到一个能够将它们混合在一起的解决方案。因此,我开发了一个WTL类来简化使用选项卡和分割视图的SDI应用程序的构建。起初,我尝试使用标准的窗口选项卡控件。但后来我无法抗拒使用由 Bjarke ViksoeCCustomTabCtrl)和 Daniel BowenCDotNetTabCtrl)实现的令人惊叹的选项卡控件实现。

它的作用

该类具有三个主要功能。通过拖放操作,您可以

  • 更改选项卡控件中选项卡的位置
  • 将选项卡式客户端视图拖放到其他客户端视图窗格中
  • 将选项卡式客户端视图排列在另一个选项卡式窗格的边缘(与其余的垂直或水平分割)

使用代码

实现以上所有功能的主要类是SplitPane。在您的应用程序中使用它应该不会遇到任何困难。首先,您需要在编译器可以访问的地方包含以下文件:

atlgdix.h 额外的GDI/USER包装器。由Bjarke Viksoe编写
CustomTabCtrl.h 一个帮助实现具有不同外观的选项卡控件的基类。由 Bjarke Viksoe 编写。由 Daniel Bowen 进行了一些改进。
DotNetTabCtrl.h CCustomTabCtrl派生的选项卡控件,旨在模仿VS.Net中的选项卡(MDI选项卡、解决方案浏览器选项卡等)。由 Daniel Bowen 编写。
DockTabPane.h 选项卡控件和选项卡窗格实现。使用以上所有包含文件。
DockTabSplitPane.h 分割窗格实现。包含DockTabPane.h

然后使用ATL/WTL向导创建一个SDI应用程序项目。您需要关闭“Minimize CRT use in ATL”配置选项。通过以下步骤将Split Pane类添加为其中的主视图:

  • 包含DockTabSplitPane.h,例如,在stdafx.h文件中。
  • 为您的CMainFrame类添加SplitPane类成员,并继承自CallBackListener接口类。
class CMainFrame
    : public CFrameWindowImpl< CMainFrame>
    , public CUpdateUI< CMainFrame>
    , public CMessageFilter
    , public CIdleHandler
    , public DockSplitTab::CallBackListener
    ....
    DockSplitTab::SplitPane mainPane;
    ....
public:
    CMainFrame();

CallBackListener类的目的是向所有者或父对象提供Split Pane通知。我认为这比win32消息更简单。除了继承自MainFrame类之外,您还可以设计一个特殊的类适配器,实现SplitPane与其所有者之间的所有通信需求。所以,请不要忘记像下面这样初始化mainPane,使其指向这个类。

...
CMainFrame::CMainFrame()
    : mainPane( this, true)
    ...
{}
...

mainPane构造函数的第二个参数为Split Pane包含的所有选项卡窗格设置顶部的选项卡控件栏。要完成mainPane的设置,请在CMainFrame::OnCreate事件处理程序中放置Split Pane窗口创建代码,并将其分配给m_hWndClient属性。

LRESULT CMainFrame::OnCreate(UINT, WPARAM, LPARAM, BOOL&) {
    ...
    this->m_hWndClient = this->mainPane.create( this->m_hWnd);
    ...
}

就是这样。我们已经完成了Split Pane作为应用程序主视图窗口的定义。不要忘记为CallBackListener接口定义函数处理程序。如何做到这一点,请参见演示项目

我在所有定义中使用DockSplitTab命名空间。当然,您可以 대신使用以下代码行,而不是为每个名称使用命名空间说明符。

using namespace DockSplitTab;

SplitPane

公共方法

// creates a new Split Pane window with parentWnd and rect parameters
HWND create( parentWnd, rect);

// adds the new client view window to Split Pane.
// The new client view is added into the focused tab pane
bool append( caption, clientViewWnd, tooltip, imageIndex);

// detaches the client view window from Split Pane. 
// This method changes a parent window of the client window view 
// to the Split Pane parent one.
bool detachClientView( clientViewWnd);

// returns the client view window that receives the keyboard focus
HWND focusedClientView();

// sets the keyboard focus to a tab pane at the specified position
bool setFocusTo( x, y)

// sets the keyboard focus to the specified client view window
bool setFocusTo( clientViewWnd) {

// returns the number of client view windows in Split Pane
int getClientViewCount();

// returns the rectangle of tab pane, if any, is at a specified position
bool getClientViewRect( point, &rect);

// move the client view window (sourceWnd) to the same 
// tab pane where the specified client view window is located
void moveClientView( HWND sourceWnd, HWND targetWnd);

// move all client view windows to the specified split pane.
void moveClientViewsTo( SplitPane* targetPane);

// splits source client view window (sourceWnd) with the target client view
void splitClientView( sourceWnd, targetWnd, targetArea);

// set and get Image List
void setImageList( HIMAGELIST imgList);
HIMAGELIST getImageList();

CallBackListener接口类

此类提供SplitPane与其所有者类之间的通知接口。

// triggered when client view client view wnd has gained the keyboard focus
virtual void clientActivate( childWnd, clientViewWnd) = 0;

// triggered when client view client view wnd 
// got doble mouse click on the tab button
virtual void clientDblClick( childWnd, clientViewWnd) = 0;

// triggered the close button was pushed for the client view client
virtual void clientCloseClick( childWnd, clientViewWnd) = 0;

// drag and drop notifications
virtual void dragStart(  childWnd, clientViewWnd, x, y, keysPressed) = 0;
virtual void dragOver(   childWnd, clientViewWnd, x, y, keysPressed) = 0;
virtual void dragDrop(   childWnd, clientViewWnd, x, y, keysPressed) = 0;
virtual void dragCancel( childWnd, clientViewWnd) = 0;

// performs the drag and drop tracking with drag and drop notifications
void trackDragAndDrop( HWND hWnd, POINT startPoint, 
    bool lockWindowUpdate = false);

里面有什么

我不相信“黑箱”的概念。我认为了解内部原理总是更好的,所以我试图解释SplitPane类内部的结构。基本构建块是Pane类,它可以在您的应用程序中独立使用,就像SplitPane那样,如果您只需要选项卡窗格以及更改选项卡视图选项卡位置的功能。SplitPane拥有选项卡窗格对象的层次结构,使用VSpliterHSpliter类,它们是标准WTL::CSplitterWindowImpl<>类模板的特化。您可以使用Spy++实用程序进行检查。

class ClientProperties - 提供三个客户端视图属性:标题、工具提示和图像索引。
Pane::get方法的必需参数。
class TabControlItem - 继承自CCustomTabItem类,并包含客户端视图窗口句柄成员。
class TabControl - 特化了CDotNetTabCtrlImpl<>类模板。
class Pane - 选项卡控件和与选项卡控件相关的所有客户端视图窗口的容器类。
class RectTracker - 绘制拖放操作期间跟踪矩形的辅助类。
class VSplitter - 垂直分割窗口。特化了CSplitterWindowImpl<>类模板。
class HSplitter - 水平分割窗口。特化了CSplitterWindowImpl<>类模板。
class SplitPane::DragContext - 继承自RectTracker类。

接下来做什么

显然,这需要某种序列化机制。还有其他想法吗?;-)

© . All rights reserved.