GUI应用程序具有以下窗口层次:最好的办法从另一个嵌套窗口访问嵌套的窗口
CMainWnd <---- main window
CLeftPane CRightPane <---- left and right panes (views)
CLDlg1 CLDlg2 CRDlg1 CRDlg2 <---- controls container windows (dialogs)
... ... ... ... <---|
CCtrl1 ... ... CCtrl2 <---|- controls
... ... ... ... <---|
父窗口是孩子以上。
每个子窗口都是父wnd类的受保护成员。
每个子窗口类都有一个引用/指向其父窗口的指针。
窗格是自定义控件占位符(视图)。
所有的控件都是标准的MFC控件。
某些CCtrl1
的事件处理程序需要更改CCtrl2
(例如设置其文本)。达到此目的的最佳方法是什么? 从另一个窗口访问嵌套在窗口层次结构的一个分支中的窗口,嵌套在另一个窗口层次结构中的最佳方式是什么?
我在这里发布两个解决方案。
解决方案1层
- 所有儿童的对话框(控制容器)有:
- 公共干将其返回父对话框和
- 公共方法是对子女的控制执行某些操作(所以儿童控件隐藏)
- 公共干将其返回父对话框和
- root win道指具有返回窗格
MainWnd.h公共干将:
#include "LeftPane.h"
#include "RightPane.h"
class CMainWnd
{
public:
CLeftPane& GetLeftPane(){return m_leftPane;}
CRightPane& GetRightPane(){return m_rightPane;}
...
protected:
CLeftPane m_leftPane;
CRightPane m_rightPane;
...
};
LeftPane.h:
#include "MainWnd.h"
#include "LDlg1.h"
#include "LDlg2.h"
class CLeftPane
{
public:
CLeftPane(CMainWnd& mainWnd) : m_mainWnd(mainWnd){};
CMainWnd& GetMainWnd() {return m_mainWnd;}
...
protected:
CMainWnd& m_mainWnd;
CLDlg1 m_LDlg1;
CLDlg2 m_LDlg2;
...
};
RightPane.h:
#include "MainWnd.h"
#include "RDlg1.h"
#include "RDlg2.h"
class CRightPane
{
public:
CRightPane(CMainWnd& mainWnd) : m_mainWnd(mainWnd){};
CMainWnd& GetMainWnd() {return m_mainWnd;}
CRDlg2& GetRDlg2() {return m_RDlg2;}
...
protected:
CMainWnd& m_mainWnd;
CRDlg1 m_RDlg1;
CRDlg2 m_RDlg2;
...
};
LDlg1.h :
#include "LeftPane.h"
#include "Ctrl1.h"
class CLDlg1
{
public:
CLDlg1(CLeftPane& leftPane) : m_leftPane(leftPane){}
protected:
CLeftPane& m_leftPane;
CCtrl1 m_ctrl1;
void OnCtrl1Event();
};
LDlg1.cpp:
#include "LDlg1.h"
#include "RDlg2.h"
void CLDlg1::OnCtrl1Event()
{
...
CString strText("test");
m_leftPane.GetMainWnd().GetRightPane().GetRDlg2().SetCtrl2Text(strText);
....
}
RDlg2.h:
#include "RightPane.h"
#include "Ctrl2.h"
class CRDlg2
{
public:
CRDlg2(CRightPane& rightPane) : m_rightPane(rightPane){}
void SetCtrl2Text(const CString& strText) {m_ctrl2.SetWindowText(strText);}
protected:
CRightPane& m_rightPane;
CCtrl2 m_ctrl2;
};
案例我这里是类似于一个在this问题描述:公共干将链(GetMainWnd().GetRightPane().GetRDlg2()...
)使用访问所需的嵌套对象。 CLDlg1知道违反Law of Demeter的CRightPane和CRDlg2。
溶液2
在这种情况下CMainWnd
包含执行深度嵌套控制操作的所有必要的方法:
这种情况可以通过移动SetCtrl2Text(...)
方法在层次结构上水平,其中所述被避免。
MainWnd.h:
#include "LeftPane.h"
#include "RightPane.h"
class CMainWnd
{
public:
void SetCtrl2Text(const CString& strText);
...
protected:
CLeftPane m_leftPane;
CRightPane m_rightPane;
...
};
MainWnd.cpp:
void CMainWnd::SetCtrl2Text(const CString& strText)
{
m_rightPane.SetCtrl2Text(strText);
}
RightPane.h:
#include "MainWnd.h"
#include "RDlg1.h"
#include "RDlg2.h"
class CRightPane
{
public:
CRightPane(CMainWnd& mainWnd) : m_mainWnd(mainWnd){};
CMainWnd& GetMainWnd() {return m_mainWnd;}
void SetCtrl2Text(const CString& strText);
...
protected:
CMainWnd& m_mainWnd;
CRDlg1 m_RDlg1;
CRDlg2 m_RDlg2;
...
};
RightPane.cpp:
LDlg1.cpp:
#include "LDlg1.h"
void CLDlg1::OnCtrl1Event()
{
...
CString strText("test");
m_leftPane.GetMainWnd().SetCtrl2Text(strText);
....
}
RDlg2.h:
#include "RightPane.h"
#include "Ctrl2.h"
class CRDlg2
{
public:
CRDlg2(CRightPane& rightPane) : m_rightPane(rightPane){}
void SetCtrl2Text(const CString& strText);
protected:
CRightPane& m_rightPane;
CCtrl2 m_ctrl2;
};
RDlg2.cpp:
void CRDlg2::SetCtrl2Text(const CString& strText)
{
m_ctrl2.SetWindowText(strText);
}
这个隐藏窗口的层次结构,从它的客户,但这种方法:
- 使得
CMainWnd
类拥挤的公共方法对所有嵌套控件采取一切行动;CMainWnd
为所有客户的行为提供主开关板的服务; - CMainWnd和每个嵌套对话已多次在公开接口
这些方法哪种办法最好?或者,这个问题还有其他解决方案/模式吗?
溶液3
又一解决方案将使用包含用于特定事件源对象的事件处理程序的接口类。目标对象的类实现了这个接口,并且事件源和处理器是松散耦合的。这可能是要走的路吗?这是GUI中的常见做法吗?
编辑:
解决方案4 - 发布/订阅模式
在以前的解决方案事件源对象保持对事件处理程序的引用,但如果存在多个事件侦听器的出现问题(两个或更多个类需要在事件上更新)。发布者/订户(观察者)模式解决了这个问题。我对这种模式进行了一些研究,并提出了如何实现将事件数据从源传递给处理程序的two versions。这里的代码是基于第二个:
Observer.h
template<class TEvent>
class CObserver
{
public:
virtual void Update(TEvent& e) = 0;
};
通知。ħ
#include "Observer.h"
#include <set>
template<class TEvent>
class CNotifier
{
std::set<CObserver<TEvent>*> m_observers;
public:
void RegisterObserver(const CObserver<TEvent>& observer)
{
m_observers.insert(const_cast<CObserver<TEvent>*>(&observer));
}
void UnregisterObserver(const CObserver<TEvent>& observer)
{
m_observers.erase(const_cast<CObserver<TEvent>*>(&observer));
}
void Notify(TEvent& e)
{
std::set<CObserver<TEvent>*>::iterator it;
for(it = m_observers.begin(); it != m_observers.end(); it++)
{
(*it)->Update(e);
}
}
};
EventTextChanged.h
class CEventTextChanged
{
CString m_strText;
public:
CEventTextChanged(const CString& strText) : m_strText(strText){}
CString& GetText(){return m_strText;}
};
LDlg1.h:
class CLDlg1
{
CNotifier<CEventTextChanged> m_notifierEventTextChanged;
public:
CNotifier<CEventTextChanged>& GetNotifierEventTextChanged()
{
return m_notifierEventTextChanged;
}
};
LDlg1.cpp:
// CEventTextChanged event source
void CLDlg1::OnCtrl1Event()
{
...
CString strNewText("test");
CEventTextChanged e(strNewText);
m_notifierEventTextChanged.Notify(e);
...
}
RDlg2.h:
class CRDlg2
{
// use inner class to avoid multiple inheritance (in case when this class wants to observe multiple events)
class CObserverEventTextChanged : public CObserver<CEventTextChanged>
{
CActualObserver& m_actualObserver;
public:
CObserverEventTextChanged(CActualObserver& actualObserver) : m_actualObserver(actualObserver){}
void Update(CEventTextChanged& e)
{
m_actualObserver.SetCtrl2Text(e.GetText());
}
} m_observerEventTextChanged;
public:
CObserverEventTextChanged& GetObserverEventTextChanged()
{
return m_observerEventTextChanged;
}
void SetCtrl2Text(const CString& strText);
};
RDlg2.cpp:
void CRDlg2::SetCtrl2Text(const CString& strText)
{
m_ctrl2.SetWindowText(strText);
}
LeftPane.h:
#include "LDlg1.h"
#include "LDlg2.h"
// forward declaration
class CMainWnd;
class CLeftPane
{
friend class CMainWnd;
...
protected:
CLDlg1 m_LDlg1;
CLDlg2 m_LDlg2;
...
};
RightPane.h:
#include "RDlg1.h"
#include "RDlg2.h"
// forward declaration
class CMainWnd;
class CRightPane
{
friend class CMainWnd;
protected:
CRDlg1 m_RDlg1;
CRDlg2 m_RDlg2;
...
};
MainWnd.h:
class CMainWnd
{
...
protected:
CLeftPane m_leftPane;
CRightPane m_rightPane;
...
void Init();
...
};
MainWnd.cpp:
// called after all child windows/dialogs had been created
void CMainWnd::Init()
{
...
// link event source and listener
m_leftPane.m_LDlg1.GetNotifierEventTextChanged().RegisterObserver(m_rightPane.m_RDlg2.GetObserverEventTextChanged());
...
}
该解决方案解耦事件源(CLDlg1
)和处理器(CRDlg2
) - 他们不知道对方的存在。
考虑到以上解决方案和GUI的事件驱动性质,我的原始问题正在演变为另一种形式:如何将事件从一个嵌套窗口发送到另一个窗口?
我的直觉告诉我,这实际上是唯一正确的做法,但我不确定MFC中是否有一些快捷方式。在MFC中使用自定义消息不是类型安全的(从WPARAM/LPARAM中转换),我宁愿将事件与Windows中的事件分开,最好使用通用方法 - 我将其添加为解决方案4. –
将您的答案标记为接受使用事件监听器,以保持类的解耦(我选择的路线来解决这个问题)。谢谢! –