需要在按鈕下方、工具列按鈕旁、甚至視窗框架外彈出樹狀清單、下拉選單、日曆等控制項?`CPopup` 類別繼承自 `CMiniFrameWnd`,提供無標題列、無邊框的輕量級浮動視窗,可承載任意 `CWnd` 衍生控制項,支援自動定位(優先左下、空間不足時右/上對齊)、焦點管理與事件回呼介面。
class CPopup : public CMiniFrameWnd {
public:
// 顯示彈出控制項
// pControl : 要彈出的控制項 (ListBox, TreeCtrl, FormView, ActiveX...)
// pCaller : 觸發者視窗 (彈出結束時回歸焦點),可為 NULL
// rcCaller : 觸發者螢幕座標矩形 (按鈕區域、工具按鈕區域)
// pEvents : 選用事件介面指標
bool Display(CWnd* pControl, CWnd* pCaller, CRect& rcCaller, IPopupCtrlEvent* pEvents = NULL);
protected:
CPopup(); // 動態建立 (CreateObject)
virtual ~CPopup() {}
void EndPopup(bool bAbort = false, bool bSetFocus = true); // 關閉彈出
DECLARE_DYNCREATE(CPopup)
};
class IPopupCtrlEvent {
public:
virtual bool OnInit() = 0; // 彈出前初始化
virtual void OnShow() = 0; // 顯示後
virtual void OnHide(bool bAbort) = 0; // 隱藏/關閉
virtual int OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags) = 0;
virtual int OnLButtonDown(UINT nFlags, CPoint point) = 0;
virtual int OnLButtonUp(UINT nFlags, CPoint point) = 0;
virtual IPopupCtrlEvent* GetInterfaceOf(HWND hWnd) = 0; // 查詢子控制項介面
};
void CMyDialog::OnButtonPopup() {
// 1. 確保控制項已建立 (可在 OnInitDialog 建立隱藏)
if (!m_tree.m_hWnd) {
m_tree.Create(WS_CHILD | WS_VISIBLE | TVS_HASBUTTONS | TVS_LINESATROOT,
CRect(0,0,200,150), this, IDC_POPUP_TREE);
// 填充樹節點...
}
// 2. 取得按鈕螢幕座標
CRect rcBtn;
m_btn.GetWindowRect(&rcBtn);
// 3. 動態建立 CPopup 並顯示
CPopup* pPopup = (CPopup*)CPopup::CreateObject();
pPopup->Display(&m_tree, this, rcBtn, this); // this 實作 IPopupCtrlEvent
}
bool CMyDialog::OnInit() {
// 彈出前準備:展開樹節點、選取項目等
return true;
}
void CMyDialog::OnShow() { /* 可播放動畫聲音 */ }
void CMyDialog::OnHide(bool bAbort) {
if (!bAbort) {
// 使用者正常選取,取得結果
HTREEITEM hSel = m_tree.GetSelectedItem();
// ...
}
}
int CMyDialog::OnKeyDown(UINT nChar, ...) {
if (nChar == VK_ESCAPE) return 1; // 1=關閉彈出, 0=繼續處理
return 0;
}
// 其他滑鼠事件同理...
IPopupCtrlEvent* CMyDialog::GetInterfaceOf(HWND hWnd) {
return (hWnd == m_tree.m_hWnd) ? this : NULL;
}