• Thứ Hai, 22/02/2010 08:12 (GMT+7)

    Tạo ActiveX và ứng dụng bằng VC++

    Trong phương pháp lập trình hướng đối tượng, một chương trình giải quyết vấn đề nào đó là 1 tập hợp gồm nhiều đối tượng độc lập nhưng khi cần thì chúng tương tác lẫn nhau để cùng hoàn thành chức năng của ứng dụng. Mỗi đối tượng của phần mềm không nhất thiết là của riêng phần mềm đó, nó có thể được dùng chung bởi nhiều phần mềm khác nhau. Việc xây dựng đối tượng có thể độc lập với việc xây dựng ứng dụng, ứng dụng có thể dùng lại những đối tượng đã có sẵn. Nhiều năm về trước, Microsoft đã giới thiệu 2 loại đối tượng được quản lý ở cấp hệ thống để các ứng dụng dùng chúng dễ dàng nhất, đó là đối tượng dạng COM (Component Object Model) và đối tượng dạng ActiveX Control.

    ActiveX Control là đối tượng có giao diện giống như Button, TextBox, ListBox,... nhưng chưa có sẵn trong môi trường lập trình, chúng được người lập trình tạo thêm, nhưng việc dùng chúng thì y như việc dùng các điều khiển sẵn có của môi trường lập trình như Button, TextBox, ListBox,...

    Sau đây chúng tôi xin giới thiệu qui trình điển hình để xây dựng 1 ActiveX đơn giản và xây dựng 1 ứng dụng sử dụng nó bằng môi trường lập trình VC++.

    ActiveX mà chúng tôi muốn xây dựng là dàn đèn điều khiển giao lộ gồm có 3 đèn xanh, vàng, đỏ. Nó có các nhóm chân giao tiếp với thế giới bền ngoài như sau:

    - nhóm chân thuộc tính: có 1 chân 'Color' miêu tả trạng thái màu hiện hành.

    - nhóm chân tác vụ: có 1 chân 'Next()' cho phép dàn đèn chuyển trạng thái màu theo thứ tự xanh à vàng à đỏ à xanh...

    - nhóm chân sự kiện nhập có 2 chân 'LButtonDown' và 'Change' để xử lý sự kiện ấn chuột trên dàn đèn và xử lý việc container thay đổi trạng thái.

    - nhóm chân sự kiện xuất: có 5 chân 'Go', 'Stop', 'Caution', 'Off', 'Testing' được kích hoạt tương ứng với 5 trạng thái dàn đèn theo thời gian là xanh, đỏ, vàng, tắt nghỉ, sáng cả 3 đèn để kiểm tra.

    - nhóm chân cung cấp nguồn: có 1 form 'Properties Page' cho phép người dùng có thể thiết lập trạng thái dàn đèn dễ dàng tại thời điểm thiết kế.

    Ứng dụng mà chúng tôi muốn xây dựng là 1 form giao diện demo việc điều khiển luồng giao thông ở 1 ngã tư một cách tự động theo các tham số timer định sẵn.

    I. Qui trình xây dựng ActiveX dàn đèn điển hình gồm 17 bước như sau:

    1. Chạy Visual C++ 6.0 (thí dụ chọn mục Start.Programs.Microsoft Visual Studio 6.0.Microsoft Visual C++ 6.0).

    2. Chọn menu File.New để hiển thị cửa sổ New. Chọn tab "Project", chọn mục MFC ActiveX ControlWizard, chọn thư mục chứa Project ở mục "Location", nhập tên project vào textbox "Project name" (thí dụ nhập tên MyStopLite) rồi chọn button Ok để bắt đầu các bước khai báo thông số cho Project.

    3. Ở cửa sổ Step 1, bạn để các tham số mặc định, chỉ chọn button Finish để hoàn thành qui trình Wizard tạo Project.

    4. Chọn menu View.Classwizard, cửa sổ MFC ClassWizard sẽ hiển thị, cửa sổ này sẽ giúp bạn đặc tả các nhóm chân giao tiếp của ActiveX.

    5. Định nghĩa các chân thuộc tính: chọn tab "Automation", chọn button "Add property" để hiển thị cửa sổ "Add property". Bạn nhập tên "Color" vào combobox "External Name", chọn kiểu short trong listbox "Type", đánh dấu chọn checkbox "Get/Set methods" rồi chọn button Ok để hoàn thành đặc tả thuộc tính Color của ActiveX. Nếu ActiveX cần xây dựng có nhiều thuộc tính, bạn sẽ lập lại bước 5 nhiều lần.

    6. Định nghĩa các chân tác vụ: chọn tab "Automation", chọn button "Add Method" để hiển thị cửa sổ "Add Method". Bạn nhập tên "Next" vào combobox "External Name", chọn kiểu trả về "void" trong listbox "Return type", rồi chọn button Ok để hoàn thành đặc tả tác vụ Next() của ActiveX. Nếu ActiveX cần xây dựng có nhiều tác vụ, bạn sẽ lập lại bước 6 nhiều lần.

    7. Định nghĩa các chân events xuất ra : chọn tab "ActiveX Events", chọn button "Add Event" để hiển thị cửa sổ "Add event". Bạn nhập tên "Go" vào combobox "External Name", rồi chọn button Ok để hoàn thành đặc tả event "Go" của ActiveX. Lặp lại bước 7 nhiều lần để định nghĩa các sự kiện xuất ra như Stop, Caution, Off, Testing.

    8. Định nghĩa hàm xử lý sự kiện cho 1 số sự kiện nhập mà ActiveX quan tâm: chọn tab "Message Maps", duyệt tìm và chọn mục WM_LBUTTONDOWN trong danh sách "Messages" rồi chọn button "Add Function" để tạo hàm xử lý sự kiện ấn chuột trái trên ActiveX. Tương tự, duyệt tìm và chọn mục "OnAmbientPropertyChange" rồi chọn button "Add Function" để tạo hàm xử lý sự kiện Container thay đổi.

    9. Chọn tác vụ OnDraw trong listbox "Member functions" rồi chọn button "Edit Code" để hiển thị cửa sổ soạn code cho ActiveX. Viết code cho các tác vụ và các hàm xử lý sự kiện vừa tạo như sau:

    //Hiệu chỉnh lại hàm OnDraw của Activex Control để vẽ nó theo yêu cầu.
    void CMyStopLiteCtrl::OnDraw (CDC* pdc, const CRect& rcBounds, const CRect& rcInvalid) {
    //1. xóa background của ActiveX Control dùng màu background của container.
    CBrush brAmbientBack(TranslateColor(AmbientBackColor()));
    pdc->FillRect(rcBounds, &brAmbientBack);

    //2. vẽ mép ActiveX Control dùng thuộc tính stock của control : BackColor + ForeColor
    // tính kích thước khoảng 40% độ cao
    CRect rcBezel(rcBounds);
    int nHeight = rcBounds.Height();
    int nWidth = rcBounds.Width();
    if (nHeight >= nWidth) {
    int nBezelWidth = nHeight * 40 / 100;
    if (nBezelWidth > nWidth)
    nBezelWidth = nWidth; // not more then width!
    int nDeflateBezel = (nWidth - nBezelWidth) / 2;
    rcBezel.DeflateRect(nDeflateBezel, 0);
    } else {
    int nBezelHeight = nWidth * 40 / 100;
    if (nBezelHeight > nHeight)
    nBezelHeight = nHeight; // not more then width!
    int nDeflateBezel = (nHeight - nBezelHeight) / 2;
    rcBezel.DeflateRect(0,nDeflateBezel);
    }
    //tạo và chọn đối tượng brush, pen
    CBrush brBack(TranslateColor(GetBackColor()));
    CBrush * pbrOld = pdc->SelectObject(&brBack);
    CPen pnFore(PS_SOLID, 2, TranslateColor(GetForeColor()));
    CPen * ppnOld = pdc->SelectObject(&pnFore);
    //vẽ đường viền cho ActiveX
    pdc->Rectangle(rcBezel);
    // select old brush, but not old pen
    pdc->SelectObject(pbrOld);

    //3. vẽ 3 đèn dùng thuộc tính stock ForeColor.
    int nLights = TranslateLights();
    if (nHeight >= nWidth) { //hiển thị đèn từ trên xuống
    //vẽ đèn đỏ ở trên
    DrawLight(pdc, rcBounds, 6, 27,
    (nLights & SLBIT_RED) ? SLCOLOR_RED : SLCOLOR_OFF);
    //vẽ đèn vàng ở giữa
    DrawLight(pdc, rcBounds, 37, 27,
    (nLights & SLBIT_YELLOW) ? SLCOLOR_YELLOW : SLCOLOR_OFF);
    //vẽ đèn xanh ở dưới
    DrawLight(pdc, rcBounds, 68, 27,
    (nLights & SLBIT_GREEN) ? SLCOLOR_GREEN : SLCOLOR_OFF);
    } else { //hiển thị đèn từ trái sang phải
    //vẽ đèn đỏ ở bên trái
    DrawLight(pdc, rcBounds, 6, 27,
    (nLights & SLBIT_RED) ? SLCOLOR_RED : SLCOLOR_OFF);
    //vẽ đèn vàng ở giữa
    DrawLight(pdc, rcBounds, 37, 27,
    (nLights & SLBIT_YELLOW) ? SLCOLOR_YELLOW : SLCOLOR_OFF);
    //vẽ đèn xanh ở bên phải
    DrawLight(pdc, rcBounds, 68, 27,
    (nLights & SLBIT_GREEN) ? SLCOLOR_GREEN : SLCOLOR_OFF);
    }
    pdc->SelectObject(ppnOld);
    }

    //hàm tạo sự kiện xuất khi dàn đèn đổi màu
    void CMyStopLiteCtrl::FireRightEvent() {
    switch (m_color) {
    case SL_RED: FireStop();
    break;
    case SL_YELLOW: FireCaution();
    break;
    case SL_GREEN: FireGo();
    break;
    case SL_NONE: FireOff();
    break;
    case SL_TEST: FireTesting();
    break;
    }
    }

    //hàm thiết lập trạng thái tắt/sáng của 3 đèn
    int CMyStopLiteCtrl::TranslateLights() {
    int nLights = SLBIT_RED; //trị mặc định
    switch (m_color) {
    case SL_NONE: nLights = 0;
    break;
    case SL_RED: nLights = SLBIT_RED;
    break;
    case SL_GREEN: nLights = SLBIT_GREEN;
    break;
    case SL_YELLOW: nLights = SLBIT_YELLOW;
    break;
    case SL_TEST: nLights = SLBIT_RED | SLBIT_YELLOW | SLBIT_GREEN;
    break;
    }
    return nLights;
    }

    //hàm vẽ đèn theo trạng thái tắt/sáng của nó
    void CMyStopLiteCtrl::DrawLight (
    CDC* pdc, // DC được dùng để vẽ
    const CRect& rcBounds, //vùng chữ nhật vẽ
    int nPercentDown, //vị trí trên
    int nPercentDiameter, // bán kính
    COLORREF crColor //màu
    ) {
    //tính bán kính theo đơn vị vẽ
    int nHeight = rcBounds.Height();
    int nWidth = rcBounds.Width();
    if (nWidth
    int nDiameter = nHeight * nPercentDiameter / 100;
    if (nDiameter > nWidth)
    nDiameter = nWidth; //bán kính phải <= độ rộng ActiveX
    //tạo vùng chữ nhật bao quanh đèn
    int nLeftEdge = (rcBounds.left + rcBounds.right - nDiameter) / 2;
    int nTopEdge = rcBounds.top + nHeight * nPercentDown / 100;
    CRect rcLight(nLeftEdge, nTopEdge,nLeftEdge + nDiameter,nTopEdge + nDiameter);
    rcLight.IntersectRect(rcLight, rcBounds);
    //tạo brush, draw, chọn brush cũ
    CBrush brColor(crColor);
    CBrush * brOld = pdc->SelectObject(&brColor);
    pdc->Ellipse(rcLight);
    pdc->SelectObject(brOld);
    } else { //hiển thị dàn đèn ngang
    int nDiameter = nWidth * nPercentDiameter / 100;
    if (nDiameter > nHeight)
    nDiameter = nHeight; //bán kính phải <= độ cao ActiveX
    //tạo vùng chữ nhật bao quanh đèn
    int nTopEdge = (rcBounds.top + rcBounds.bottom - nDiameter) / 2;
    int nLeftEdge = rcBounds.left + nWidth * nPercentDown / 100;
    CRect rcLight( nLeftEdge,nTopEdge, nLeftEdge + nDiameter, nTopEdge + nDiameter);
    rcLight.IntersectRect(rcLight, rcBounds);
    //tạo brush, draw, chọn brush cũ
    CBrush brColor(crColor);
    CBrush * brOld = pdc->SelectObject(&brColor);
    pdc->Ellipse(rcLight);
    pdc->SelectObject(brOld);
    }
    }

    //hàm xử lý ấn chuột trái
    void CMyStopLiteCtrl::OnLButtonDown(UINT nFlags, CPoint point) {
    Next();
    COleControl::OnLButtonDown(nFlags, point);
    }

    //tác vụ đọc thuộc tính Color
    short CMyStopLiteCtrl::GetColor() {
    return m_color;
    }

    //tác vụ hiệu chỉnh thuộc tính Color
    void CMyStopLiteCtrl::SetColor(short nNewValue) {
    if (nNewValue >= SL_NONE && nNewValue <= SL_TEST) {
    m_color = nNewValue;
    InvalidateControl();
    FireRightEvent();
    SetModifiedFlag();
    } else {
    ThrowError(CTL_E_ILLEGALFUNCTIONCALL, "Color parameter out of range");
    }
    }

    //tác vụ chuyển trạng thái đèn
    void CMyStopLiteCtrl::Next() {
    if (m_color >= SL_LAST || m_color < SL_FIRST) {
    m_color = SL_FIRST;
    } else m_color++;
    InvalidateControl();
    FireRightEvent();
    SetModifiedFlag();
    }

    //hàm xử lý sự kiện thay đổi của Container
    void CMyStopLiteCtrl::OnAmbientPropertyChange(DISPID dispid) {
    //vẽ lại ActiveX nếu container thay đổi màu nền
    if (dispid == DISPID_AMBIENT_BACKCOLOR || dispid == DISPID_UNKNOWN){
    InvalidateControl();
    }
    //gọi class cha làm tiếp
    COleControl::OnAmbientPropertyChange(dispid);
    }

    10. Dời lên đầu file MyStopLiteCtl.cpp, viết đoạn lệnh định nghĩa các hằng gợi nhớ cần dùng sau đây:

    //các giá trị của thuộc tính Color
    enum { SL_NONE = 0,
    SL_FIRST = 1, //đèn đầu trong chu kỳ chuyển màu
    SL_RED = 1, SL_GREEN = 2, SL_YELLOW = 3, //thứ tự chuyển
    SL_LAST = 3, //đèn cuối trong chu kỳ chuyển màu
    SL_TEST = 4 };

    //các bit qui định vẽ đèn nào
    enum { SLBIT_RED = 1, SLBIT_GREEN = 2, SLBIT_YELLOW = 4};
    //cáu màu RGB cần dùng cho các đèn
    enum { SLCOLOR_OFF = RGB(63, 63, 63),
    SLCOLOR_RED = RGB(255, 0, 0),
    SLCOLOR_GREEN = RGB(0, 255, 0),
    SLCOLOR_YELLOW = RGB(255, 255, 0) };

    11. Mở file MyStopLiteCtl.h và viết thêm đoạn lệnh khai báo các hàm viết thêm cho ActiveX như sau (vào nhóm có tầm vực public):

    public:
    CMyStopLiteCtrl();
    //các hàm viết thêm
    void FireRightEvent();
    int TranslateLights();
    void CMyStopLiteCtrl::DrawLight(
    CDC* pdc, // DC được dùng để vẽ
    const CRect& rcBounds, //vùng chữ nhật vẽ
    int nPercentDown, //vị trí trên
    int nPercentDiameter, // bán kính
    COLORREF crColor //màu
    );

    Khai báo biến miêu tả trạng thái màu của dàn đèn vào nhóm có tầm vực protected như sau:
    protected:

    ~CMyStopLiteCtrl();
    short m_color;
    ...

    12. Chọn tab "ResourceView" trong cửa sổ hiển thị cây Project, mở rộng mục Dialog, ấn kép chuột mục IDD_PROPPAGE_MYSTOPLITE để hiển thị cửa sổ thiết kế trang thuộc tính của ActiveX, xóa label có sẵn trong form này, tạo Frame "Hãy chọn tráng thái đầu của dàn đèn :" như hình sau:

    13. Vẽ 1 Radio button, ấn phải chuột vào button vừa tạo và chọn mục Properties để hiển thị cửa sổ thuộc tính của nó. Đặt ID = IDC_OFF, Caption = Off, đánh dấu chọn vào 2 check box "Group" và "Tab Stop".

    14. Lập lại bước 13 để vẽ tuần tự 4 radio button với các thuộc tính như sau:

    ID = IDC_RED, Caption = Red, đánh dấu chọn vào check box "Tab Stop"
    ID = IDC_GREEN, Caption = Green, đánh dấu chọn vào check box "Tab Stop"
    ID = IDC_YELLOW, Caption = Yellow, đánh dấu chọn vào check box "Tab Stop"
    ID = IDC_TESTING, Caption = Testing, đánh dấu chọn vào check box "Tab Stop"

    15. Chọn menu View.ClassWizard để hiển thị cửa sổ ClassWizard. Chọn class "MyStopLitePropPage" ở listbox "Class name". Chọn tab "Member Variables", chọn mục IDC_OFF trong listbox "Control ID", chọn button "Add variable" để hiển thị cửa sổ "Add member variable", nhập tên m_color vào textbox "Member Variable name", nhập tên Color vào comboBox "Optional property name" rồi ấn button Ok để hoàn tất việc định nghĩa biến kết hợp với radion button Off.

    16 Dịch ActiveX: chọn menu Build.Rebuild all để dịch và tạo ActiveX Control, nếu có lỗi về từ vựng hay cú pháp thì sửa cho đến khi hết lỗi.

    17. Kiểm thử ActiveX: chọn menu Tools.ActiveX Control Test Container để kiểm thử các chức năng của ActiveX Control có đúng kỳ vọng không. Bạn không cần thực hiện bước này nếu như đã viết đúng các đoạn code được liệt kê ở trên.

    18. Sau khi dịch thành công, ActiveX đã được đăng ký tự động vào Windows quản lý và có thể được dùng bởi bất kỳ chương trình nào trên máy bạn. Để dùng ActiveX này trên máy khác, bạn phải copy file MyStopLite.ocx vào vị trí nào đó trên máy cần cài đặt rồi ra lệnh đăng ký vào Windows thông qua hàng lệnh sau (chọn Start.Run và nhập vào):
    regsvr32 <đường dẫn file MyStopLite.ocx>

    II. Qui trình xây dựng ứng dụng thử dùng ActiveX gồm các bước như sau:

    1. Chạy Visual C++ 6.0 (thí dụ chọn mục Start.Programs.Microsoft Visual Studio 6.0.Microsoft Visual C++ 6.0).

    2. Chọn menu File.New để hiển thị cửa sổ New. Chọn tab "Project", chọn mục MFC AppWizard (exe), chọn thư mục chứa Project ở mục "Location", nhập tên project vào textbox "Project name" (thí dụ nhập tên MyApp) rồi chọn button Ok để bắt đầu các bước khai báo thông số cho Project.

    3. Ở cửa sổ Step 1, bạn chọn checkbox "Dialog based", rồi chọn button Finish để hoàn thành qui trình Wizard tạo Project.

    4. Khi Form ban đầu của ứng dụng hiển thị, bạn xóa tất cả các đối tượng có sẵn trong Form (gồm 2 button và 1 label).

    5. Chọn menu Project.Add to Project.Components and Controls để hiển thị cửa sổ "Components and Controls Gallery", mở rộng mục "Registered ActiveX Controls", duyệt tìm và chọn mục "MyStopLite Control", chọn button Insert để "add" ActiveX này vào cửa sổ Toolbox của Project ứng dụng.

    6. Thiết kế Form gồm 4 đèn ở 1 ngả tư và 4 textbox kèm theo như sau:

    7. Nhấn phải chuột vào đèn trên trái, chọn mục Properties trong menu để hiển thị cửa sổ thuộc tính của nó, đặt ID = IDC_SL1 rồi đóng cửa sổ Proterties lại. Ấn phải chuột vào Label kết hợp với đèn trên trái, chọn mục Properties trong menu để hiển thị cửa sổ thuộc tính của nó, đặt ID = IDC_L1 rồi đóng cửa sổ Proterties lại.

    8. Lập lại bước 7 cho 3 đèn còn lại theo chiều kim đồng hồ với ID lần lượt là IDC_SL2, IDC_SL3, IDC_SL4. ID cho các Label kết hợp với chúng lần lượt là IDC_L2, IDC_L3, IDC_L4.

    9. Chọn menu View.ClassWizard để hiển thị cửa sổ "MFC ClassWizard" như sau:

    10. Chọn mục CMyAppDlg ở listbox "ClassName", chọn tab "Member Variables", chọn mục IDC_L1 trong listbox "Object IDs", chọn button "Add Variable" để tạo biến kết hợp với Label tương ứng, đặt tên biến là m_l1, Category là Value, Type là CString. Tương tự, đặt tên biến cho 3 label còn lại là m_l2, m_l3, m_l4. Chọn mục IDC_SL1 trong listbox "Object IDs", chọn button "Add Variable" để tạo biến kết hợp với đèn tương ứng, đặt tên biến là m_sl1, Category là Control, Type là CMyStopLite. Tương tự, đặt tên biến cho 3 đèn còn lại là m_sl2, m_sl3, m_sl4.

    11. Chọn mục CMyAppDlg ở listbox "ClassName", chọn tab "Message Maps", duyệt tìm và chọn mục IDC_SL1 trong listbox "Object IDs", chọn mục "Go" trong listbox "Message", chọn button "Add function" để tạo hàm xử lý sự kiện Go của đèn tương ứng (tên hàm mặc định này có tên là OnGoSl1). Tiếp tục tạo các hàm xử lý sự kiện Stop và Caution của đèn IDC_SL1. Tương tự, tạo 3 hàm xử lý sự kiện Go, Stop, Caution của đèn IDC_SL2.

    12. Chọn button "Edit Code" trong cửa sổ "MFC ClassWizard" để hiển thị cửa sổ soạn code cho các hàm xử lý vừa tạo ra và viết code cho chúng như sau:

    void CMyAppDlg::OnGoSl1() {

    // TODO: Add your control notification handler code here
    UpdateData(TRUE);
    m_l1 = m_l3 = "Den xanh. Bat dau chay.";
    UpdateData(FALSE);
    }

    void CMyAppDlg::OnStopSl1() {
    // TODO: Add your control notification handler code here
    UpdateData(TRUE);
    m_l1 = m_l3 = "Den do. Hay dung lai.";
    UpdateData(FALSE);
    }

    void CMyAppDlg::OnCautionSl1() {
    // TODO: Add your control notification handler code here
    UpdateData(TRUE);
    m_l1 = m_l3 = "Den vang. Chuan bi dung lai.";
    UpdateData(FALSE);
    }

    void CMyAppDlg::OnGoSl2() {
    // TODO: Add your control notification handler code here
    UpdateData(TRUE);
    m_l2 = m_l4 = "Den xanh. Bat dau chay.";
    UpdateData(FALSE);
    }

    void CMyAppDlg::OnStopSl2() {
    // TODO: Add your control notification handler code here
    UpdateData(TRUE);
    m_l2 = m_l4 = "Den do. Hay dung lai.";
    UpdateData(FALSE);
    }

    void CMyAppDlg::OnCautionSl2() {
    // TODO: Add your control notification handler code here
    UpdateData(TRUE);
    m_l2 = m_l4 = "Den vang. Chuan bi dung lai.";
    UpdateData(FALSE);
    }

    13. Tìm đoạn code của hàm OnInitDialog(), viết thêm đoạn code sau đây vào cuối hàm:

    // TODO: Add extra initialization here
    m_status = 0;
    OnNext();
    return TRUE; //return TRUE unless you set the focus to a control


    14. Dời chuột về trước hàm OnInitDialog() và viết thêm các hàm dịch vụ sau đây :

    //khai báo các hằng cần dùng
    #define C_OFF 0
    #define C_RED 1
    #define C_GREEN 2
    #define C_YELLOW 3
    #define C_TEST 4
    //khai báo các biến cần dùng
    int deltat[4] = {5000,1000,7000,1000};
    int m_idTimer;
    short m_status;
    //hàm chuyển trạng thái giao lộ
    void CMyAppDlg::OnNext() {
    // TODO: Add your control notification handler code here
    m_status = (m_status+1)%4;
    switch(m_status) {
    case 0 : //đèn 1,3 xanh + đèn 2,4 đỏ
    m_sl1.SetColor(C_GREEN);
    m_sl3.SetColor(C_GREEN);
    m_sl2.SetColor(C_RED);
    m_sl4.SetColor(C_RED);
    break;
    case 1 : //đèn 1,3 vàng + đèn 2,4 chưa thay đổi
    m_sl1.SetColor(C_YELLOW);
    m_sl3.SetColor(C_YELLOW);
    break;
    case 2 : //đèn 1,3 đỏ + đèn 2,4 xanh
    m_sl1.SetColor(C_RED);
    m_sl3.SetColor(C_RED);
    m_sl2.SetColor(C_GREEN);
    m_sl4.SetColor(C_GREEN);
    break;
    case 3 : //đèn 1,3 chưa thay đổi + đèn 2,4 vàng
    m_sl2.SetColor(C_YELLOW);
    m_sl4.SetColor(C_YELLOW);
    break;
    }
    //thiết lập timer đếm thời gian
    m_idTimer = SetTimer(50, deltat[m_status],(TIMERPROC)NULL);
    }

    15. Tìm đoạn code của hàm OnPaint(), hiệu chỉnh nội dung hàm như sau:

    void CMyAppDlg::OnPaint() {
    if (IsIconic()) {
    //giữ nguyên nhánh này
    } else { //viết lại nhánh sau đây
    //khai báo các hằng tọa độ 2 điểm trên trái và dưới phải của ngã tư
    //sẽ thay đổi theo tọa độ ngã tư trong Form thiết kế
    const XLeft = 207;
    const YTop = 133;
    const XRight = 270;
    const YBottom = 195;
    //xác định DC của Form
    CPaintDC dc(this);
    CRect rect;
    //xác định vùng chữ nhật của Form
    GetClientRect(&rect);
    //tạo bút vẽ
    CBrush brColor(0x00ffffff);
    CBrush * brOld = dc.SelectObject(&brColor);
    //vẽ ngã tư
    dc.MoveTo(10,YTop);
    dc.LineTo(XLeft,YTop);
    dc.LineTo(XLeft,10);
    dc.MoveTo(XRight,10);
    dc.LineTo(XRight,YTop);
    dc.LineTo(rect.Width()-10,YTop);
    dc.MoveTo(10,YBottom);
    dc.LineTo(XLeft,YBottom);
    dc.LineTo(XLeft,rect.Height()-10);
    dc.MoveTo(XRight,rect.Height()-10);
    dc.LineTo(XRight,YBottom);
    dc.LineTo(rect.Width()-10,YBottom);

    dc.SelectObject(brOld);
    CDialog::OnPaint();
    }
    }

    16. Mở file CMyAppDlg.h và thêm lệnh khai báo hàm Next vào nhóm public của class như sau:

    class CMyAppDlg : public CDialog {
    // Construction
    public:
    CMyAppDlg(CWnd* pParent = NULL); // standard constructor
    void OnNext();
    //giữ nguyên phần còn lại của class


    17. Chọn menu Build.ReBuild All để dịch chương trình, nếu có lỗi thì sửa cho hết lỗi (hi vọng bạn nhập đúng các đoạn lệnh trên để chương trình không bị lỗi!).

    18. Chọn menu Build.Execute MyApp.exe để chạy chương trình. Nếu không có gì trục trặc, chương trình sẽ hiển thị ngã tư và 4 đèn báo hiệu. Các đèn này sẽ tự động cập nhật trạng thái theo thời gian qui định trong biến deltat của chương trình.

    Nguyễn Văn Hiệp

    ID: A1001_103