首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> VC >> 關於VC++

MFC 動態建立控件完整方案:事件響應、INI 持久化與拖曳設計

     編輯:關於VC++

架構設計

採用三層架構分離關注點:

  • 全域資料類別 _globalData:管理控制項陣列、INI 路徑、拖曳狀態
  • 控制項屬性基類 _myControl:封裝共通屬性 (標題、矩形、類型)
  • 具體控制項包裝類:CMyButton、CMyEdit、CMyLabel 分別繼承 CButton、CEdit、CStatic

動態建立核心函數模板


template<class T>
void addControl(T& parent, int nType, int nReadID = 0)
{
    int nIndex = nReadID ? nReadID : ++globalData.nCount;
    _myControl* pCtrl = globalData.myControl[nIndex];
    pCtrl->nType = nType;
    
    // 共用字型建立
    HFONT hFont = CreateFont(12, 0, 0, 0, 400, 0, 0, 0, ANSI_CHARSET,
        OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY,
        DEFAULT_PITCH | FF_DONTCARE, _T("新細明體"));
    
    CRect rc = pCtrl->rect;
    switch (nType) {
    case MYBUTTON:
        pCtrl->myButton.Create(pCtrl->strCaption, WS_VISIBLE|WS_CHILD|BS_PUSHBUTTON,
            rc, &parent, IDB_MYCONTROL + nIndex);
        SendMessage(pCtrl->myButton, WM_SETFONT, (WPARAM)hFont, TRUE);
        break;
    case MYEDIT:
        pCtrl->myEdit.Create(WS_VISIBLE|WS_CHILD, rc, &parent, IDB_MYCONTROL + nIndex);
        pCtrl->myEdit.ModifyStyleEx(0, WS_EX_CLIENTEDGE, SWP_DRAWFRAME);
        // 修正 3D 邊框尺寸差異
        pCtrl->myEdit.SetWindowPos(NULL, rc.left-2, rc.top-2, 
            rc.Width()+4, rc.Height()+4, NULL);
        SendMessage(pCtrl->myEdit, WM_SETFONT, (WPARAM)hFont, TRUE);
        break;
    case MYLABEL:
        pCtrl->myLabel.Create(pCtrl->strCaption, WS_VISIBLE|WS_CHILD|SS_NOTIFY,
            rc, &parent, IDB_MYCONTROL + nIndex);
        SendMessage(pCtrl->myLabel, WM_SETFONT, (WPARAM)hFont, TRUE);
        break;
    }
}

事件響應:繼承類別中直接用 ClassWizard 加入


// CMyButton.cpp
void CMyButton::OnClicked() {
    AfxMessageBox(_T("你點擊了動態按鈕!"));
}
// CMyEdit, CMyLabel 同理可加入 EN_CHANGE, STN_CLICKED 等訊息

關鍵優勢:繼承 MFC 控制項類別並使用 ClassWizard,訊息映射自動生成,無需手寫巨集,管理極簡單。

INI 持久化:儲存與讀取完整流程


// 取得相對父視窗座標
template<class T, class P>
CRect GetRelativeRect(T& ctrl, P& parent) {
    CRect rcCtrl, rcParent;
    ctrl.GetClientRect(&rcCtrl);
    parent.GetClientRect(&rcParent);
    parent.ClientToScreen(&rcParent);
    ctrl.ClientToScreen(&rcCtrl);
    return CRect(rcCtrl.left - rcParent.left, rcCtrl.top - rcParent.top,
                 rcCtrl.right - rcParent.left, rcCtrl.bottom - rcParent.top);
}

// 儲存
void SaveConfig(CWnd& wnd) {
    GetAllControlRects(wnd); // 更新最新座標
    CFile file(globalData.strIniPath, CFile::modeCreate | CFile::modeWrite);
    file.Close(); // 清空
    WritePrivateProfileString(_T("AppInfo"), _T("Count"), strCount, iniPath);
    for (int i=1; i<=count; i++) {
        CString sec; sec.Format(_T("Ctrl%d"), i);
        WritePrivateProfileString(sec, _T("Type"), strType, iniPath);
        WritePrivateProfileString(sec, _T("Caption"), caption, iniPath);
        WritePrivateProfileString(sec, _T("Left"), strLeft, iniPath);
        // Top, Right, Bottom 同理...
    }
}

// 讀取
void LoadConfig(CWnd& wnd) {
    ClearAllControls(); // 刪除舊控制項物件
    int nCount = GetPrivateProfileInt(_T("AppInfo"), _T("Count"), 0, iniPath);
    for (int i=1; i<=nCount; i++) {
        // 讀取屬性...
        addControl(wnd, nType, i); // 指定索引重建
    }
}

拖曳移動控制項


BOOL CMainDlg::PreTranslateMessage(MSG* pMsg)
{
    if (pMsg->message == WM_LBUTTONDOWN && globalData.bDragMode) {
        CWnd::FromHandle(pMsg->hwnd)->SendMessage(WM_SYSCOMMAND, SC_MOVE | HTCAPTION, 0);
        Invalidate(TRUE);
        return TRUE;
    }
    return CDialog::PreTranslateMessage(pMsg);
}

利用 WM_SYSCOMMAND + SC_MOVE 系統內建視窗移動邏輯,無需手寫滑鼠追蹤迴圈,並可擴充 SC_SIZE 實現縮放。

總結

此方案解決動態控制項三大難題:建立、事件、持久化。採用繼承類別承接訊息映射、模板函數統一建立邏輯、INI 區段化儲存,代碼結構清晰且易擴充新控制項類型。完整範例在 VC6.0 編譯通過。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved