Programming with Microsoft Visual C++.NET 6ed [Electronic resources] نسخه متنی

اینجــــا یک کتابخانه دیجیتالی است

با بیش از 100000 منبع الکترونیکی رایگان به زبان فارسی ، عربی و انگلیسی

Programming with Microsoft Visual C++.NET 6ed [Electronic resources] - نسخه متنی

George Shepherd, David Kruglinski

| نمايش فراداده ، افزودن یک نقد و بررسی
افزودن به کتابخانه شخصی
ارسال به دوستان
جستجو در متن کتاب
بیشتر
تنظیمات قلم

فونت

اندازه قلم

+ - پیش فرض

حالت نمایش

روز نیمروز شب
جستجو در لغت نامه
بیشتر
توضیحات
افزودن یادداشت جدید








The Ex08a Example: Standard Common Controls


To get an idea of how these common controls work, we'll put them in a modal dialog box. The steps are as follows.



    Run the MFC Application Wizard to generate a project named Ex08a. Choose New Project from Visual Studio's File menu. In the New Project dialog box, select the MFC Application template, type the name Ex08a, and click OK. In the MFC Application Wizard, accept all the defaults but two: On the Application Type page, select Single Document, and on the Advanced Features page, deselect Printing And Print Preview. When you're finished, click Finish.



    Create a new dialog resource with ID IDD_DIALOG1. Choose Add Resource from the Project menu and add a new dialog resource. Using the Toolbox, add controls to the dialog box. The following is a list of the control types, their IDs, and their tab order. After you set the Caption properties to the appropriate text, the dialog box should have the following controls and tab order:



    Don't worry about the other properties now—you'll set those in the following steps. (Some controls might look different than they do in Figure 8-1 until you set their properties.)






































































    Control Type


    ID


    Tab Order


    Static Text


    IDC_STATIC


    1


    Progress


    IDC_PROGRESS1


    2


    Static Text


    IDC_STATIC


    3


    Slider


    IDC_SLIDER1


    4


    Static Text


    IDC_STATIC_SLIDER1


    5


    Static Text


    IDC_STATIC


    6


    Slider


    IDC_SLIDER2


    7


    Static Text


    IDC_STATIC_SLIDER2


    8


    Static Text


    IDC_STATIC


    9


    Edit


    IDC_BUDDY_SPIN1


    10


    Spin


    IDC_SPIN1


    11


    Static Text


    IDC_STATIC


    12


    Static Text


    IDC_STATIC


    13


    List control


    IDC_LISTVIEW1


    14


    Static Text


    IDC_STATIC_LISTVIEW1


    15


    Static Text


    IDC_STATIC


    16


    Tree control


    IDC_TREEVIEW1


    17


    Static Text


    IDC_STATIC_TREEVIEW1


    18


    Button


    IDOK


    19


    Button


    IDCANCEL


    20




    Use the MFC Class Wizard to create a new class, CEx08aDialog, derived from CDialog. Choose Add Class from Project menu to display the MFC Class Wizard. Select CDialog as the base class and IDD_DIALOG1 as the name of the Dialog ID, as shown here:




    Override the OnInitDialog function and handle the WM_HSCROLL and the WM_VSCROLL messages. Select the CEx08aDialog class in Class View. Click the Overrides button at the top of the Properties window and add the OnInitDialog function. Click the Messages button at the top of the Properties window and add the OnHScroll and OnVScroll functions for the WM_HSCROLL and WM_VSCROLL messages.



    Program the progress control. Visual Studio won't generate a data member for this control, so you must do it yourself. Add a public integer data member named m_nProgress in the CEx08aDialog class header, and set it to 0 in the constructor. Also, add the following code in the OnInitDialog member function:


    // Progress control
    CProgressCtrl* pProg =
    (CProgressCtrl*) GetDlgItem(IDC_PROGRESS1);
    pProg->SetRange(0, 100);
    pProg->SetPos(m_nProgress);



    Program the "continuous" slider control. Add a public integer data member named m_nSlider1 to the CEx08aDialog header, and set it to 0 in the constructor. Then add the following code in the OnInitDialog member function to set the slider's range, initialize its position from the data member, and set the neighboring static control to the slider's current value:


    // Slider control
    CString strText1;
    CSliderCtrl* pSlide1 =
    (CSliderCtrl*) GetDlgItem(IDC_SLIDER1);
    pSlide1->SetRange(0, 100);
    pSlide1->SetPos(m_nSlider1);
    strText1.Format("%d", pSlide1->GetPos());
    SetDlgItemText(IDC_STATIC_SLIDER1, strText1);

    To keep the static control updated, you must map the WM_HSCROLL message that the slider sends to the dialog box. Add the following boldface code to the OnHScroll handler, replacing the existing code:

    void CEx08aDialog::OnHScroll(UINT nSBCode, UINT nPos,
    CScrollBar* pScrollBar)
    {
    CSliderCtrl* pSlide = (CSliderCtrl*) pScrollBar;
    CString strText;
    strText.Format("%d", pSlide->GetPos());
    SetDlgItemText(IDC_STATIC_SLIDER1, strText);
    }

    Finally, you need to update the slider's m_nSlider1 data member when the user clicks OK. Your natural instinct might be to put this code in the OnOK button handler. You would have a problem, however, if a data exchange validation error occurred that involved any other control in the dialog box. Your handler would set m_nSlider1 even if the user chose to cancel the dialog box. To avoid this problem, add your code in the DoDataExchange function as shown below. If you do your own validation and detect a problem, call the CDataExchange::Fail function, which alerts the user with a message box.

    void CEx08aDialog::DoDataExchange(CDataExchange* pDX)
    {
    if (pDX->m_bSaveAndValidate) {
    TRACE("updating slider data members\n");
    CSliderCtrl* pSlide1 =
    (CSliderCtrl*) GetDlgItem(IDC_SLIDER1);
    m_nSlider1 = pSlide1->GetPos();
    }
    CDialog::DoDataExchange(pDX);
    }



    Program the "discrete" slider control. Add a public integer data member named m_nSlider2 to the CEx08aDialog header, and set it to 0 in the constructor. This data member is a zero-based index into the dValue, the array of numbers (4.0, 5.6, 8.0, 11.0, and 16.0) that the slider can represent. Define dValue as a public static double array member variable in

    Ex08aDialog.h :


    static double dValue[5];

    Initialize dValue at the top of

    Ex08aDialog.cpp using the following line:


    double CEx08aDialog::dValue[5] = {4.0, 5.6, 8.0, 11.0, 16.0};

    Next, add code in the OnInitDialog member function to set the slider's range and initial position.


    CString strText2;
    CSliderCtrl* pSlide2 =
    (CSliderCtrl*) GetDlgItem(IDC_SLIDER2);
    pSlide2->SetRange(0, 4);
    pSlide2->SetPos(m_nSlider2);
    strText2.Format("%3.1f", dValue[pSlide2->GetPos()]);
    SetDlgItemText(IDC_STATIC_SLIDER2, strText2);

    If you had only one slider, the WM_HSCROLL handler in step 5 would work. But because you have two sliders that send WM_HSCROLL messages, the handler must differentiate between them. Here's the new code:

    void CEx08aDialog::OnHScroll(UINT nSBCode, UINT nPos,
    CScrollBar* pScrollBar)
    {
    CSliderCtrl* pSlide = (CSliderCtrl*) pScrollBar;
    CString strText;
    // Two sliders are sending
    // HSCROLL messages (different processing)
    switch(pScrollBar->GetDlgCtrlID()) {
    case IDC_SLIDER1:
    strText.Format("%d", pSlide->GetPos());
    SetDlgItemText(IDC_STATIC_SLIDER1, strText);
    break;
    case IDC_SLIDER2:
    strText.Format("%3.1f", dValue[pSlide->GetPos()]);
    SetDlgItemText(IDC_STATIC_SLIDER2, strText);
    break;
    }
    }

    Slider2 needs tick marks, so display the dialog editor and set the control's Tick Marks and Auto Ticks properties to True in the Properties window. With Auto Ticks set to True, the slider will place a tick at every increment. If you don't see the tick marks after setting these properties, you might need to increase the height of the control.

    The same data exchange considerations that applied to the previous slider apply to this slider. Add the following code in the dialog class DoDataExchange member function inside the block for the if statement you added in the previous step:


    CSliderCtrl* pSlide2 =
    (CSliderCtrl*) GetDlgItem(IDC_SLIDER2);
    m_nSlider2 = pSlide2->GetPos();

    Display the dialog editor and set the Point property of both sliders to Bottom/Right. Select the IDC_STATIC_SLIDER1 and IDC_STATIC_SLIDER2 static controls and set the Align Text property to Right.



    Program the spin button control. The spin control depends on its buddy edit control, which is located immediately before it in the tab order. Use the Add Member Variable Wizard to add a double-precision data member named m_dSpin for the IDC_BUDDY_SPIN1 edit control. We're using a double instead of an int because the int would require almost no programming, and that would be too easy. We want the edit control range to be 0.0 to 10.0, but the spin control itself needs an integer range. You can start the Add Member Variable Wizard by selecting the CEx08aDialog class in Class View and then choosing Add Variable from the Project menu. The settings for the wizard are shown here:



    Add the following code to OnInitDialog to set the spin control range from 0 to 100 and set its initial value to m_dSpin * 10.0:


    // Spin control
    CSpinButtonCtrl* pSpin =
    (CSpinButtonCtrl*) GetDlgItem(IDC_SPIN1);
    pSpin->SetRange(0, 100);
    pSpin->SetPos((int) (m_dSpin * 10.0));

    To display the current value in the buddy edit control, you need to handle the WM_VSCROLL message that the spin control sends to the dialog box. Add the following boldfaced code to OnVScroll:

    void CEx08aDialog::OnVScroll(UINT nSBCode, UINT nPos, 
    CScrollBar* pScrollBar)
    {
    if (nSBCode == SB_ENDSCROLL) {
    return; // Reject spurious messages
    }
    // Process scroll messages from IDC_SPIN1 only
    if (pScrollBar->GetDlgCtrlID() == IDC_SPIN1) {
    CString strValue;
    strValue.Format("%3.1f", (double) nPos / 10.0);
    ((CSpinButtonCtrl*) pScrollBar)->GetBuddy()
    ->SetWindowText(strValue);
    }
    CDialog::OnVScroll(nSBCode, nPos, pScrollBar);
    }

    There's no need to add code in OnOK or in DoDataExchange because the Dialog Data Exchange (DDX) code processes the contents of the edit control.

    Display the dialog editor and set the Auto Buddy property for the spin control to True. Set the Read Only property for the buddy edit control to True.



    Set up an image list. Both the list control and the tree control need an image list, and the image list needs icons. The companion CD contains icons in the Ex08a\res folder. These icons are circles with black outlines and different-colored interiors. Use fancier icons if you have them.

    To import these icons into the Ex08a project, first copy the .ico files to your Ex08a\res folder and then choose Add Resource from the Project menu. In the Add Resource dialog box, click Import. In the Import dialog box, navigate to the icon files. Set the Files Of Type drop-down list to Icon Files. Select

    Icon0.ico to

    Icon7.ico and click Open. The icons will be opened in the image editor and added to the Icon folder in Resource View. Using the Properties window, set the ID property for each icon as shown here, and then close the icons in the icon editor.


































    Icon File


    ID


    Icon0.ico


    IDI_WHITE


    Icon1.ico


    IDI_BLACK


    Icon2.ico


    IDI_RED


    Icon3.ico


    IDI_BLUE


    Icon4.ico


    IDI_YELLOW


    Icon5.ico


    IDI_CYAN


    Icon6.ico


    IDI_PURPLE


    Icon7.ico


    IDI_GREEN


    When you're finished, the Icon folder in Resource View will look like the following:








    About Icons


    You probably know that a bitmap is an array of bits that represent pixels on the display. (Bitmaps were discussed in Chapter 6.) In Windows, an icon is a "bundle" of bitmaps. First of all, an icon has different bitmaps for different sizes. Typically, small icons are 16 by 16 pixels and large icons are 32 by 32 pixels. Within each size are two separate bitmaps: one 4-bit-per-pixel bitmap for the color image and one monochrome (1-bit-per-pixel) bitmap for the "mask." If a mask bit is 0, the corresponding image pixel represents an opaque color. If the mask bit is 1, an image color of black (0) means that the pixel is transparent and an image color of white (0xF) means that the background color is inverted at the pixel location.

    Small icons were new with Windows 95. They're used on the taskbar, in Windows Explorer, and in your list and tree controls, if you want them there. If an icon doesn't have a 16-by-16-pixel bitmap, Windows manufactures a small icon out of the 32-by-32-pixel bitmap, but it won't be as neat as one you draw yourself. The image editor in Visual Studio lets you create and edit icons. The following shows the image editor and the Colors palette.


    The top square in the upper left portion of the Colors palette shows you the main color for brushes, shape interiors, and so on, and the square underneath it shows the border color for shape outlines. You select a main color by left-clicking on a color, and you select a border color by right-clicking on a color. Now look at the two "monitors" to the right of the upper left square of the Colors palette. You click on the upper monitor to paint transparent pixels, which are drawn in dark cyan. You click on the lower monitor to paint inverted pixels, which are drawn in red.











    Next, add a public CImageList data member named m_imageList in the CEx08aDialog class header, and then add the following code to OnInitDialog:


    // Icons
    HICON hIcon[8];
    int n;
    m_imageList.Create(16, 16, 0, 8, 8); // 32, 32 for large icons
    hIcon[0] = AfxGetApp()->LoadIcon(IDI_WHITE);
    hIcon[1] = AfxGetApp()->LoadIcon(IDI_BLACK);
    hIcon[2] = AfxGetApp()->LoadIcon(IDI_RED);
    hIcon[3] = AfxGetApp()->LoadIcon(IDI_BLUE);
    hIcon[4] = AfxGetApp()->LoadIcon(IDI_YELLOW);
    hIcon[5] = AfxGetApp()->LoadIcon(IDI_CYAN);
    hIcon[6] = AfxGetApp()->LoadIcon(IDI_PURPLE);
    hIcon[7] = AfxGetApp()->LoadIcon(IDI_GREEN);
    for (n = 0; n < 8; n++) {
    m_imageList.Add(hIcon[n]);
    }



    Program the list control. In the dialog editor, set the following properties for the list control.






















    List Control Property


    Value


    Alignment


    Top


    Always Show Selection


    True


    Single Selection


    True


    View


    List


    Then add the following code to OnInitDialog:


    // List control
    static char* color[] = {"white", "black", "red",
    "blue", "yellow", "cyan",
    "purple", "green"};
    CListCtrl* pList =
    (CListCtrl*) GetDlgItem(IDC_LISTVIEW1);
    pList->SetImageList(&m_imageList, LVSIL_SMALL);
    for (n = 0; n < 8; n++) {
    pList->InsertItem(n, color[n], n);
    }
    pList->SetBkColor(RGB(0, 255, 255)); // UGLY!
    pList->SetTextBkColor(RGB(0, 255, 255));

    As the last two lines illustrate, you don't use the WM_CTLCOLOR message with common controls; you just call a function to set the background color. As you'll see when you run the program, however, the icons' inverse-color pixels look shabby.

    If you use the list control's LVN_ITEMCHANGED notification message, you'll be able to track the user's selection of items. In Class View, select the CEx08aDialog class. In the Properties window, click the Events button, expand the IDC_LISTVIEW1 item, select the LVN_ITEMCHANGED event, and then add the OnLvnItemchangedListview1 handler. Add the following code to the OnLvnItemchangedListview1 handler to display the selected item's text in a static control:

    void CEx08aDialog::OnLvnItemchangedListview1(NMHDR* pNMHDR,
    LRESULT* pResult)
    {
    LPNMLISTVIEW pNMLV = reinterpret_cast<LPNMLISTVIEW>(pNMHDR);
    CListCtrl* pList =
    (CListCtrl*) GetDlgItem(IDC_LISTVIEW1);
    int nSelected = pNMLV->iItem;
    if (nSelected >= 0) {
    CString strItem = pList->GetItemText(nSelected, 0);
    SetDlgItemText(IDC_STATIC_LISTVIEW1, strItem);
    }
    *pResult = 0;
    }

    The NM_LISTVIEW structure has a data member called iItem that contains the index of the selected item.



    Program the tree control. In the dialog editor, set the following properties for the tree control.






















    Tree Control Property


    Value


    Has Buttons


    True


    Has Lines


    True


    Lines At Root


    True


    Scroll


    True


    Next, add the following lines to OnInitDialog:


    // Tree control
    CTreeCtrl* pTree = (CTreeCtrl*) GetDlgItem(IDC_TREEVIEW1);
    pTree->SetImageList(&m_imageList, TVSIL_NORMAL);
    // tree structure common values
    TV_INSERTSTRUCT tvinsert;
    tvinsert.hParent = NULL;
    tvinsert.hInsertAfter = TVI_LAST;
    tvinsert.item.mask = TVIF_IMAGE | TVIF_SELECTEDIMAGE |
    TVIF_TEXT;
    tvinsert.item.hItem = NULL;
    tvinsert.item.state = 0;
    tvinsert.item.stateMask = 0;
    tvinsert.item.cchTextMax = 6;
    tvinsert.item.iSelectedImage = 1;
    tvinsert.item.cChildren = 0;
    tvinsert.item.lParam = 0;
    // top level
    tvinsert.item.pszText = "Homer";
    tvinsert.item.iImage = 2;
    HTREEITEM hDad = pTree->InsertItem(&tvinsert);
    tvinsert.item.pszText = "Marge";
    HTREEITEM hMom = pTree->InsertItem(&tvinsert);
    // second level
    tvinsert.hParent = hDad;
    tvinsert.item.pszText = "Bart";
    tvinsert.item.iImage = 3;
    pTree->InsertItem(&tvinsert);
    tvinsert.item.pszText = "Lisa";
    pTree->InsertItem(&tvinsert);
    // second level
    tvinsert.hParent = hMom;
    tvinsert.item.pszText = "Bart";
    tvinsert.item.iImage = 4;
    pTree->InsertItem(&tvinsert);
    tvinsert.item.pszText = "Lisa";
    pTree->InsertItem(&tvinsert);
    tvinsert.item.pszText = "Dilbert";
    HTREEITEM hOther = pTree->InsertItem(&tvinsert);
    // third level
    tvinsert.hParent = hOther;
    tvinsert.item.pszText = "Dogbert";
    tvinsert.item.iImage = 7;
    pTree->InsertItem(&tvinsert);
    tvinsert.item.pszText = "Ratbert";
    pTree->InsertItem(&tvinsert);

    As you can see, this code sets TV_INSERTSTRUCT text and image indexes and calls InsertItem to add nodes to the tree.

    Finally, add the TVN_SELCHANGED notification for the tree control. In Class View, select the CEx08aDialog class. In the Properties window, click the Events button, expand the IDC_TREEVIEW1 item, select the TVN_SELCHANGED event, and then add the OnTvnSelchangedTreeview1 handler. Add the following boldface code to display the selected text in a static control:

    void CEx08aDialog::OnTvnSelchangedTreeview1 (NMHDR* pNMHDR,
    LRESULT* pResult)
    {
    LPNMTREEVIEW pNMTreeView = reinterpret_cast<LPNMTREEVIEW>(pNMHDR;
    CTreeCtrl* pTree = (CTreeCtrl*) GetDlgItem(IDC_TREEVIEW1);
    HTREEITEM hSelected = pNMTreeView->itemNew.hItem;
    if (hSelected != NULL) {
    char text[31];
    TV_ITEM item;
    item.mask = TVIF_HANDLE | TVIF_TEXT;
    item.hItem = hSelected;
    item.pszText = text;
    item.cchTextMax = 30;
    VERIFY(pTree->GetItem(&item));
    SetDlgItemText(IDC_STATIC_TREEVIEW1, text);
    }
    *pResult = 0;
    }

    The NM_TREEVIEW structure has a data member called itemNew that contains information about the selected node; itemNew.hItem is the handle of that node. The GetItem function retrieves the node's data, storing the text using a pointer supplied in the TV_ITEM structure. The mask variable tells Windows that the hItem handle is valid going in and that text output is desired.



    Add code to the virtual OnDraw function in the file

    Ex08aView.cpp . Add the following boldface code:

    void CEx08aView::OnDraw(CDC* pDC)
    {
    CEx08aDoc* pDoc = GetDocument();
    ASSERT_VALID(pDoc);
    pDC->TextOut(0, 0, "Press the left mouse button here.");
    }



    Add the OnLButtonDown member function. Select the CEx08aView class in Class View. In the Properties window, click the Messages button, select the WM_LBUTTONDOWN message, and add the OnLButtonDown function. Add the following boldface code:

    void CEx08aView::OnLButtonDown(UINT nFlags, CPoint point)
    {
    CEx08aDialog dlg;
    dlg.m_nSlider1 = 20;
    dlg.m_nSlider2 = 2; // index for 8.0
    dlg.m_nProgress = 70; // write-only
    dlg.m_dSpin = 3.2;
    dlg.DoModal();
    CView::OnLButtonDown(nFlags, point);
    }

    In

    Ex08aView.cpp , add a statement to include

    Ex08aDialog.h :


    #include "Ex08aDialog.h"



    Compile and run the program. Experiment with the controls to see how they work. We haven't added code to make the progress indicator functional; we'll cover that in Chapter 13.




/ 319