tag:C++,弹出菜单,列表框,wxWidgets
/* Create by zyzx
* Created 2008-06-24
* Modified 2008-07-17
*/
VS系列IDE在管理工程文件的列表框中右击不同类型对象会弹出不同的操作菜单,这是个不错的用户体验方式,那么他是如何实现的呢?
目标:在自定义的列表框(继承标准列表框)右击不同对象,会弹出不同操作菜单。
分析:列表框一般只会保存每一个项目的显示字串和图标,我们能跟据用户操作(左击,右击等)得到当前的ItemID(这个ID是唯一的)。那么我们将这个ID与实际对应的对象做一个关联,当用户右击时,检索关联列表取得对象类型,根据这个类型弹出适应于此类型的菜单。如下实现是基于wxWidgets的wxTreeCtrl类。
一、先定义我们要处理对象
class StrObjProject; //对应VS工程
class StrObjFolder; //对应VS的文件夹
class StrOjbFile; //对应VS的cpp,h等文件
typedef std::map<long, StrObjProject*> MapProject; //存储工程对象结构
typedef std::map<long, StrObjProject*>::iterator MapProjectIt;
typedef std::map<long, StrObjFolder*> MapFolder; //存储文件夹对象结构
typedef std::map<long, StrObjFolder*>::iterator MapFolderIt;
typedef std::map<long, StrOjbFile*> MapFile; //存储文件对象结构
typedef std::map<long, StrOjbFile*>::iterator MapFileIt;
typedef std::map<int, wxMenu*> MapMenu;
typedef std::map<int, wxMenu*>::iterator MapMenuIt;
enum{VS_UNKNOWN = 0, VS_PROJECT, VS_FOLDER, VS_FILE} //对象样式,分别对应不同菜单
class VsObj // 此基类的作用在于包装统一的结构
{
public:
virtual ~VsObj(){}
virtual int GetType(){ }
......
}
class VsProject : public VsObj // VS_PROJECT类,存储工程结构
{
public:
virtual ~VsProject (){}
virtual int GetType() { return VS_PROJECT;}
virtual MapProject* GetMap(){ return m_MapProject;}
......
protected:
MapProject* m_MapProject;
}
class VsFolder : public VsObj // VS_FOLDER类,存储文件夹结构
{
public:
virtual ~VsFoldert (){}
virtual int GetType() { return VS_FOLDER;}
virtual MapFolder* GetMap(){ return m_MapFolder;}
......
protected:
MapFolder* m_MapFolder;
}
class VsFile : public VsObj // VS_FILE类,存储文件(.cpp,.h)之结构
{
public:
virtual ~VsFile (){}
virtual int GetType() { return VS_FILE;}
virtual MapFile* GetMap(){ return m_MapFile;}
......
protected:
MapFile* m_MapFile;
}
typedef std::map<long, VsObj*> MapVsObj;
typedef std::map<long, VsObj*>::iterator MapVsObjIt;
二、将这些对象集成到自定义的控件中去
class VsTreeCtrl : public wxTreeCtrl
{
DECLARE_DYNAMIC_CLASS( VsTreeCtrl )
DECLARE_EVENT_TABLE()
public:
......
protected:
//* 创建弹出菜单
wxMenu* CreatePopupMenuUnknown( const wxString& title );
wxMenu* CreatePopupMenuProject( const wxString& title );
wxMenu* CreatePopupMenuFolder( const wxString& title );
wxMenu* CreatePopupMenuFile( const wxString& title );
void InitPoupMenu(){
m_Menu.insert( std::make_pair( VS_UNKNOWN, CreatePopupMenuUnknown( ... ) ) );
m_Menu.insert( std::make_pair( VS_PROJECT, CreatePopupMenuProject( ... ) ) );
m_Menu.insert( std::make_pair( VS_FOLDER, CreatePopupMenuFolder( ... ) ) );
m_Menu.insert( std::make_pair( VS_ VS_FILE, CreatePopupMenuFile( ... ) ) );
}
void PopUpMenuFromType( int type, wxPoint pt ) { // 根据类型弹出对应的菜单
MapMenuIt* it= m_Menu.find( type );
if ( it != m_Menu.end() ){
this->PopupMenu( it->second, pt);
}
}
protected:
void InitData(){
m_VsObj.insert( std::make_pair( VS_PROJECT, new VsProject () ) );
m_VsObj.insert( std::make_pair( VS_FOLDER, new VsFolder () ) );
m_VsObj.insert( std::make_pair( VS_FILE, new VsFile () ) );
}
int GetDataType( long ID ){
// 遍历整个m_VsObj中存储的数据
//返回VS_PROJECT,VS_FOLDER,,VS_FILE等。
}
protected:
virtual bool DoAdd( long ID, StrObjProject* obj);
virtual bool DoAdd( long ID, StrObjFolder*obj);
virtual bool DoAdd( long ID, StrOjbFile* obj){
MapVsObjIt it = m_VsObj.find( VS_FILE );
if ( it != m_VsObj.end() ){
VsObj* obj = it->second;
VsFile* vsFile = wxDynamicCast( obj , VsFile ); //类型转换,还要检查。
vsFile->GetMap()->insert( std::make_paer( ID, obj ) );
}
}
protected:
//* 事件处理-- Sys Mouse Event
void OnLMouseDown(wxMouseEvent& event);
void OnRMouseDown(wxMouseEvent& event){
wxTreeItemId id = HitTest( event.GetPosition() );
if( id )
{
//wxPtrToUInt函数将一个void*转换成Int类型,这里将列表框创建的wxTreeItemId::m_pItem指针的值作为标示对象的唯一ID。
PopUpMenuFromType( GetDataType( (long)wxPtrToUInt( id.m_pItem) ), event.GetPosition() );
}else{
PopUpMenuFromType( TYPE_MU_UNKNOWN, event.GetPosition() );
}
event.Skip();
}
void OnRMouseUp(wxMouseEvent& event);
void OnRMouseDClick(wxMouseEvent& event);
protected:
MapMenu m_Menu; // <= 存储全部的菜单
MapVsObj m_VsObj; // <= 存储全部的对象
};
上面的类只写了关键的部分。基本思想还是先取得列表框对象ID编号(即列表框生成GDI对象指针地址),然后查询这个ID得到其类型,根据此类型做出相应的反馈(此处为弹出不同的菜单)。
扩展:可以将 m_Menu和 m_VsObj “外包”给专门的管理类来统一协调,做到我们的TreeCtrl列表框与实际的数据对象无关。
也可以扩展此类思想,比如Windows桌面右键菜单等等,跳出列表框的限制。
还可以考虑列表框对象较多时,如何加速查找。
这里只是提供一个思路,其实对于列表框而言,实现类似的效果还有更好的处理手法。一般而言,它会提供一个指针,用户只需要访问此指针的信息即可做出判断。。