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. –
將您的答案標記爲接受使用事件監聽器,以保持類的解耦(我選擇的路線來解決這個問題)。謝謝! –