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

《VC++技術內幕》學習筆記(九):模態對話框與通用控件實戰 (上) — 體育彩票選號器完整開發

     編輯:VC++

Day 9 核心主題:模態對話框與通用控件 (上)

對話框兩大類型

MFC 對話框開發標準六步驟

  1. **資源編輯器**繪製對話框模板,自動更新 .rc。
  2. **ClassWizard** 建立 CDialog 派生類,關聯模板資源 ID。
  3. **ClassWizard** 添加成員變數 (控件值)、DoDataExchange (DDX)、驗證函數 (DDV)。
  4. **ClassWizard** 為按鈕/控件添加消息處理函數 (BN_CLICKED, EN_CHANGE...)。
  5. 編輯特殊初始化 (OnInitDialog) 與消息處理函數;關閉時記得調用 OnOK。
  6. 視圖/主框架代碼中構造對話框對象,調用 DoModal() (模態) 或 Create() (非模態)。

實戰專案:北京體育彩票 36 選 7 選號器 (Dialog Based)

界面設計 (資源編輯器)

  • 刪除預設 OK/Cancel 按鈕。
  • 添加:靜態文字「電腦體育彩票選號:」、7 個 Edit 控件 (IDC_EDIT1~7)、1 個 Button「選號」(IDC_BUTTON1)。
  • 每個 Edit 控件屬性:**Number** (只允許數字)、**Read Only** (防用戶手改)。

ClassWizard 綁定變數 (int 型)

Control ID      Variable    Type
IDC_EDIT1       m_num1      int
IDC_EDIT2       m_num2      int
...
IDC_EDIT7       m_num7      int

添加按鈕點擊處理

Message Maps → IDC_BUTTON1 → BN_CLICKED → OnButton1。

核心算法:隨機數生成、去重、排序

void CGuessDlg::OnButton1() {
    // 1. 初始化 1~36 候選池
    int pnum[36];
    for (int i = 0; i < 36; ++i) pnum[i] = i + 1;

    int num[7] = {0};
    int temp;

    // 2. 選 7 個不重複號碼
    for (int i = 0; i < 7; ++i) {
        // 每次重新播種:當前時間作種子,保證序列不重複
        srand((unsigned)time(NULL));
        temp = rand() % 36;           // 0~35
        while (pnum[temp] == 0) {     // 已被取走,重選
            srand((unsigned)time(NULL));
            temp = rand() % 36;
        }
        num[i] = pnum[temp];          // 記錄號碼 (1~36)
        pnum[temp] = 0;               // 標記已用
    }

    // 3. 簡單冒泡排序 (升序)
    for (int pass = 1; pass < 7; ++pass) {
        for (int i = 0; i < 7 - pass; ++i) {
            if (num[i] > num[i+1]) {
                temp = num[i];
                num[i] = num[i+1];
                num[i+1] = temp;
            }
        }
    }

    // 4. 賦值給綁定變數
    m_num1 = num[0]; m_num2 = num[1]; m_num3 = num[2];
    m_num4 = num[3]; m_num5 = num[4]; m_num6 = num[5];
    m_num7 = num[6];

    // 5. 更新界面 (FALSE = 變數 → 控件)
    UpdateData(FALSE);
}

關鍵技術點解析

  1. 隨機數種子:srand((unsigned)time(NULL)) 在每次 rand() 前調用,利用當前秒數作種子,**避免程式多次運行產生相同序列** (偽隨機數特性)。
  2. 去重策略:候選池陣列 pnum,選中後置 0,下次遇到 0 即重選。空間換時間,簡單高效。
  3. 排序:7 個元素用冒泡/選擇排序即可,代碼極短,無需 qsort/std::sort。
  4. DDX 機制:UpdateData(FALSE) 觸發 DoDataExchange → DDX_Text 將 m_numX 格式化為字串寫入 Edit 控件。
    反向 UpdateData(TRUE) 將控件文字解析為整數存入變數 (含 DDV_MinMaxInt 驗證)。

DoDataExchange 自動生成代碼

void CGuessDlg::DoDataExchange(CDataExchange* pDX) {
    CDialog::DoDataExchange(pDX);
    DDX_Text(pDX, IDC_EDIT1, m_num1);
    DDX_Text(pDX, IDC_EDIT2, m_num2);
    DDX_Text(pDX, IDC_EDIT3, m_num3);
    DDX_Text(pDX, IDC_EDIT4, m_num4);
    DDX_Text(pDX, IDC_EDIT5, m_num5);
    DDX_Text(pDX, IDC_EDIT6, m_num6);
    DDX_Text(pDX, IDC_EDIT7, m_num7);
}

運行效果與優化建議

  • 點擊「選號」按鈕,7 個 Edit 瞬間顯示排序後的隨機號碼。
  • **優化 1**:運行時滑鼠變「沙漏」 — CWaitCursor wait; (構造即變沙漏,析構自動恢復)。
  • **優化 2**:添加進度條控件 (CProgressCtrl) 顯示選號進度 (下篇實現)。
  • **優化 3**:用 std::random_device + std::mt19937 + std::uniform_int_distribution (C++11) 替換 rand/srand,統計質量更高。

雷神心法:從「能跑」到「好用」

  1. **最小可行性產品 (MVP)**:先跑通核心邏輯 (隨機、去重、排序、顯示)。
  2. **體驗打磨**:沙漏游標、進度條、輸入驗證、鍵盤快捷鍵 (Enter 觸發選號)、工具提示。
  3. **健壯性**:檢查 time(NULL) 返回值、處理 rand() 返回 0 邊界、防止重複點擊 (按鈕禁用/標誌位)。
  4. **可維護性**:將算法提取為獨立函數 GenerateLotteryNumbers(int count, int max, vector& out),便於單元測試、換算法、移植平台。

下一篇預告

Day 10:模態對話框與通用控件 (下) — 攔截 ESC/Enter 關閉、自定義對話框背景/控件顏色 (WM_CTLCOLOR)、添加進度條 (CProgressCtrl) 顯示選號進度、總結對話框改進模式。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved
類型行為典型例子
模態關閉前禁止操作同程式其他窗口開啟檔案、列印、屬性設定
非模態可同時操作其他窗口尋找/取代、工具箱、屬性頁