久久精品国产精品国产精品污,男人扒开添女人下部免费视频,一级国产69式性姿势免费视频,夜鲁夜鲁很鲁在线视频 视频,欧美丰满少妇一区二区三区,国产偷国产偷亚洲高清人乐享,中文 在线 日韩 亚洲 欧美,熟妇人妻无乱码中文字幕真矢织江,一区二区三区人妻制服国产

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

第12章 对话框

發布時間:2023/12/14 编程问答 17 豆豆
生活随笔 收集整理的這篇文章主要介紹了 第12章 对话框 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

如果有很多輸入超出了菜單可以處理的程度,那么我們可以使用對話框來取得輸入信息。程序寫作者可以通過在某選項后面加上省略號(…)來表示該菜單項將啟動一個對話框。

對話框的一般形式是包含多種子窗口控件的彈出式窗口,這些控件的大小和位置在程序資源描述文件的「對話框模板」中指定。雖然程序寫作者能夠「手工」定義對話框模板,但是現在通常是在Visual C++ Developer Studio中以交談式操作的方式設計的,然后由Developer Studio建立對話框模板。

當程序呼叫依據模板建立的對話框時,Microsoft Windows 98負責建立彈出式對話框窗口和子窗口控件,并提供處理對話框消息(包括所有鍵盤和鼠標輸入)的窗口消息處理程序。有時候稱呼完成這些功能的Windows內部程序代碼為「對話框管理器」。

Windows的內部對話框窗口消息處理程序所處理的許多消息也傳遞給您自己程序中的函數,這個函數即是所謂的「對話框程序」或者「對話程序」。對話程序與普通的窗口消息處理程序類似,但是也存在著一些重要區別。一般來說,除了在建立對話框時初始化子窗口控件,處理來自子窗口控件的消息以及結束對話框之外,程序寫作者不需要再給對話框程序增加其它功能。對話程序通常不處理WM_PAINT消息,也不直接處理鍵盤和鼠標輸入。

對話框這個主題的含義太廣了,因為它還包含子窗口控件的使用。不過,我們已經在第九章研究了子窗口控件。當您在對話框中使用子窗口控件時,第九章所提到的許多工作都可以由Windows的對話框管理器來完成。尤其是,在程序COLORS1中遇到在滾動條之間切換輸入焦點的問題也不會在對話框中出現。Windows會處理對話框中的控件之間切換輸入焦點所必需完成的全部工作。

不過,在程序中添加對話框要比添加圖標或者菜單更麻煩一些。我們將從一個簡單的對話框開始,讓您對各部分之間的相互聯系有所了解。

模態對話框

對話框分為兩類:「模態的」和「非模態的」,其中模態對話框最為普遍。當您的程序顯示一個模態對話框時,使用者不能在對話框與同一個程序中的另一個窗口之間進行切換,使用者必須主動結束該對話框,這藉由通過按一下「OK」或者「Cancel」鍵來完成。不過,在顯示模態對話框時,使用者通??梢詮哪壳暗某绦蚯袚Q到另一個程序。而有些對話框(稱為「系統模態」)甚至連這樣的切換程序操作也不允許。在Windows中,顯示了系統模態對話框之后,要完成其它任何工作,都必須先結束該對話框。

建立「About」對話框

Windows程序即使不需要接收使用者輸入,也通常具有由菜單上的「About」選項啟動的對話框,該對話框用來顯示程序的名字、圖標、版權旗標和標記為「OK」的按鍵,也許還會有其它信息(例如技術支持的電話號碼)。我們將要看到的第一個程序除了顯示一個「About」對話框外,別無它用。這個ABOUT1程序如程序11-1所示:

程序11-1 ABOUT1

ABOUT1.C/*------------------------------------------------------------------------ABOUT1.C -- About Box Demo Program No. 1(c) Charles Petzold, 1998-------------------------------------------------------------------------*/#include <windows.h>#include "resource.h"LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM) ;BOOL CALLBACK AboutDlgProc (HWND, UINT, WPARAM, LPARAM) ;int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance,PSTR szCmdLine, int iCmdShow){static TCHAR szAppName[] = TEXT ("About1") ;MSG msg ;HWND hwnd ;WNDCLASS wndclass ;wndclass.style = CS_HREDRAW | CS_VREDRAW ;wndclass.lpfnWndProc = WndProc ;wndclass.cbClsExtra = 0 ;wndclass.cbWndExtra = 0 ;wndclass.hInstance = hInstance ;wndclass.hIcon = LoadIcon (hInstance, szAppName) ;wndclass.hCursor = LoadCursor (NULL, IDC_ARROW) ;wndclass.hbrBackground = (HBRUSH) GetStockObject (WHITE_BRUSH) ;wndclass.lpszMenuName = szAppName ;wndclass.lpszClassName = szAppName ;if (!RegisterClass (&wndclass)){MessageBox (NULL, TEXT ("This program requires Windows NT!"),szAppName, MB_ICONERROR) ;return 0 ;}hwnd = CreateWindow (szAppName, TEXT ("About Box Demo Program"),WS_OVERLAPPEDWINDOW,CW_USEDEFAULT, CW_USEDEFAULT,CW_USEDEFAULT, CW_USEDEFAULT,NULL, NULL, hInstance, NULL) ;ShowWindow (hwnd, iCmdShow) ;UpdateWindow (hwnd) ;while (GetMessage (&msg, NULL, 0, 0)){TranslateMessage (&msg) ;DispatchMessage (&msg) ;}return msg.wParam ;}LRESULT CALLBACK WndProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam){static HINSTANCE hInstance ;switch (message){case WM_CREATE :hInstance = ((LPCREATESTRUCT) lParam)->hInstance ;return 0 ;case WM_COMMAND :switch (LOWORD (wParam)){case IDM_APP_ABOUT :DialogBox (hInstance, TEXT ("AboutBox"), hwnd, AboutDlgProc) ;break ;}return 0 ;case WM_DESTROY :PostQuitMessage (0) ;return 0 ;}return DefWindowProc (hwnd, message, wParam, lParam) ;}BOOL CALLBACK AboutDlgProc (HWND hDlg, UINT message,WPARAM wParam, LPARAM lParam){switch (message){case WM_INITDIALOG :return TRUE ;case WM_COMMAND :switch (LOWORD (wParam)){case IDOK :case IDCANCEL :EndDialog (hDlg, 0) ;return TRUE ;}break ;}return FALSE ;}

ABOUT1.RC (摘錄)

//Microsoft Developer Studio generated resource script.#include "resource.h"#include "afxres.h"/// DialogABOUTBOX DIALOG DISCARDABLE 32, 32, 180, 100STYLE DS_MODALFRAME | WS_POPUPFONT 8, "MS Sans Serif"BEGINDEFPUSHBUTTON "OK",IDOK,66,80,50,14ICON "ABOUT1",IDC_STATIC,7,7,21,20CTEXT "About1",IDC_STATIC,40,12,100,8CTEXT "About Box Demo Program",IDC_STATIC,7,40,166,8CTEXT "(c) Charles Petzold,1998",IDC_STATIC,7,52,166,8END/// MenuABOUT1 MENU DISCARDABLEBEGINPOPUP "&Help"BEGINMENUITEM "&About About1...", IDM_APP_ABOUTENDEND/// IconABOUT1 ICON DISCARDABLE "About1.ico"

RESOURCE.H (摘錄)

// Microsoft Developer Studio generated include file.// Used by About1.rc#define IDM_APP_ABOUT 40001#define IDC_STATIC -1

ABOUT1.ICO


 

?

藉由后面章節中介紹的方法,您還可以在程序中建立圖標和菜單。圖示和菜單的ID名均為「About1」。菜單有一個選項,它產生一條ID名為IDM_APP_ABOUT的WM_COMMAND消息。這使得程序顯示的圖11-1所示的對話框。


 

?

圖11-1 程序ABOUT1的對話框

對話框及其模板

要把一個對話框添加到Visual C++ Developer Studio會有的應用程序上,可以先從Insert菜單中選擇 Resource,然后選擇Dialog Box?,F在一個對話框出現在您的眼前,該對話框帶有標題列、標題(Dialog)以及 OKCancel按鈕。Controls工具列允許您在對話框中插入不同的控件。

Developer Studio將對話框的ID設為標準的IDD_DIALOG1。您可以在此名稱上(或者在對話框本身)單擊右鍵,然后從菜單中選擇 Properties。在本程序中,將ID改為「AboutBox」(帶有引號)。為了與我建立的對話框保持一致,請將 X PosY Pos字段改為32。這表示對話框相對于程序窗口顯示區域左上角的顯示位置待會會有關于對話框坐標的詳細討論)。

現在,繼續在Properties對話框中選擇Styles頁面標簽。因為此對話框沒有標題列,所以不要選取 Title Bar復選框。然后請單擊Properties對話框的 關閉按鈕。

現在可以設計對話框了。因為不需要Cancel按鈕,所以先單擊該按鈕,然后按下鍵盤上的 Delete鍵。接著單擊OK按鈕,將其移動到對話框的底部。在Developer Studio窗口下面的工具列上有一個小位圖,它可使控件在窗口內水平居中對齊,請按下此鈕。

如果您要讓程序的圖標出現在對話框中,可以這樣做:先在浮動的Controls工具列中按下「 Pictures」按鈕。將鼠標移動到對話框的表面,按下左鍵,然后拉出一個矩形。這就是圖標將出現的位置。然后在次矩形上按下鼠標右鍵,從菜單中選擇 Properties。保持IDIDC_STATIC。此標識符在RESOURCE.H中定義為-1,用于程序中不使用的所有ID。將 Type改為Icon。您可以在Image字段輸入程序圖標的名稱,或者,如果您已經建立了一個圖示,那么您也可以從下拉式清單方塊中選擇一個名稱(About1)。

對于對話框中的三個靜態字符串,可以從Controls工具列中選擇 Static Text,然后確定文字在對話框中的位置。右鍵單擊控件,然后從菜單中選擇 Properties。在Properties框的 Caption字段中輸入要顯示的文字。選擇Styles頁面標簽,從 Align Text字段選擇Center

在添加這些字符串的時候,若希望對話框可以更大一些,請先選中對話框,然后拖曳邊框。您也可以選擇并縮放控件。通常用鍵盤上的光標移動鍵完成此操作會更容易些。箭頭鍵本身移動控件,按下Shift鍵后按箭頭鍵,可以改變控件的大小。所選控件的坐標和大小顯示在Developer Studio窗口的右下角。

如果您建立了一個應用程序,那么以后在查看資源描述檔ABOUT1.RC時,您將發現Developer Studio建立的模板。我所設計的對話框模板如下:

ABOUTBOX DIALOG DISCARDABLE 32, 32, 180, 100STYLE DS_MODALFRAME | WS_POPUPFONT 8, "MS Sans Serif"BEGINDEFPUSHBUTTON "OK",IDOK,66,80,50,14ICON "ABOUT1",IDC_STATIC,7,7,21,20CTEXT "About1",IDC_STATIC,40,12,100,8CTEXT "About Box Demo Program",IDC_STATIC,7,40,166,8CTEXT "(c) Charles Petzold, 1998",IDC_STATIC,7,52,166,8END

第一行給出了對話框的名稱(這里為ABOUTBOX)。如同其它資源,您也可以使用數字作為對話框的名稱。名稱后面是關鍵詞DIALOG和DISCARDABLE以及四個數字。前兩個數字是對話框左上角的x、y坐標,該坐標在程序呼叫對話框時,是相對于父窗口顯示區域的。后兩個數字是對話框的寬度和高度。

這些坐標和大小的單位都不是圖素。它們實際上依據一種特殊的坐標系統,該系統只用于對話框模板。數字依據對話框使用字體的大小而定(這里是8點的MS Sans Serif字體):x坐標和寬度的單位是字符平均寬度的1/4;y坐標和高度的單位是字符高度的1/8。因此,對這個對話框來說,對話框左上角距離主窗口顯示區域的左邊是5個字符,距離頂邊是2-1/2個字符。對話框本身寬40個字符,高10個字符。

這樣的坐標系使得程序寫作者可以使用坐標和大小來大致勾勒對話框的尺寸和外觀,而不管視訊顯示器的分辨率是多少。由于系統字體字符的高度大致為其寬度的兩倍,所以,x軸和y軸的量度差不多相等。

模板中的STYLE敘述類似于CreateWindow呼叫中的style字段。對于模態對話框,通常使用WS_POPUP和DS_MODALFRAME,我們將在稍后介紹其它的選項。

在BEGIN和END敘述(或者是左右大括號,手工設計對話框模板時,您可能會使用)之間,定義出現在對話框中的子窗口控件。這個對話框使用了三種型態的子窗口控件,它們分別是DEFPUSHBUTTON(內定按鍵)、ICON(圖標)和CTEXT(文字居中)。這些敘述的格式為:

control-type "text" id, xPos, yPos, xWidth, yHeight, iStyle

其中,后面的iStyle項是可選的,它使用Windows表頭文件中定義的標識符來指定其它窗口樣式。

DEFPUSHBUTTON、ICON和CTEXT等標識符只可以在對話框中使用,它們是某種特定窗口類別和窗口樣式的縮寫。例如,CTEXT指示這個子窗口控件類別是「靜態的」,其樣式為:

WS_CHILD | SS_CENTER | WS_VISIBLE | WS_GROUP

雖然前面沒有出現過WS_GROUP標識符,但是在第九章的COLORS1程序中已經出現過WS_CHILD、SS_CENTER和WS_VISIBLE窗口樣式,我們在建立靜態子窗口文字控件時已經用到了它們。

對于圖標,文字字段是程序的圖標資源名稱,它也在ABOUT1資源描述檔中定義。對于按鍵,文字字段是出現在按鍵里的文字,這個文字相同于在程序中建立子窗口控件時呼叫CreateWindow所指定的第二個參數。

id字段是子窗口在向其父窗口發送消息(通常為WM_COMMMAND消息)時用來標示它自身的值。這些子窗口控件的父窗口就是對話框本身,它將這些消息發送給Windows的一個窗口消息處理程序。不過,這個窗口消息處理程序也將這些消息發送給您在程序中給出的對話框程序。ID值相同于我們在第九章建立子窗口時,在CreateWindow函數中使用的子窗口ID。由于文字和圖標控件不向父窗口回送消息,所以這些值被設定為IDC_STATIC,它在RESOURCE.H中定義為-1。按鍵的ID值為IDOK,它在WINUSER.H中定義為1。

接下來的四個數字設定子窗口的位置(相對于對話框顯示區域的左上角)和大小,它們是以系統字體平均寬度的1/4和平均高度的1/8為單位來表示的。對于ICON敘述,寬度和高度將被忽略。

對話框模板中的DEFPUSHBUTTON敘述,除了包含DEFPUSHBUTTON關鍵詞所隱含的窗口樣式,還包含窗口樣式WS_GROUP。稍后討論該程序的第二個版本ABOUT2時,還會詳細說明WS_GROUP(以及相關的WS_TABSTOP樣式)。

對話框程序

您程序內的對話框程序處理傳送給對話框的消息。盡管看起來很像是窗口消息處理程序,但是它并不是真實的窗口消息處理程序。對話框的窗口消息處理程序在Windows內部定義,這個窗口過程調用您編寫的對話框程序,把它所接收到的許多消息作為參數。下面是ABOUT1的對話框程序:

BOOL CALLBACK AboutDlgProc (HWND hDlg, UINT message,WPARAM wParam, LPARAM lParam){switch (message){case WM_INITDIALOG :return TRUE ;case WM_COMMAND :switch (LOWORD (wParam)){case IDOK :case IDCANCEL :EndDialog (hDlg, 0) ;return TRUE ;}break ;}return FALSE ;}

該函數的參數與常規窗口消息處理程序的參數相同,與窗口消息處理程序類似,對話框程序都必須定義為一個CALLBACK(callback)函數。盡管我使用了hDlg作為對話框窗口的句柄,但是您也可以按照您自己的意思使用hwnd。首先,讓我們來看一下這個函數與窗口消息處理程序的區別:

  • 窗口消息處理程序傳回一個LRESULT。對話框傳回一個BOOL,它在Windows表頭文件中定義為int型態。
     
  • 如果窗口消息處理程序不處理某個特定的消息,那么它將呼叫DefWindowProc。如果對話框程序處理一個消息,那么它傳回TRUE(非0),如果不處理,則傳回FALSE(0)。
     
  • 對話框程序不需要處理WM_PAINT或WM_DESTROY消息。對話框程序不接收WM_CREAT消息,而是在特殊的WM_INITDIALOG消息處理期間,對話框程序執行初始化操作。
     

WM_INITDIALOG消息是對話框接收到的第一個消息,這個消息只發送給對話框程序。如果對話框程序傳回TRUE,那么Windows將輸入焦點設定給對話框中第一個具有WS_TABSTOP樣式(我們將在ABOUT2的討論中加以解釋)的子窗口控件。在這個對話框中,第一個具有WS_TABSTOP樣式的子窗口控件是按鍵。另外,對話框程序也可以在處理WM_INITDIALOG時使用SetFocus來將輸入焦點設定為對話框中的某個子窗口控件,然后傳回FALSE。

此外,對話框程序只處理WM_COMMAND消息。這是當按鍵被鼠標點中,或者在按鈕具有輸入焦點的情況下按下空格鍵時,按鍵控件發送給其父窗口的消息。這個控件的ID(我們在對話框模板中將其設定為IDOK)在wParam的低字組中。對于這個消息,對話框過程調用EndDialog,它告訴Windows清除對話框。對于所有其它消息,對話框程序傳回FALSE,并告訴Windows內部的對話框窗口消息處理程序:我們的對話框程序不處理這些消息。

模態對話框的消息不通過您程序的消息隊列,所以不必擔心對話框中鍵盤快捷鍵的影響。

激活對話框

在WndProc中處理WM_CREATE消息時,ABOUT1取得程序的執行實體句柄并將它放在靜態變量中:

hInstance = ((LPCREATESTRUCT) lParam)->hInstance ;

ABOUT1檢查WM_COMMAND消息,以確保消息wParam的低位字等于IDM_APP_ABOUT。當它獲得這樣一個消息時,程序呼叫DialogBox:

DialogBox (hInstance, TEXT ("AboutBox"), hwnd, AboutDlgProc) ;

該函數需要執行實體句柄(在處理WM_CREATE時儲存的)、對話框名稱(在資源描述文件中定義的)、對話框的父窗口(也是程序的主窗口)和對話框程序的地址。如果您使用一個數字而不是對話框模板名稱,那么可以用MAKEINTRESOURCE宏將它轉換為一個字符串。

從菜單中選擇「About About1」,將顯示圖11-2所示的對話框。您可以使用鼠標單擊「OK」按鈕、按空格鍵或者按Enter鍵來結束這個對話框。對任何包含內定按鈕的對話框,在按下Enter鍵或空格鍵之后,Windows發送一個WM_COMMAND消息給對話框,并令wParam的低字組等于內定按鍵的ID,此時的ID為IDOK。按下Escape鍵也可以關閉對話框,這時Windows將發送一個WM_COMMAND消息,并令ID等于IDCANCEL。

直到對話框結束之后,用來顯示對話框的DialogBox才將控制權傳回給WndProc。DialogBox的傳回值是對話框程序內部呼叫的EndDialog函數的第二個參數(這個值未在ABOUT1中使用,但會在ABOUT2中使用)。然后,WndProc可以將控制權傳回給Windows。

即使在顯示對話框時,WndProc也可以繼續接收消息。實際上,您可以從對話框程序內部給WndProc發送消息。ABOUT1的主窗口是彈出式對話框窗口的父窗口,所以AboutDlgProc中的SendMessage呼叫可以使用如下敘述來開始:

SendMessage (GetParent (hDlg), . . . ) ;

不同的主題

雖然Visual C++ Developer Studio中的對話框編輯器和其它資源編輯器,使我們幾乎不用考慮資源描述的寫作問題,但是學習一些資源描述的語法還是有用的。尤其對于對話框模板來說,知道了語法,您就可以近一步了解對話框的范圍和限制。甚至當它不能滿足您的需要時,您還可以自己建立一個對話框模板(就像本章后面的HEXCALC程序)。資源編譯器和資源描述語法的文件位于/Platform SDK/Windows Programming Guidelines/Platform SDK Tools/Compiling/Using the Resource Compiler。

在Developer Studio的「Properties」對話框中指定了對話框的窗口樣式,它翻譯成對話框模板中的STYLE敘述。對于ABOUT1,我們使用模態對話框最常用的樣式;

STYLE WS_POPUP | DS_MODALFRAME

然而,您也可以嘗試其它樣式。有些對話框有標題列,標題列用于指出對話框的用途,并允許使用者通過鼠標在顯示屏上移動對話框。此樣式為WS_CAPTION。如果您使用WS_CAPTION,那么DIALOG敘述中所指定的x坐標和y坐標是對話框顯示區域的坐標,并相對于父窗口顯示區域的左上角。標題列將在y坐標之上顯示。

如果使用了標題列,那么您可以用CAPTION敘述將文字放入標題中。在對話框模板中,CAPTION敘述在STYLE敘述的后面:

CAPTION "Dialog Box Caption"

另外,在對話框程序處理WM_INITDIALOG消息處理期間,您還可以呼叫:

SetWindowText (hDlg, TEXT ("Dialog Box Caption")) ;

如果您使用WS_CAPTION樣式,也可以添加一個WS_SYSMENU樣式的系統菜單按鈕。此樣式允許使用者從系統菜單中選擇 MoveClose

Properties對話框的Border清單方塊中選擇 Resizing(相同于樣式WS_THICKFRAME),允許使用者縮放對話框,僅管此操作并不常用。如果您不介意更特殊一點的話,還可以著為此對話框樣式添加最大化方塊。

您甚至可以給對話框添加一個菜單。這時對話框模板將包括下面的敘述:

MENU menu-name

其參數不是菜單的名稱,就是資源描述中的菜單號。模態對話框很少使用菜單。如果使用了菜單,那么您必須確保菜單和對話框控件中的所有ID都是唯一的;或者不是唯一的,卻表達了相同的命令。

FONT敘述使您可以設定非系統字體,以供對話框文字使用。這在過去的對話框中不常用,但現在卻非常普遍。事實上,在內定情況下,Developer Studio為您建立的每一個對話框都選用8點的MS Sans Serif字體。一個Windows程序能把自己外觀打點得非常與眾不同,這只需為程序的對話框及其它文字輸出單獨準備一種字體即可。

盡管對話框窗口消息處理程序通常位于Windows內部,但是您也可以使用自己編寫的窗口消息處理程序來處理對話框消息。要這樣做,您必須在對話框模板中指定一個窗口類別名:

CLASS "class-name"

這種用法很少見,但是在本章后面所示的HEXCALC程序中我們將用到它。

當您使用對話框模板的名稱來呼叫DialogBox時,Windows通過呼叫普通的CreateWindow函數來完成建立彈出式窗口所需要完成的一切操作。Windows從對話框模板中取得窗口的坐標、大小、窗口樣式、標題和菜單,從DialogBox的參數中獲得執行實體句柄和父窗口句柄。它所需要的唯一其它信息是一個窗口類別(假設對話框模板不指定窗口類別的話)。Windows為對話框注冊一個專用的窗口類別,這個窗口類別的窗口消息處理程序可以存取對話框程序地址(該地址是您在DialogBox呼叫中指定的),所以它可以使程序獲得該彈出式窗口所接收的消息。當然,您可以通過自己建立彈出式窗口來建立和維護自己的對話框。不過,使用DialogBox則更簡單。

也許您希望受益于Windows對話框管理器,但不希望(或者能夠)在資源描述中定義對話框模板,也可能您希望程序在執行時可以動態地建立對話框。這時可以完成這種功能的函數是DialogBoxIndirect,此函數用數據結構來定義模板。

在ABOUT1.RC的對話框模板中,我們使用縮寫CTEXT、ICON和DEFPUSHBUTTON來定義對話框所需要的三種型態的子窗口控件。您還可以使用其它型態,每種型態都隱含一個特定的預先定義窗口類別和一種窗口樣式。下表顯示了與一些控件型態相同的窗口類別和窗口樣式:

表 11-1

?

控件型態

窗口類別

窗口樣式

PUSHBUTTON

按鈕

BS_PUSHBUTTON | WS_TABSTOP

DEFPUSHBUTTON

按鈕

BS_DEFPUSHBUTTON | WS_TABSTOP

CHECKBOX

按鈕

BS_CHECKBOX | WS_TABSTOP

RADIOBUTTON

按鈕

BS_RADIOBUTTON | WS_TABSTOP

GROUPBOX

按鈕

BS_GROUPBOX | WS_TABSTOP

LTEXT

靜態文字

SS_LEFT | WS_GROUP

CTEXT

靜態文字

SS_CENTER | WS_GROUP

RTEXT

靜態文字

SS_RIGHT | WS_GROUP

ICON

靜態圖標

SS_ICON

EDITTEXT

編輯

ES_LEFT | WS_BORDER | WS_TABSTOP

SCROLLBAR

滾動條

SBS_HORZ

LISTBOX

清單方塊

LBS_NOTIFY | WS_BORDER | WS_VSCROLL

COMBOBOX

下拉式清單方塊

CBS_SIMPLE | WS_TABSTOP

資源編譯器是唯一能夠識別這些縮寫的程序。除了表中所示的窗口樣式外,每個控件還具有下面的樣式:

WS_CHILD | WS_VISIBLE

對于這些控件型態,除了EDITTEXT、SCROLLBAR、LISTBOX和COMBOBOX之外,控件敘述的格式為:

control-type "text", id, xPos, yPos, xWidth, yHeight, iStyle

對于EDITTEXT、SCROLLBAR、LISTBOX和COMBOBOX,其格式為:

control-type id, xPos, yPos, xWidth, yHeight, iStyle

其中沒有文字字段。在這兩種敘述中,iStyle參數都是選擇性的。

在第九章,我討論了確定預先定義子窗口的寬度和高度的規則。您可能需要回到第九章去參考這些規則,這時請記住:對話框模板中指定大小的單位為平均字符寬度的1/4,及平均字符高度的1/8。

控件敘述的style字段是可選的。它允許您包含其它窗口樣式標識符。例如,如果您想建立在正方形框左邊包含文字的復選框,那么可以使用:

CHECKBOX "text", id, xPos, yPos, xWidth, yHeight, BS_LEFTTEXT

注意,控件型態EDITTEXT會自動添加一個邊框。如果您想建立一個沒有邊框的子窗口編輯控件,您可以使用:

EDITTEXT id, xPos, yPos, xWidth, yHeight, NOT WS_BORDER

資源編譯器也承認與下面敘述類似的專用控件敘述:

CONTROL "text", id, "class", iStyle, xPos, yPos, xWidth, yHeight

此敘述允許您通過指定窗口類別和完整的窗口樣式,來建立任意型態的子窗口控件。例如,要取代:

PUSHBUTTON "OK", IDOK, 10, 20, 32, 14

您可以使用:

CONTROL "OK", IDOK, "button", WS_CHILD | WS_VISIBLE |BS_PUSHBUTTON | WS_TABSTOP, 10, 20, 32, 14

當編譯資源描述檔時,這兩條敘述在.RES和.EXE文件中的編碼是相同的。在Developer Studio中,您可以使用Controls工具列中的Custom Control選項來建立此敘述。在ABOUT3程序中,我向您展示了如何用此選項建立一個控件,且在您的程序中已定義了該控件的窗口類別。

當您在對話框模板中使用CONTROL敘述時,不必包含WS_CHILD和WS_VISIBLE樣式。在建立子窗口時,Windows已經包含了這些窗口樣式。CONTROL敘述的格式也說明Windows對話框管理器在建立對話框時就完成了此項操作。首先,就像我前面所討論的,它建立一個彈出式窗口,其父窗口句柄在DialogBox函數中提供。然后,對話框管理器為對話框模板中的每個控件建立一個子窗口。所有這些控件的父窗口均是這個彈出式對話框。上面給出的CONTROL敘述被轉換成一個CreateWindow呼叫,形式如下所示:

hCtrl =CreateWindow (TEXT ("button"), TEXT ("OK"),WS_CHILD | WS_VISIBLE | WS_TABSTOP | BS_PUSHBUTTON,10 * cxChar / 4, 20 * cyChar / 8,32 * cxChar / 4, 14 * cyChar / 8,hDlg, IDOK, hInstance, NULL) ;

其中,cxChar和cyChar是系統字體字符的寬度和高度,以圖素為單位。hDlg參數是從建立該對話框窗口的CreateWindow呼叫傳回的值;hInstance參數是從DialogBox呼叫獲得的。

更復雜的對話框

ABOUT1中的簡單對話框展示了設計和執行一個對話框的要點,現在讓我們來看一個稍微復雜的例子。程序11-2給出的ABOUT2程序展示了如何在對話框程序中管理控件(這里用單選按鈕)以及如何在對話框的顯示區域中繪圖。

程序11-2 ABOUT2

ABOUT2.C/*--------------------------------------------------------------------------ABOUT2.C -- About Box Demo Program No. 2(c) Charles Petzold, 1998---------------------------------------------------------------------------*/#include <windows.h>#include "resource.h"LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM) ;BOOL CALLBACK AboutDlgProc (HWND, UINT, WPARAM, LPARAM) ;int iCurrentColor = IDC_BLACK,iCurrentFigure = IDC_RECT ;int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance,PSTR szCmdLine, int iCmdShow){static TCHAR szAppName[] = TEXT ("About2") ;MSG msg ;HWND hwnd ;WNDCLASS wndclass ;wndclass.style = CS_HREDRAW | CS_VREDRAW ;wndclass.lpfnWndProc = WndProc ;wndclass.cbClsExtra = 0 ;wndclass.cbWndExtra = 0 ;wndclass.hInstance = hInstance ;wndclass.hIcon = LoadIcon (hInstance, szAppName) ;wndclass.hCursor = LoadCursor (NULL, IDC_ARROW) ;wndclass.hbrBackground = (HBRUSH) GetStockObject (WHITE_BRUSH) ;wndclass.lpszMenuName = szAppName ;wndclass.lpszClassName = szAppName ;if (!RegisterClass (&wndclass)){MessageBox ( NULL, TEXT ("This program requires Windows NT!"),szAppName, MB_ICONERROR) ;return 0 ;}hwnd = CreateWindow ( szAppName, TEXT ("About Box Demo Program"),WS_OVERLAPPEDWINDOW,CW_USEDEFAULT, CW_USEDEFAULT,CW_USEDEFAULT, CW_USEDEFAULT,NULL, NULL, hInstance, NULL) ; ShowWindow (hwnd, iCmdShow) ;UpdateWindow (hwnd) ;while (GetMessage (&msg, NULL, 0, 0)){TranslateMessage (&msg) ;DispatchMessage (&msg) ;}return msg.wParam ;}void PaintWindow (HWND hwnd, int iColor, int iFigure){static COLORREF crColor[8] = { RGB ( 0, 0, 0), RGB ( 0, 0, 255),RGB ( 0, 255, 0), RGB ( 0, 255, 255),RGB (255, 0, 0), RGB (255, 0, 255),RGB (255, 255, 0), RGB (255, 255, 255)} ;HBRUSH hBrush ;HDC hdc ;RECT rect ;hdc = GetDC (hwnd) ;GetClientRect (hwnd, &rect) ;hBrush = CreateSolidBrush (crColor[iColor - IDC_BLACK]) ;hBrush = (HBRUSH) SelectObject (hdc, hBrush) ;if (iFigure == IDC_RECT)Rectangle (hdc, rect.left, rect.top, rect.right, rect.bottom) ;elseEllipse (hdc, rect.left, rect.top, rect.right, rect.bottom) ;DeleteObject (SelectObject (hdc, hBrush)) ;ReleaseDC (hwnd, hdc) ;}void PaintTheBlock (HWND hCtrl, int iColor, int iFigure){InvalidateRect (hCtrl, NULL, TRUE) ;UpdateWindow (hCtrl) ;PaintWindow (hCtrl, iColor, iFigure) ;}LRESULT CALLBACK WndProc ( HWND hwnd, UINT message, WPARAM wParam,LPARAM lParam){static HINSTANCE hInstance ;PAINTSTRUCT ps ;switch (message){case WM_CREATE:hInstance = ((LPCREATESTRUCT) lParam)->hInstance ;return 0 ;case WM_COMMAND:switch (LOWORD (wParam)){case IDM_APP_ABOUT:if (DialogBox (hInstance, TEXT ("AboutBox"), hwnd, AboutDlgProc))InvalidateRect (hwnd, NULL, TRUE) ;return 0 ;}break ;case WM_PAINT:BeginPaint (hwnd, &ps) ;EndPaint (hwnd, &ps) ;PaintWindow (hwnd, iCurrentColor, iCurrentFigure) ;return 0 ;case WM_DESTROY:PostQuitMessage (0) ;return 0 ;}return DefWindowProc (hwnd, message, wParam, lParam) ;}BOOL CALLBACK AboutDlgProc (HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam){static HWND hCtrlBlock ;static int iColor, iFigure ;switch (message){case WM_INITDIALOG:iColor = iCurrentColor ;iFigure = iCurrentFigure ;CheckRadioButton (hDlg, IDC_BLACK, IDC_WHITE, iColor) ;CheckRadioButton (hDlg, IDC_RECT, IDC_ELLIPSE, iFigure) ;hCtrlBlock = GetDlgItem (hDlg, IDC_PAINT) ;SetFocus (GetDlgItem (hDlg, iColor)) ;return FALSE ;case WM_COMMAND:switch (LOWORD (wParam)){case IDOK:iCurrentColor = iColor ;iCurrentFigure = iFigure ;EndDialog (hDlg, TRUE) ;return TRUE ;case IDCANCEL:EndDialog (hDlg, FALSE) ;return TRUE ;case IDC_BLACK:case IDC_RED:case IDC_GREEN:case IDC_YELLOW:case IDC_BLUE:case IDC_MAGENTA:case IDC_CYAN:case IDC_WHITE:iColor = LOWORD (wParam) ;CheckRadioButton (hDlg, IDC_BLACK, IDC_WHITE, LOWORD (wParam)) ;PaintTheBlock (hCtrlBlock, iColor, iFigure) ;return TRUE ;case IDC_RECT:case IDC_ELLIPSE:iFigure = LOWORD (wParam) ;CheckRadioButton (hDlg, IDC_RECT, IDC_ELLIPSE, LOWORD (wParam)) ;PaintTheBlock (hCtrlBlock, iColor, iFigure) ;return TRUE ;}break ;case WM_PAINT:PaintTheBlock (hCtrlBlock, iColor, iFigure) ;break ;}return FALSE ;}

ABOUT2.RC (摘錄)

//Microsoft Developer Studio generated resource script.#include "resource.h"#include "afxres.h"/// DialogABOUTBOX DIALOG DISCARDABLE 32, 32, 200, 234STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTIONFONT 8, "MS Sans Serif"BEGINICON "ABOUT2",IDC_STATIC,7,7,20,20CTEXT "About2",IDC_STATIC,57,12,86,8CTEXT "About Box Demo Program",IDC_STATIC,7,40,186,8LTEXT "",IDC_PAINT,114,67,74,72GROUPBOX "&Color",IDC_STATIC,7,60,84,143RADIOBUTTON "&Black",IDC_BLACK,16,76,64,8,WS_GROUP | WS_TABSTOPRADIOBUTTON "B&lue",IDC_BLUE,16,92,64,8RADIOBUTTON "&Green",IDC_GREEN,16,108,64,8RADIOBUTTON "Cya&n",IDC_CYAN,16,124,64,8RADIOBUTTON "&Red",IDC_RED,16,140,64,8RADIOBUTTON "&Magenta",IDC_MAGENTA,16,156,64,8RADIOBUTTON "&Yellow",IDC_YELLOW,16,172,64,8RADIOBUTTON "&White",IDC_WHITE,16,188,64,8GROUPBOX "&Figure",IDC_STATIC,109,156,84,46,WS_GROUPRADIOBUTTON "Rec&tangle",IDC_RECT,116,172,65,8,WS_GROUP | WS_TABSTOPRADIOBUTTON "&Ellipse",IDC_ELLIPSE,116,188,64,8DEFPUSHBUTTON "OK",IDOK,35,212,50,14,WS_GROUPPUSHBUTTON "Cancel",IDCANCEL,113,212,50,14,WS_GROUPEND/// IconABOUT2 ICON DISCARDABLE "About2.ico"/// MenuABOUT2 MENU DISCARDABLEBEGINPOPUP "&Help"BEGINMENUITEM "&About", IDM_APP_ABOUTENDEND

RESOURCE.H (摘錄)

// Microsoft Developer Studio generated include file.// Used by About2.rc#define IDC_BLACK 1000#define IDC_BLUE 1001#define IDC_GREEN 1002#define IDC_CYAN 1003#define IDC_RED 1004#define IDC_MAGENTA 1005#define IDC_YELLOW 1006#define IDC_WHITE 1007#define IDC_RECT 1008#define IDC_ELLIPSE 1009#define IDC_PAINT 1010#define IDM_APP_ABOUT 40001#define IDC_STATIC -1

ABOUT2.ICO


 

?

ABOUT2中的About框有兩組單選按鈕。一組用來選擇顏色,另一組用來選擇是矩形還是橢圓形。所選的矩形或者橢圓顯示在對話框內,其內部以目前選擇的顏色著色。使用者按下「OK」按鈕后,對話框會終止,程序的窗口消息處理程序在它自己的顯示區域內繪出所選圖形。如果您按下「Cancel」,則主窗口的顯示區域會保持原樣。對話框如圖11-2所示。盡管ABOUT2使用預先定義的標識符IDOK和IDCANCEL作為兩個按鍵,但是每個單選按鈕均有自己的標識符,它們以前綴IDC開頭(用于控件的ID)。這些標識符在RESOURCE.H中定義。


 

?

圖11-2 ABOUT2程序的對話框

當您在ABOUT2對話框中建立單選按鈕時,請按顯示順序建立。這能保證Developer Studio依照順序定義標識符的值,程序將使用這些值。另外,每個單選按鈕都不要選中「Auto」選項?!窤uto Radio Button」需要的程序代碼較少,但基本上處理起來更深奧些。然后請依照ABOUT2.RC中的定義來設定它們的標識符。

選中「Properties」對話框中下列對象的「Group」選項:「OK」和「Cancel」按鈕、「Figure」分組方塊、每個分組方塊中的第一個單選按鈕(「Black」和「Rectangle」)。選中這兩個單選按鈕的「Tab Stop」復選框。

當您有全部控件在對話框中的近似位置和大小時,就可以從「Layout」菜單選擇「Tab Order」選項。按ABOUT2.RC資源描述中顯示的順序單擊每一個控件。

使用對話框控件

在第九章中,您會發現大多數子窗口控件發送WM_COMMAND消息給其父窗口(唯一例外的是滾動條控件)。您還看到,經由發送消息給子窗口控件,父窗口可以改變子窗口控件的狀態(例如,選擇或不選擇單選按鈕、復選框)。您也可以用類似方法在對話框程序中改變控件。例如,如果您設計了一系列單選按鈕,就可以發送消息給它們,以選擇或者不選擇這些按鈕。不過,Windows也提供了幾種使用對話框控件的簡單辦法。我們來看一看對話框程序與子窗口控件相互通信的方式。

ABOUT2的對話框模板顯示在程序11-2的ABOUT2.RC資源描述檔中。GROUPBOX控件只是一個帶標題(標題為「Color」或者「Figure」)的分組方塊,每組單選按鈕都由這樣的分組方塊包圍。前一組的八個單選按鈕是互斥的,第二組的兩個單選按鈕也是如此。

當用鼠標單擊其中一個單選按鈕時(或者當單選按鈕擁有輸入焦點時按空格鍵),子窗口向其父窗口發送一個WM_COMMAND消息,消息的wParam的低字組被設為控件的ID,wParam的高字組是一個通知碼,lParam值是控件的窗口句柄。對于單選按鈕,這個通知碼是BN_CLICKED或者0。然后Windows中的對話框窗口消息處理程序將這個WM_COMMAND消息發送給ABOUT2.C內的對話框程序。當對話框程序收到一個單選按鈕的WM_COMMAND消息時,它為此按鈕設定選中標記,并為組中其它按鈕清除選中標記。

您可能還記得在第九章中已經提過,選中和不選中按鈕均需要向子窗口控件發送BM_CHECK消息。要設定一個按鈕選中標記,您可以使用:

SendMessage (hwndCtrl, BM_SETCHECK, 1, 0) ;

要消除選中標記,您可以使用:

SendMessage (hwndCtrl, BM_SETCHECK, 0, 0) ;

其中hwndCtrl參數是子窗口按鈕控件的窗口句柄。

但是在對話框程序中使用這種方法是時有點問題的,因為您不知道所有單選按鈕的窗口句柄,只是從您獲得的消息中知道其中一個句柄。幸運的是,Windows為您提供了一個函數,可以用對話框句柄和控件ID來取得一個對話框控件的窗口句柄:

hwndCtrl = GetDlgItem (hDlg, id) ;

(您也可以使用如下函數,從窗口句柄中取得控件的ID值:

id = GetWindowLong (hwndCtrl, GWL_ID) ;

但是在大多數情況下這是不必要的。)

您會注意到,在程序11-2所示的表頭文件ABOUT2.H中,八種顏色的ID值是從IDC_BLACK到IDC_WHITE連續變化的,這種安排在處理來自單選按鈕的WM_COMMAND消息時將會很有用。在第一次嘗試選中或者不選中單選按鈕時,您可能會在對話框程序中編寫如下的程序:

static int iColor ;其它行程序case WM_COMMAND:switch (LOWORD (wParam)){其它行程序case IDC_BLACK:case IDC_RED:case IDC_GREEN:case IDC_YELLOW:case IDC_BLUE:case IDC_MAGENTA:case IDC_CYAN:case IDC_WHITE:iColor = LOWORD (wParam) ;for (i = IDC_BLACK, i <= IDC_WHITE, i++)SendMessage (GetDlgItem (hDlg, i),BM_SETCHECK, i == LOWORD (wParam), 0) ;return TRUE ;其它行程序

這種方法能讓人滿意地執行。您將新的顏色值儲存在iColor中,并且還建立了一個循環,輪流使用所有八種顏色的ID值。您取得每個單選按鈕控件的窗口句柄,并用SendMessage給每個句柄發送一條BM_SETCHECK消息。只有對于向對話框窗口消息處理程序發送WM_COMMAND消息的按鈕,這個消息的wParam值才被設定為1。

第一種簡化的方法是使用專門的對話框程序SendDlgItemMessage:

SendDlgItemMessage (hDlg, id, iMsg, wParam, lParam) ;

它相同于:

SendMessage (GetDlgItem (hDlg, id), id, wParam, lParam) ;

現在,循環將變成這樣:

for (i = IDC_BLACK, i <= IDC_WHITE, i++)SendDlgItemMessage (hDlg, i, BM_SETCHECK, i == LWORD (wParam), 0) ;

稍微有些改進。但是真正的重大突破要等到使用了CheckRadioButton函數時才會出現:

CheckRadioButton (hDlg, idFirst, idLast, idCheck) ;

這個函數將ID在idFirst到idLast之間的所有單選按鈕的選中標記都清除掉,除了ID為idCheck的單選按鈕,因為它是被選中的。這里,所有ID必須是連續的。從此我們可以完全擺脫循環,并使用:

CheckRadioButton (hDlg, IDC_BLACK, IDC_WHITE, LOWORD (wParam)) ;

這正是ABOUT2對話框程序所采用的方法。

在使用復選框時,也提供了類似的簡化函數。如果您建立了一個「CHECKBOX」對話框窗口控件,那么可以使用如下的函數來設定和清除選中標記:

CheckDlgButton (hDlg, idCheckbox, iCheck) ;

如果iCheck設定為1,那么按鈕被選中;如果設定為0,那么按鈕不被選中。您可以使用如下的方法來取得對話框中某個復選框的狀態:

iCheck = IsDlgButtonChecked (hDlg, idCheckbox) ;

在對話框程序中,您既可以將選中標記的目前狀態儲存在一個靜態變量中,又可以在收到一個WM_COMMAND消息后,使用如下方法觸發按鈕:

CheckDlgButton (hDlg, idCheckbox,!IsDlgButtonChecked (hDlg, idCheckbox)) ;

如果您定義了BS_AUTOCHECKBOX控件,那么完全沒有必要處理WM_COMMAND消息。在終止對話框之前,您只要使用IsDlgButtonChecked就可以取得按鈕目前的狀態。不過,如果您使用BS_AUTORADIOBUTTON樣式,那么IsDlgButtonChecked就不能令人滿意了,因為需要為每個單選按鈕都呼叫它,直到函數傳回TRUE。實際上,您還要攔截WM_COMMAND消息來追蹤按下的按鈕。

「OK」和「Cancel」按鈕

ABOUT2有兩個按鍵,分別標記為「OK」和「Cancel」。在ABOUT2.RC的對話框模板中,「OK」按鈕的ID值為IDOK(在WINUSER.H中被定義為1),「Cancel」按鈕的ID值為IDCANCEL(定義為2),「OK」按鈕是內定的:

DEFPUSHBUTTON "OK",IDOK,35,212,50,14PUSHBUTTON "Cancel",IDCANCEL,113,212,50,14

在對話框中,通常都這樣安排「OK」和「Cancel」按鈕:將「OK」按鈕作為內定按鈕有助于用鍵盤接口終止對話。一般情況下,您通過單擊兩個鼠標按鍵之一,或者當所期望的按鈕具有輸入焦點時按下Spacebar來終止對話框。不過,如果使用者按下Enter,對話框窗口消息處理程序也將產生一個WM_COMMAND消息,而不管哪個控件具有輸入焦點。wParam的低字組被設定為對話框中內定按鍵的ID值,除非另一個按鍵擁有輸入焦點。在后一種情況下,wParam的低字組被設定為具有輸入焦點之按鍵的ID值。如果對話框中沒有內定按鍵,那么Windows向對話框程序發送一個WM_COMMAND消息,消息中wParam的低字組被設定為IDOK。如果使用者按下Esc鍵或者Ctrl-Break鍵,那么Windows令wParam等于IDCANCEL,并給對話框程序發送一個WM_COMMAND消息。所以,您不用在對話框程序中加入單獨的處理鍵盤操作,因為通常終止對話框的按鍵會由Windows將這兩個按鍵動作轉換為WM_COMMAND消息。

AboutDlgProc函數通過呼叫EndDialog來處理這兩種WM_COMMAND消息:

switch (LWORD (wParam)){case IDOK:iCurrentColor = iColor ;iCurrentFigure = iFigure ;EndDialog (hDlg, TRUE) ;return TRUE ;case IDCANCEL :EndDialog (hDlg, FALSE) ;return TRUE ;

ABOUT2的窗口消息處理程序在程序的顯示區域中繪制矩形或橢圓時,使用了整體變量iCurrentColor和iCurrentFigure。AboutDlgProc在對話框中畫圖時使用了靜態區域變量iColor和iFigure。

注意EndDialog的第二個參數的值不同,這個值是在WndProc中作為原DialogBox函數的傳回值傳回的:

case IDM_ABOUT:if (DialogBox (hInstance, TEXT ("AboutBox"), hwnd, AboutDlgProc))InvalidateRect (hwnd, NULL, TRUE) ;return 0 ;

如果DialogBox傳回TRUE(非0),則意味著按下了「OK」按鈕,然后需要使用新的顏色來更新WndProc顯示區域。當AboutDlgProc收到一個WM_COMMAND消息并且消息的wParam的低字組等于IDOK時,AboutDlgProc將圖形和顏色儲存在整體變量iCurrentColor和iCurrentFigure中。如果DialogBox傳回FALSE,則主窗口繼續使用iCurrentColor和iCurrentFigure的原始設定。

TRUE和FALSE通常用于EndDialog呼叫中,以告知主窗口消息處理程序使用者是用「OK」還是用「Cancel」來終止對話框的。不過,EndDialog的參數實際上是一個int值,而DialogBox也傳回一個int值。所以,用這種方法能比僅用TRUE或者FALSE傳回更多的信息。

避免使用整體變量

在ABOUT2中使用整體變量可能會、也可能不會影響您。一些程序寫作者(包括我自己)較喜歡少用整體變量。ABOUT2中的整體變量iCurrentColor和iCurrentFigure看來使用得完全合法,因為它們必須同時在窗口消息處理程序和對話框程序中使用。不過,在一個有一大堆對話框的程序中,每個對話框都可能改變一堆變量的值,使整體變量的數量容易用得過多。

您可能更喜歡將程序中的對話框與數據結構相聯系,該數據結構含有對話框可以改變的所有變量。您將在typedef敘述中定義這些結構。例如,在ABOUT2中,可以定義與「About」方塊相聯系的結構:

typedef struct{int iColor, iFigure ;}ABOUTBOX_DATA ;

在WndProc中,您可以依據此結構來定義并初始化一個靜態變量:

static ABOUTBOX_DATA ad = { IDC_BLACK, IDC_RECT } ;

在WndProc中也是這樣,用ad.iColor和ad.iFigure替換了所有的iCurrentColor和iCurrentFigure。呼叫對話框時,使用DialogBoxParam而不用DialogBox。此函數的第五個參數可以是任意的32位值。一般來說,此值設定為指向一個結構的指針,在這里是WndProc中的ABOUTBOX_DATA結構。

case IDM_ABOUT:if (DialogBoxParam (hInstance, TEXT ("AboutBox"),hwnd, AboutDlgProc, &ad))InvalidateRect (hwnd, NULL, TRUE) ;return 0 ;

這是關鍵:DialogBoxParam的最后一個參數是作為WM_INITDIALOG消息中的lParam傳遞給對話框程序的。

對話框程序有兩個ABOUTBOX_DATA結構型態的靜態變量(一個結構和一個指向結構的指針):

static ABOUTBOX_DATA ad, * pad ;

在AboutDlgProc中,此定義代替了iColor和iFigure的定義。在WM_INITDIALOG消息的開始部分,對話框程序根據lParam設定了這兩個變量的值:

pad = (ABOUTBOX_DATA *) lParam ;ad = * pad ;

第一道敘述中,pad設定為lParam的指標。亦即,pad實際是指向在WndProc定義的ABOUTBOX_DATA結構。第二個參數完成了從WndProc中的結構,到DlgProc中的區域結構的字段對字段內容復制。

現在,除了使用者按下「OK」按鈕時所用的程序代碼以之外,所有的AboutDlgProc都用ad.iColor和ad.iFigure替換了iFigure和iColor。這時,將區域結構的內容復制回WndProc中的結構:

case IDOK:* pad = ad ;EndDialog (hDlg, TRUE) ;return TRUE ;

Tab停留和分組

在第九章,我們利用窗口子類別化為COLORS1增加功能,使我們能夠按下Tab鍵從一個滾動條轉移到另一個滾動條。在對話框中,窗口子類別化是不必要的,因為Windows完成了將輸入焦點從一個控件移動到另一個控件的所有工作。盡管如此,您必須在對話框模板中使用WS_TABSTOP和WS_GROUP窗口樣式達到此目的。對于所有想要使用Tab鍵存取的控件,都要在其窗口樣式中指定WS_TABSTOP。

如果參閱表11-1,您就會注意到許多控件將WS_TABSTOP定義為內定樣式,其它一些則沒有將它作為內定樣式。一般而言,不包含WS_TABSTOP樣式的控件(特別是靜態控件)不應該取得輸入焦點,因為即使有了輸入焦點,它們也不能完成操作。除非在處理WM_INITDIALOG消息時您將輸入焦點設定給一個特定的控件,并從消息中傳回FALSE。否則Windows將輸入焦點設定為對話框內第一個具有WS_TABSTOP樣式的控件。

Windows給對話框增加的第二個鍵盤接口包括光標移動鍵,這種接口對于單選按鈕有特殊的重要性。如果您使用Tab鍵移動到某一組內目前選中的單選按鈕,那么,就需要使用光標移動鍵,將輸入焦點從該單選按鈕移動到組內其它單選按鈕上。使用WS_GROUP窗口樣式即可獲得這個功能。對于對話框模板中的特定控件序列,Windows將使用光標移動鍵把輸入焦點從第一個具有WS_GROUP樣式的控制權切換到下一個具有WS_GROUP樣式的控件中。如果有必要,Windows將從對話框的最后一個控件循環到第一個控件,以便找到分組的結尾。

在內定設定下,控件LTEXT、CTEXT、RTEXT和ICON包含有WS_GROUP樣式,這種樣式方便地標記了分組的結尾。您必須經常將WS_GROUP樣式加到其它型態的控件中。

讓我們來看一看ABOUT2.RC中的對話框模板。四個具有WS_TABSTOP樣式的控件是每個組的第一個單選按鈕(明顯地包含)和兩個按鍵(內定設定)。在第一次啟動對話框時,您可以使用Tab鍵在這四個控件之間移動。

在每組單選按鈕中,您可以使用光標移動鍵切換輸入焦點并改變選中標記。例如, Color下拉式清單方塊的第一個單選按鈕(Black)和 Figure下拉式清單方塊都具有WS_GROUP樣式。這意味著您可以用光標移動鍵將焦點從「Black」單選按鈕移動到 Figure分組方塊中。類似的情形,Figure分組方塊的第一個單選按鈕( Rectangle)和DEFPUSHBUTTON都具有WS_GROUP樣式,所以您可以使用光標移動鍵在組內兩個單選按鈕- RectangleEllipse之間移動。兩個按鍵都有WS_GROUP樣式,以阻止光標移動鍵在按鍵具有輸入焦點時起作用。

使用ABOUT2時,Windows的對話框管理器在兩組單選按鈕中完成一些相當復雜的處理。正如所預期的那樣,處于單選按鈕組內時,光標移動鍵切換輸入焦點,并給對話框程序發送WM_COMMAND消息。但是,當您改變了組內選中的單選按鈕時,Windows也給新選中的單選按鈕設定了WS_TABSTOP樣式。當您下一次使用Tab切換到這一組后,Windows將會把輸入焦點設定為選中的單選按鈕。

文字字段中的「&」將導致緊跟其后的字母以底線顯示,這就增加了另一種鍵盤接口,您可以通過按底線字母來將輸入焦點移動到任意單選按鈕上。透過按下C(代表 Color下拉式清單方塊)或者F(代表Figure下拉式清單方塊),您可以將輸入焦點移動到相對應組內目前選中的單選按鈕上。

盡管程序寫作者通常讓對話框管理器來完成這些工作,但是Windows提供了兩個函數,以便程序寫作者找尋下一個或者前一個Tab鍵停留項或者組項。這些函數為:

hwndCtrl = GetNextDlgTabItem (hDlg, hwndCtrl, bPrevious) ;

hwndCtrl = GetNextDlgGroupItem (hDlg, hwndCtrl, bPrevious) ;

如果bPrevious為TRUE,那么函數傳回前一個Tab鍵停留項或組項;如果為FALSE,則傳回下一個Tab鍵停留項或者組項。

在對話框上畫圖

ABOUT2還完成了一些相對說來很特別的事情,亦即在對話框上畫圖。讓我們來看一看它是怎樣做的。在ABOUT2.RC的對話框模板內,使用位置和大小為我們想要畫圖的區域定義了一塊空白文字控件:

LTEXT "" IDC_PAINT, 114, 67, 72, 72

這個區域為18個字符寬和9個字符高。由于這個控件沒有文字,所以窗口消息處理程序為「靜態」類別所做的工作,只是在必須重繪這個子窗口控件時清除其背景。

在目前顏色或圖形選擇發生改變,或者對話框自身獲得一個WM_PAINT消息時,對話框過程調用PaintTheBlock,這個函數在ABOUT2.C中:

PaintTheBlock (hCtrlBlock, iColor, iFigure) ;

在AboutDlgProc中,窗口句柄hCtrlBlock已經在處理WM_INITDIALOG消息時被設定:

hCtrlBlock = GetDlgItem (hDlg, IDD_PAINT) ;

下面是PaintTheBlock函數:

void PaintTheBlock (HWND hCtrl, int iColor, int iFigure){InvalidateRect (hCtrl, NULL, TRUE) ;UpdateWindow (hCtrl) ;PaintWindow (hCtrl, iColor, iFigure) ;}

這個函數使得子窗口控件無效,并為控件窗口消息處理程序產生一個WM_PAINT消息,然后呼叫ABOUT2中的另一個函數PaintWindow 。

PaintWindow函數取得一個設備內容句柄,并將其放到hCtrl中,畫出所選圖形,根據所選顏色用一個著色畫刷填入圖形。子窗口控件的大小從GetClientRect獲得。盡管對話框模板以字符為單位定義了控件的大小,但GetClientRect取得以圖素為單位的尺寸。您也可以使用函數MapDialogRect將對話框中的字符坐標轉換為顯示區域中的圖素坐標。

我們并非真的繪制了對話框的顯示區域,實際繪制的是子窗口控件的顯示區域。每當對話框得到一個WM_PAINT消息時,就令子窗口控件的顯示區域失效,并更新它,使它確信現在其顯示區域又有效了,然后在其上畫圖。

將其它函數用于對話框

大多數可以用在子窗口的函數也可以用于對話框中的控件。例如,如果您想搗亂的話,那么可以使用MoveWindow在對話框內移動控件,強迫使用者用鼠標來追蹤它們。

有時,您需要根據其它控件的設定,動態地啟用或者禁用某些控件,這需要呼叫:

EnableWindow (hwndCtrl, bEnable) ;

當bEnable為TRUE(非0)時,它啟用控件;當bEnable為FALSE(0)時,它禁用控件。在控件被禁用時,它不再接收鍵盤或者鼠標輸入。您不能禁用一個擁有輸入焦點的控件。

定義自己的控件

盡管Windows承攬了許多維護對話框和子窗口控件的工作,它同時也為您提供了各種加入程序代碼的方法。前面我們已經看到了在對話框上繪圖的方法。您也可以使用第九章中討論的窗口子類別化來改變子窗口控件的操作。

您還可以定義自己的子窗口控件,并將它們用到對話框中。例如,假定您特別不喜歡普通的矩形按鍵,而傾向于建立橢圓形按鍵,那么您可以通過注冊一個窗口類別,并使用自己編寫的窗口消息處理程序處理來自您所建立窗口的消息,從而建立橢圓形按鍵。在Developer Studio中,您可以在與自訂控件相聯系的「Properties」對話框中指定這個窗口類別,這將轉換成對話框模板中的CONTROL敘述。程序11-3所示的ABOUT3程序正是這樣做的。

程序11-3 ABOUT3

ABOUT3.C/*-----------------------------------------------------------------------------ABOUT3.C -- About Box Demo Program No. 3(c) Charles Petzold, 1998----------------------------------------------------------------------------*/#include <windows.h>#include "resource.h"LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM) ;BOOL CALLBACK AboutDlgProc (HWND, UINT, WPARAM, LPARAM) ;LRESULT CALLBACK EllipPushWndProc (HWND, UINT, WPARAM, LPARAM) ;int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance,PSTR szCmdLine, int iCmdShow){static TCHAR szAppName[] = TEXT ("About3") ;MSG msg ;HWND hwnd ;WNDCLASS wndclass ;wndclass.style = CS_HREDRAW | CS_VREDRAW ;wndclass.lpfnWndProc = WndProc ;wndclass.cbClsExtra = 0 ;wndclass.cbWndExtra = 0 ;wndclass.hInstance = hInstance ;wndclass.hIcon = LoadIcon (hInstance, szAppName) ;wndclass.hCursor = LoadCursor (NULL, IDC_ARROW) ;wndclass.hbrBackground = (HBRUSH) GetStockObject (WHITE_BRUSH) ;wndclass.lpszMenuName = szAppName ;wndclass.lpszClassName = szAppName ;if (!RegisterClass (&wndclass)){MessageBox ( NULL, TEXT ("This program requires Windows NT!"),szAppName, MB_ICONERROR) ;return 0 ;}wndclass.style = CS_HREDRAW | CS_VREDRAW ;wndclass.lpfnWndProc = EllipPushWndProc ;wndclass.cbClsExtra = 0 ;wndclass.cbWndExtra = 0 ;wndclass.hInstance = hInstance ;wndclass.hIcon = NULL ;wndclass.hCursor = LoadCursor (NULL, IDC_ARROW) ;wndclass.hbrBackground = (HBRUSH) (COLOR_BTNFACE + 1) ;wndclass.lpszMenuName = NULL ;wndclass.lpszClassName = TEXT ("EllipPush") ;RegisterClass (&wndclass) ;hwnd = CreateWindow ( szAppName, TEXT ("About Box Demo Program"),WS_OVERLAPPEDWINDOW,CW_USEDEFAULT, CW_USEDEFAULT,CW_USEDEFAULT, CW_USEDEFAULT,NULL, NULL, hInstance, NULL) ;ShowWindow (hwnd, iCmdShow) ;UpdateWindow (hwnd) ;while (GetMessage (&msg, NULL, 0, 0)){TranslateMessage (&msg) ;DispatchMessage (&msg) ;}return msg.wParam ;}LRESULT CALLBACK WndProc ( HWND hwnd, UINT message, WPARAM wParam,LPARAM lParam){static HINSTANCE hInstance ;switch (message){case WM_CREATE :hInstance = ((LPCREATESTRUCT) lParam)->hInstance ;return 0 ;case WM_COMMAND :switch (LOWORD (wParam)){case IDM_APP_ABOUT :DialogBox (hInstance, TEXT ("AboutBox"), hwnd, AboutDlgProc) ;return 0 ;}break ;case WM_DESTROY :PostQuitMessage (0) ;return 0 ;}return DefWindowProc (hwnd, message, wParam, lParam) ;}BOOL CALLBACK AboutDlgProc (HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam){switch (message){case WM_INITDIALOG :return TRUE ;case WM_COMMAND :switch (LOWORD (wParam)){case IDOK :EndDialog (hDlg, 0) ;return TRUE ;}break ;}return FALSE ;}LRESULT CALLBACK EllipPushWndProc (HWND hwnd, UINT message,WPARAM wParam, LPARAM lParam){TCHAR szText[40] ;HBRUSH hBrush ;HDC hdc ;PAINTSTRUCT ps ;RECT rect ;switch (message){case WM_PAINT :GetClientRect (hwnd, &rect) ;GetWindowText (hwnd, szText, sizeof (szText)) ;hdc = BeginPaint (hwnd, &ps) ;hBrush = CreateSolidBrush (GetSysColor (COLOR_WINDOW)) ;hBrush = (HBRUSH) SelectObject (hdc, hBrush) ;SetBkColor (hdc, GetSysColor (COLOR_WINDOW)) ;SetTextColor (hdc, GetSysColor (COLOR_WINDOWTEXT)) ;Ellipse (hdc, rect.left, rect.top, rect.right, rect.bottom) ;DrawText (hdc, szText, -1, &rect,DT_SINGLELINE | DT_CENTER | DT_VCENTER) ;DeleteObject (SelectObject (hdc, hBrush)) ;EndPaint (hwnd, &ps) ;return 0 ;case WM_KEYUP :if (wParam != VK_SPACE)break ;// fall throughcase WM_LBUTTONUP :SendMessage (GetParent (hwnd), WM_COMMAND,GetWindowLong (hwnd, GWL_ID), (LPARAM) hwnd) ;return 0 ;}return DefWindowProc (hwnd, message, wParam, lParam) ;}

ABOUT3.RC (摘錄)

//Microsoft Developer Studio generated resource script.#include "resource.h"#include "afxres.h"/// DialogABOUTBOX DIALOG DISCARDABLE 32, 32, 180, 100STYLE DS_MODALFRAME | WS_POPUPFONT 8, "MS Sans Serif"BEGINCONTROL "OK",IDOK,"EllipPush",WS_GROUP | WS_TABSTOP,73,79,32,14ICON "ABOUT3",IDC_STATIC,7,7,20,20CTEXT "About3",IDC_STATIC,40,12,100,8CTEXT "About Box Demo Program",IDC_STATIC,7,40,166,8CTEXT "(c) Charles Petzold, 1998",IDC_STATIC,7,52,166,8END/// MenuABOUT3 MENU DISCARDABLEBEGINPOPUP "&Help"BEGINMENUITEM "&About About3...", IDM_APP_ABOUTENDEND/// IconABOUT3 ICON DISCARDABLE "icon1.ico"

RESOURCE.H (摘錄)

// Microsoft Developer Studio generated include file.// Used by About3.rc#define IDM_APP_ABOUT 40001#define IDC_STATIC -1

ABOUT3.ICO


 

?

我們所注冊的窗口類別叫做「EllipPush」(橢圓形按鍵)。在Developer Studio的對話框編輯器中,刪除「Cancel」和「OK」按鈕。要添加依據此窗口類別的控件,請從「 Controls」工具列選擇「Custom Control」。在此控件的「Properties」對話框的「 Class」字段輸入「EllipPush」。在對話框模板中我們沒有使用DEFPUSHBUTTON敘述,而是用CONTROL敘述來指定此窗口類別:

CONTROL "OK" IDOK, "EllipPush", TABGRP, 64, 60, 32, 14

當在對話框中建立子窗口控件時,對話框管理器把這個窗口類別用于CreateWindow呼叫中。

ABOUT3.C程序在WinMain中注冊了EllipPush窗口類別:

wndclass.style = CS_HREDRAW | CS_VREDRAW ;wndclass.lpfnWndProc = EllipPushWndProc ;wndclass.cbClsExtra = 0 ;wndclass.cbWndExtra = 0 ;wndclass.hInstance = hInstance ;wndclass.hIcon = NULL ;wndclass.hCursor = LoadCursor (NULL, IDC_ARROW) ;wndclass.hbrBackground = (HBRUSH) (COLOR_WINDOW + 1) ;wndclass.lpszMenuName = NULL ;wndclass.lpszClassName = TEXT ("EllipPush") ;RegisterClass (&wndclass) ;

該窗口類別指定窗口消息處理程序為EllipPushWndProc,在ABOUT3.C中正是這樣。

EllipPushWndProc窗口消息處理程序只處理三種消息:WM_PAINT、WM_KEYUP和WM_LBUTTONUP。在處理WM_PAINT消息時,它從GetClientRect中取得窗口的大小,從GetWindowText中取得顯示在按鍵上的文字,用Windows函數Ellipse和DrawText來輸出橢圓和文字。

WM_KEYUP和WM_LBUTTONUP消息的處理非常簡單:

case WM_KEYUP :if (wParam != VK_SPACE)break ; // fall throughcase WM_LBUTTONUP :SendMessage (GetParent (hwnd), WM_COMMAND,GetWindowLong (hwnd, GWL_ID), (LPARAM) hwnd) ;return 0 ;

窗口消息處理程序使用GetParent來取得其父窗口(即對話框)的句柄,并發送一個WM_COMMAND消息,消息的wParam等于控件的ID,這個ID是用GetWindowLong取得的。然后,對話框窗口消息處理程序將這個消息傳給ABOUT3內的對話框程序,結果得到一個使用者自訂的按鍵,如圖11-3所示。您可以用同樣的方法來建立其它自訂對話框控件。


 

?

圖11-3 ABOUT3建立的自訂按鍵

這就是全部要做的嗎?其實不然。通常,對于維護子窗口控件所需要的處理而言,EllipPushWndProc只是一個空架子。例如,按鈕不會像普通的按鍵那樣閃爍。要翻轉按鍵內的顏色,窗口消息處理程序必須處理WM_KEYDOWN(來自空格鍵)和WM_LBUTTONDOWN消息。窗口消息處理程序還必須在收到WM_LBUTTONDOWN消息時攔截鼠標,并且,如果當按鈕還處于按下狀態,而鼠標移到了子窗口的顯示區域之外,那么得要釋放鼠標攔截(并將按鈕的內部顏色回復為正常狀態)。只有在鼠標被攔截時松開該按鈕,子窗口才會給其父窗口送回一個WM_COMMAND消息。

EllipPushWndProc也不處理WM_ENABLE消息。如上所述,對話框程序可以使用EnableWindow函數來禁用某窗口。于是,子窗口將顯示灰色文字,而不再是黑色文字,以表示它已經被禁用,并且不能再接收任何消息了。

如果子窗口控件的窗口消息處理程序需要為所建立的每個窗口存放各自不同的數據,那么它可以通過使用窗口類別結構中的cbWndExtra值來做到。這樣就在內部窗口結構中保留了空間,并可以用SetWindowLong和GetWindowLong來存取該數據。

非模態對話框

在本章的開始,我曾經說過對話框分為「模態的」和「非模態的」兩種。現在我們已經研究過這兩種對話框中最常見的一種-模態對話框。模態對話框(不包括系統模態對話框)。允許使用者在對話框與其它程序之間進行切換。但是,使用者不能切換到同一程序的另一個窗口,直到模態對話框被清除為止。非模態對話框允許使用者在對話框與其它程序之間進行切換,又可以在對話框與建立對話框的窗口之間進行切換。因此,非模態對話框與使用者程序常見的普通彈出式窗口可能更為相似。

當使用者覺得讓對話框保留片刻會更加方便時,使用非模態對話框是合適的。例如,文書處理程序經常使用非模態對話框來進行「Find」和「Change」操作。如果「Find」對話框是模態的,那么使用者必須從菜單中選擇「Find」,然后輸入要尋找的字符串,結束對話框,傳回到文件中,接著再重復整個程序來尋找同一字符串的另一次出現。允許使用者在文件與對話框之間進行切換則會方便得多。

您已經看到,模態對話框是用DialogBox來建立的。只有在清除對話框之后,函數才會傳回值。在對話框程序內使用EndDialog呼叫來終止對話框,DialogBox傳回的是該呼叫的第二個參數的值。非模態對話框是使用CreateDialog來建立的,該函數所使用的參數與DialogBox相同。

hDlgModeless = CreateDialog ( hInstance, szTemplate,hwndParent, DialogProc) ;

區別是CreateDialog函數立即傳回對話框的窗口句柄,并通常將這個窗口句柄存放到整體變量中。

盡管將DialogBox這一名字用于模態對話框而CreateDialog用于非模態對話框是隨意的,但是您可以通過非模態對話框與普通窗口類似這一點來記住這兩個函數的區別。CreateDialog可以令人想起CreateWindow函數來,而后者建立的是普通窗口。

模態對話框與非模態對話框的區別

使用非模態對話框與使用模態對話框相似,但是也有一些重要的區別:

首先,非模態對話框通常包含一個標題列和一個系統菜單按鈕。當您在Developer Studio中建立對話框時,這些是內定選項。用于非模態對話框的對話框模板中的STYLE敘述形如:

STYLE WS_POPUP | WS_CAPTION | WS_SYSMENU | WS_VISIBLE

標題列和系統菜單允許使用者,使用鼠標或者鍵盤將非模態對話框移動到另一個顯示區域。對于模態對話框,您通常無須提供標題列和系統菜單,因為使用者不能在其下面的窗口中做任何其它的事情。

第二項重要的區別是:注意,在我們的范例STYLE敘述中包含有WS_VISIBLE樣式。在 Developer Studio中,從「Dialog Properties」對話框的「More Styles」頁面卷標中選擇此選項。如果省略了WS_VISIBLE,那么您必須在CreateDialog呼叫之后呼叫ShowWindow:

hDlgModeless = CreateDialog ( . . . ) ;ShowWindow (hDlgModeless, SW_SHOW) ;

如果您既沒有包含WS_VISIBLE樣式,又沒有呼叫ShowWindow,那么非模態對話框將不會被顯示。如果忽略這個事實,那么習慣于模態對話框的程序寫作者在第一次試圖建立非模態對話框時,經常會出現問題。

第三項區別:與模態對話框和消息框的消息不同,非模態對話框的消息要經過程序式的消息隊列。要將這些消息傳送給對話框窗口消息處理程序,則必須改變消息隊列。方法如下:當您使用CreateDialog建立非模態對話框時,應該將從呼叫中傳回的對話框句柄儲存在一個整體變量(如hDlgModeless)中,并將消息循環改變為:

while (GetMessage (&msg, NULL, 0, 0)){if (hDlgModeless == 0 || !IsDialogMessage (hDlgModeless, &msg)){TranslateMessage (&msg) ;DispatchMessage (&msg) ;}}

如果消息是發送給非模態對話框的,那么IsDialogMessage將它發送給對話框中窗口消息處理程序,并傳回TRUE(非0);否則,它將傳回FALSE(0)。只有hDlgModeless為0或者消息不是該對話框的消息時,才必須呼叫TranslateMessage和DispatchMessage函數。如果您將鍵盤快捷鍵用于您的程序窗口,那么消息循環將如下所示:

while (GetMessage (&msg, NULL, 0, 0)){if (hDlgModeless == 0 || !IsDialogMessage (hDlgModeless, &msg)){if (!TranslateAccelerator (hwnd, hAccel, &msg)){TranslateMessage (&msg) ;DispatchMessage (&msg) ;}}}

由于整體變量被初始化為0,所以hDlgModeless將為0,直到建立對話框為止,從而保證不會使用無效的窗口句柄來呼叫IsDialogMessage。在清除非模態對話框時,您也必須注意這一點,正如最后一點所說明的。

hDlgModeless變量也可以由程序的其它部分使用,以便對非模態對話框是否存在加以驗證。例如,程序中的其它窗口可以在hDlgModeless不等于0時給對話框發送消息。

最后一項重要的區別:使用DestroyWindow而不是EndDialog來結束非模態對話框。當您呼叫DestroyWindow后,將hDlgModeless整體變量設定為0。

使用者習慣于從系統菜單中選擇「Close」來結束非模態對話框。盡管啟用了「Close」選項,Windows內的對話框窗口消息處理程序并不處理WM_CLOSE消息。您必須自己在對話框程序中處理它:

case WM_CLOSE :DestroyWindow (hDlg) ;hDlgModeless = NULL ;break ;

注意這兩個窗口句柄之間的區別:DestroyWindow的hDlg參數是傳遞給對話框程序的參數;hDlgModeless是從CreateDialog傳回的整體變量,程序在消息循環內檢驗它。

您也可以允許使用者使用按鍵來關閉非模態對話框,處理方式與處理WM_CLOSE消息一樣。對話框必須傳回給建立它的窗口之任何數據都可以儲存在整體變量中。如果不喜歡使用整體變量,那么您也可以用CreateDialogParam來建立非模態對話框,并按前面介紹的方法讓它儲存一個結構指針。

新的COLORS程序

第九章中所描述的COLORS1程序建立了九個子窗口,以便顯示三個滾動條和六個文字項。那時候,這個程序還是我們所寫過的程序中相當復雜的一個。如果將COLORS1轉換為使用非模態對話框則會使程序-特別是WndProc函數-變得令人難以置信的簡單,修正后的COLORS2程序如程序11-4所示。

程序11-4 COLORS2

COLORS2.C/*----------------------------------------------------------------------------COLORS2.C -- Version using Modeless Dialog Box(c) Charles Petzold, 1998----------------------------------------------------------------------------*/#include <windows.h>LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM) ;BOOL CALLBACK ColorScrDlg (HWND, UINT, WPARAM, LPARAM) ;HWND hDlgModeless ;int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance,PSTR szCmdLine, int iCmdShow){static TCHAR szAppName[] = TEXT ("Colors2") ;HWND hwnd ;MSG msg ;WNDCLASS wndclass ;wndclass.style = CS_HREDRAW | CS_VREDRAW ;wndclass.lpfnWndProc = WndProc ;wndclass.cbClsExtra = 0 ;wndclass.cbWndExtra = 0 ;wndclass.hInstance = hInstance ;wndclass.hIcon = LoadIcon (NULL, IDI_APPLICATION) ;wndclass.hCursor = LoadCursor (NULL, IDC_ARROW) ;wndclass.hbrBackground = CreateSolidBrush (0L) ;wndclass.lpszMenuName = NULL ;wndclass.lpszClassName = szAppName ;if (!RegisterClass (&wndclass)){MessageBox ( NULL, TEXT ("This program requires Windows NT!"),szAppName, MB_ICONERROR) ;return 0 ;}hwnd = CreateWindow (szAppName, TEXT ("Color Scroll"),WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN,CW_USEDEFAULT, CW_USEDEFAULT,CW_USEDEFAULT, CW_USEDEFAULT,NULL, NULL, hInstance, NULL) ;ShowWindow (hwnd, iCmdShow) ;UpdateWindow (hwnd) ;hDlgModeless = CreateDialog (hInstance, TEXT ("ColorScrDlg"),hwnd, ColorScrDlg) ;while (GetMessage (&msg, NULL, 0, 0)){if (hDlgModeless == 0 || !IsDialogMessage (hDlgModeless, &msg)){TranslateMessage (&msg) ;DispatchMessage (&msg) ;}}return msg.wParam ;}LRESULT CALLBACK WndProc (HWND hwnd, UINT message, WPARAM wParam,LPARAM lParam){switch (message){case WM_DESTROY :DeleteObject ((HGDIOBJ) SetClassLong (hwnd, GCL_HBRBACKGROUND,(LONG) GetStockObject (WHITE_BRUSH))) ;PostQuitMessage (0) ;return 0 ;}return DefWindowProc (hwnd, message, wParam, lParam) ;}BOOL CALLBACK ColorScrDlg (HWND hDlg, UINT message,WPARAM wParam, LPARAM lParam){static int iColor[3] ;HWND hwndParent, hCtrl ;int iCtrlID, iIndex ;switch (message){case WM_INITDIALOG :for (iCtrlID = 10 ; iCtrlID < 13 ; iCtrlID++){hCtrl = GetDlgItem (hDlg, iCtrlID) ;SetScrollRange (hCtrl, SB_CTL, 0, 255, FALSE) ;SetScrollPos (hCtrl, SB_CTL, 0, FALSE) ;}return TRUE ;case WM_VSCROLL :hCtrl = (HWND) lParam ;iCtrlID = GetWindowLong (hCtrl, GWL_ID) ;iIndex = iCtrlID - 10 ;hwndParent = GetParent (hDlg) ;switch (LOWORD (wParam)){case SB_PAGEDOWN :iColor[iIndex] += 15 ; // fall throughcase SB_LINEDOWN :iColor[iIndex] = min (255, iColor[iIndex] + 1) ;break ;case SB_PAGEUP :iColor[iIndex] -= 15 ; // fall throughcase SB_LINEUP :iColor[iIndex] = max (0, iColor[iIndex] - 1) ;break ;case SB_TOP :iColor[iIndex] = 0 ;break ;case SB_BOTTOM :iColor[iIndex] = 255 ;break ;case SB_THUMBPOSITION :case SB_THUMBTRACK :iColor[iIndex] = HIWORD (wParam) ;break ;default :return FALSE ;}SetScrollPos (hCtrl, SB_CTL, iColor[iIndex], TRUE) ;SetDlgItemInt (hDlg, iCtrlID + 3, iColor[iIndex], FALSE) ;DeleteObject ((HGDIOBJ) SetClassLong (hwndParent, GCL_HBRBACKGROUND,(LONG) CreateSolidBrush (RGB (iColor[0], iColor[1], iColor[2])))) ;InvalidateRect (hwndParent, NULL, TRUE) ;return TRUE ;}return FALSE ;}

COLORS2.RC (摘錄)

//Microsoft Developer Studio generated resource script.#include "resource.h"#include "afxres.h"/// DialogCOLORSCRDLG DIALOG DISCARDABLE 16, 16, 120, 141STYLE DS_MODALFRAME | WS_POPUP | WS_VISIBLE | WS_CAPTIONCAPTION "Color Scroll Scrollbars"FONT 8, "MS Sans Serif"BEGINCTEXT "&Red",IDC_STATIC,8,8,24,8,NOT WS_GROUPSCROLLBAR 10,8,20,24,100,SBS_VERT | WS_TABSTOPCTEXT "0",13,8,124,24,8,NOT WS_GROUPCTEXT "&Green",IDC_STATIC,48,8,24,8,NOT WS_GROUPSCROLLBAR 11,48,20,24,100,SBS_VERT | WS_TABSTOPCTEXT "0",14,48,124,24,8,NOT WS_GROUPCTEXT "&Blue",IDC_STATIC,89,8,24,8,NOT WS_GROUPSCROLLBAR 12,89,20,24,100,SBS_VERT | WS_TABSTOPCTEXT "0",15,89,124,24,8,NOT WS_GROUPEND

RESOURCE.H (摘錄)

// Microsoft Developer Studio generated include file.// Used by Colors2.rc#define IDC_STATIC -1

原來的COLORS1程序所顯示的滾動條大小是依據窗口大小決定的,而新程序在非模態對話框內以固定的尺寸來顯示它們,如圖11-4所示。

當您建立對話框模板時,直接將三個滾動條的ID分別設為10、11和12,將顯示滾動條目前值的三個靜態文字字段的ID分別設為13、14和15。將每個滾動條都設定為Tab Stop樣式,而從所有的六個靜態文字字段中刪除Group樣式。


 

?

圖11-4 COLORS2的屏幕顯示

在COLORS2中,非模態對話框是在WinMain函數里建立的,緊跟在程序主窗口的ShowWindow呼叫之后。注意,主窗口的窗口樣式包含WS_CLIPCHILDREN,這允許程序無須擦除對話框就能夠重畫主窗口。

如上所述,從CreateDialog傳回的對話框窗口句柄存放在整體變量hDlgModeless中,并在消息循環中被測試。不過,在這個程序中,不需要將句柄存放在整體變量中,也不需要在呼叫IsDialogMessage之前測試這個值。消息循環可以編寫如下:

while (GetMessage (&msg, NULL, 0, 0)){if (!IsDialogMessage (hDlgModeless, &msg)){TranslateMessage (&msg) ;DispatchMessage (&msg) ;}}

由于對話框是在程序進入消息循環前建立,并且直到程序結束時才會被清除,所以hDlgModeless的值將總是有效的。我加入了如下的處理方式,以便您可能會往對話框的窗口消息處理程序中加入一段清除對話框的程序代碼:

case WM_CLOSE :DestroyWindow (hDlg) ;hDlgModeless = NULL ;break ;

在原來的COLORS1程序中,SetWindowText在使用wsprintf將三個數值卷標轉換為文字之后才設定它們的值。敘述為:

wsprintf (szBuffer, TEXT ("%i"), color[i]) ;SetWindowText (hwndValue[i], szBuffer) ;

i的值為目前處理的滾動條的ID,hwndValue是一個數組,它包含顏色數值的三個靜態文字子窗口的窗口句柄。

新版本使用SetDlgItemInt為每個子窗口的每個文字字段設定一個號碼:

SetDlgItemInt (hDlg, iCtrlID + 3, color [iCtrlID], FALSE) ;

盡管SetDlgItemInt和與其對應的GetDlgItemInt在編輯控件中用得最多,它們也可以用來設定其它控件的文字字段,如靜態文字控件等。iCtrlID變量是滾動條的ID,給ID加上3使之變成對應數字卷標的ID。第三個參數是顏色值。通常,第四個參數表示第三個參數的值是解釋為有正負號的(第四個參數為TRUE)還是無正負號的(第四個參數為FALSE)。但是,對于這個程序,值的范圍是從0到256,所以這個參數沒有意義。

在將COLORS1轉換為COLORS2的程序中,我們把越來越多的工作交給了Windows。舊版本呼叫了CreateWindow 10次;而新版本只呼叫了CreateWindow和CreateDialog各一次。但是,如果您認為我們已經把呼叫CreateWindow的次數降到最少,那么您就錯了,請看下一個程序。

HEXCALC:窗口還是對話框?

HEXCALC程序可能是寫程序偷懶的經典之作,如程序11-5所示。這個程序完全不呼叫CreateWindow,也不處理WM_PAINT消息,不取得設備內容,也不處理鼠標消息。但是它只用了不到150行的原始碼,就構成了一個具有完整鍵盤和鼠標接口以及10種運算的十六進制計算器。計算器如圖11-5所示。

程序11-5 HEXCALC

HEXCALC.C/*------------------------------------------------------------------------HEXCALC.C -- Hexadecimal Calculator(c) Charles Petzold, 1998-------------------------------------------------------------------------*/#include <windows.h>LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM) ;int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance,PSTR szCmdLine, int iCmdShow){static TCHAR szAppName[] = TEXT ("HexCalc") ;HWND hwnd ;MSG msg ;WNDCLASS wndclass ;wndclass.style = CS_HREDRAW | CS_VREDRAW;wndclass.lpfnWndProc = WndProc ;wndclass.cbClsExtra = 0 ;wndclass.cbWndExtra = DLGWINDOWEXTRA ; // Note!wndclass.hInstance = hInstance ;wndclass.hIcon = LoadIcon (hInstance, szAppName) ;wndclass.hCursor = LoadCursor (NULL, IDC_ARROW) ;wndclass.hbrBackground = (HBRUSH) (COLOR_BTNFACE + 1) ;wndclass.lpszMenuName = NULL ;wndclass.lpszClassName = szAppName ;if (!RegisterClass (&wndclass)){MessageBox ( NULL, TEXT ("This program requires Windows NT!"),szAppName, MB_ICONERROR) ;return 0 ;}hwnd = CreateDialog (hInstance, szAppName, 0, NULL) ;ShowWindow (hwnd, iCmdShow) ;while (GetMessage (&msg, NULL, 0, 0)){TranslateMessage (&msg) ;DispatchMessage (&msg) ;}return msg.wParam ;}void ShowNumber (HWND hwnd, UINT iNumber){TCHAR szBuffer[20] ;wsprintf (szBuffer, TEXT ("%X"), iNumber) ;SetDlgItemText (hwnd, VK_ESCAPE, szBuffer) ;}DWORD CalcIt (UINT iFirstNum, int iOperation, UINT iNum){switch (iOperation){case '=': return iNum ;case '+': return iFirstNum + iNum ;case '-': return iFirstNum - iNum ;case '*': return iFirstNum * iNum ;case '&': return iFirstNum & iNum ;case '|': return iFirstNum | iNum ;case '^': return iFirstNum ^ iNum ;case '<': return iFirstNum << iNum ;case '>': return iFirstNum >> iNum ;case '/': return iNum ? iFirstNum / iNum: MAXDWORD ;case '%': return iNum ? iFirstNum % iNum: MAXDWORD ;default : return 0 ;}}LRESULT CALLBACK WndProc (HWND hwnd, UINT message, WPARAM wParam,LPARAM lParam){static BOOL bNewNumber = TRUE ;static int iOperation = '=' ;static UINT iNumber, iFirstNum ;HWND hButton ;switch (message){case WM_KEYDOWN: // left arrow --> backspaceif (wParam != VK_LEFT)break ;wParam = VK_BACK ;// fall throughcase WM_CHAR:if ((wParam = (WPARAM) CharUpper ((TCHAR *) wParam)) == VK_RETURN)wParam = '=' ;if (hButton = GetDlgItem (hwnd, wParam)){SendMessage (hButton, BM_SETSTATE, 1, 0) ;Sleep (100) ;SendMessage (hButton, BM_SETSTATE, 0, 0) ;}else{MessageBeep (0) ;break ;}// fall throughcase WM_COMMAND:SetFocus (hwnd) ;if (LOWORD (wParam) == VK_BACK) //backspaceShowNumber (hwnd, iNumber /= 16) ;else if (LOWORD (wParam) == VK_ESCAPE) // escapeShowNumber (hwnd, iNumber = 0) ;else if (isxdigit (LOWORD (wParam))) // hex digit{if (bNewNumber){iFirstNum = iNumber ;iNumber = 0 ;}bNewNumber = FALSE ;if (iNumber <= MAXDWORD >> 4)ShowNumber (hwnd, iNumber = 16 * iNumber + wParam -(isdigit (wParam) ? '0': 'A' - 10)) ;elseMessageBeep (0) ;}else // operation{if (!bNewNumber)ShowNumber (hwnd, iNumber =CalcIt (iFirstNum, iOperation, iNumber)) ;bNewNumber = TRUE ;iOperation = LOWORD (wParam) ;}return 0 ;case WM_DESTROY:PostQuitMessage (0) ;return 0 ;}return DefWindowProc (hwnd, message, wParam, lParam) ;}

HEXCALC.RC (摘錄)

//Microsoft Developer Studio generated resource script.#include "resource.h"#include "afxres.h"/// IconHEXCALC ICON DISCARDABLE "HexCalc.ico"/#include "hexcalc.dlg" HEXCALC.DLG/*--------------------------------HEXCALC.DLG dialog script----------------------------------*/HexCalc DIALOG -1, -1, 102, 122STYLE WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOXCLASS "HexCalc"CAPTION "Hex Calculator"{PUSHBUTTON "D", 68, 8, 24, 14, 14PUSHBUTTON "A", 65, 8, 40, 14, 14PUSHBUTTON "7", 55, 8, 56, 14, 14PUSHBUTTON "4", 52, 8, 72, 14, 14PUSHBUTTON "1", 49, 8, 88, 14, 14PUSHBUTTON "0", 48, 8, 104,14, 14PUSHBUTTON "0", 27, 26, 4, 50, 14PUSHBUTTON "E", 69, 26, 24, 14, 14PUSHBUTTON "B", 66, 26, 40, 14, 14PUSHBUTTON "8", 56, 26, 56, 14, 14PUSHBUTTON "5", 53, 26, 72, 14, 14PUSHBUTTON "2", 50, 26, 88, 14, 14PUSHBUTTON "Back", 8, 26, 104,32, 14PUSHBUTTON "C", 67, 44, 40, 14, 14PUSHBUTTON "F", 70, 44, 24, 14, 14PUSHBUTTON "9", 57, 44, 56, 14, 14PUSHBUTTON "6", 54, 44, 72, 14, 14PUSHBUTTON "3", 51, 44, 88, 14, 14PUSHBUTTON "+", 43, 62, 24, 14, 14PUSHBUTTON "-", 45, 62, 40, 14, 14PUSHBUTTON "*", 42, 62, 56, 14, 14PUSHBUTTON "/", 47, 62, 72, 14, 14PUSHBUTTON "%", 37, 62, 88, 14, 14PUSHBUTTON "Equals", 61, 62, 104,32, 14PUSHBUTTON "&&",38, 80, 24, 14, 14PUSHBUTTON "|", 124, 80, 40, 14, 14PUSHBUTTON "^", 94, 80, 56, 14, 14PUSHBUTTON "<", 60, 80, 72, 14, 14PUSHBUTTON ">", 62, 80, 88, 14, 14}

HEXCALC.ICO

?


 

?


 

?


 

?

圖11-5 HEXCALC的屏幕顯示

HEXCALC是一個普通的中序表達式計算器,使用C語言的符號表示方式進行計算。它對無正負號32位整數作加、減、乘、除和取余數運算,位AND, OR, exclusive-OR運算,還有左右位移運算。被0除將導致結果被設定為FFFFFFFF。

在HEXCALC中既可以使用鼠標又可以使用鍵盤。您從按鍵點入」或者輸入第一個數(最多8位十六進制數字)開始,然后輸入運算子,然后是第二個數。接著,您可以透過單擊「Equals」按鈕或者按下等號鍵或Enter鍵便可以顯示運算結果。為了更正輸入,您可以使用「Back」按鈕、Backspace或者左箭頭鍵。單擊「display」方塊或者按下Esc鍵即可清除目前的輸入。

HEXCALC比較奇怪的一點是,屏幕上顯示的窗口似乎是普通的重迭式窗口與非模態對話框的混合體。一方面,HEXCALC的所有消息都在函數的WndProc中處理,這個函數與通常的窗口消息處理程序相似,該函數傳回一個長整數,它處理WM_DESTROY消息,呼叫DefWindowProc,就像普通的窗口消息處理程序一樣。另一方面,窗口是在WinMain中呼叫CreateDialog并使用HEXCALC.DLG中的對話框模板建立的。那么,HEXCALC到底是一個普通的可重迭窗口,還是一個非模態對話框呢?

簡單的回答是,對話框就是窗口。通常,Windows使用它自己內部的窗口消息處理程序處理對話框窗口的消息,然后,Windows將這些消息傳送給建立對話框的程序內的對話框程序。在HEXCALC中,我們讓Windows使用對話框模板建立一個窗口,但是自己寫程序處理這個窗口的消息。

不幸的是,在Developer Studio的Dialog Editor中,對話框模板需要一些我們不能添加的東西。因此,對話框模板包含在HEXCALC.DLG文件中,而且需要手工輸入。依照下面的方法,您可以將一個文本文件添加到任何項目中:從「 File」菜單選擇「New」,再選擇「 Files」頁面卷標,然后從文件型態列表中選擇「Text File」。像這樣的文件-包含附加資源定義-需要包含在資源描述中。從「 View」菜單選擇「Resource Includes」。這顯示一個對話框。在「Compile-time Directives」編輯欄輸入

#include "hexcalc.dlg"

這一行將插入到HEXCALC.RC資源描述中,像上面所顯示的一樣。

仔細看一下HEXCALC.DLG文件中的對話框模板,您將發現HEXCALC如何為對話框使用它自己的窗口消息處理程序。對話框模板的上方如下:

HexCalc DIALOG -1, -1, 102, 122STYLE WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOXCLASS "HexCalc"CAPTION "Hex Calculator"

注意諸如WS_OVERLAPPED和WS_MINIMIZEBOX等標識符,我們可以將它們用在CreateWindow呼叫中以建立普通的窗口。CLASS敘述是這個對話框與曾經建立過的對話框之間最重要的區別(而且它也是Developer Studio中的Dialog Editor不允許我們指定的)。當對話框模板省略了這個敘述時,Windows為對話框注冊一個窗口類別,并使用它自己的窗口消息處理程序處理對話框消息。這里,包含CLASS敘述就告訴Windows將消息發送到其它的地方-具體的說,就是發送到在HexCalc窗口類別中指定的窗口消息處理程序。

HexCalc窗口類別是在HEXCALC的WinMain函數中注冊的,就像普通窗口的窗口類別一樣。但是,請注意有個十分重要的區別:WNDCLASS結構的cbWndExtra字段設定為DLGWINDOWEXTRA。對于您自己注冊的對話框程序,這是必需的。

在注冊窗口類別之后,WinMain呼叫CreateDialog:

hwnd = CreateDialog (hInstance, szAppName, 0, NULL) ;

第二個參數(字符串「HexCaEc」)是對話框模板的名字。第三個參數通常是父窗口的窗口句柄,這里設定為0,因為窗口沒有父窗口。最后一個參數,通常是對話框程序的地址,這里不需要。因為Windows不會處理這些消息,因而也不會將消息發送給對話框程序。

這個CreateDialog呼叫與對話框模板一起,被Windows有效地轉換為一個CreateWindow呼叫。該CreateWindow呼叫的功能與下面的呼叫相同:

hwnd = CreateWindow (TEXT ("HexCalc"), TEXT ("Hex Calculator"),WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX,CW_USEDEFAULT, CW_USEDEFAULT,102 * 4 / cxChar, 122 * 8 / cyChar,NULL, NULL, hInstance, NULL) ;

其中,cxChar和cyChar變量分別是系統字體字符的寬度和高度。

我們通過讓Windows來進行CreateWindow呼叫而收獲甚豐:Windows不會在建立彈出式窗口1后就停止,它還會為對話框模板中定義的其它29個子窗口按鍵控件呼叫CreateWindow。所有這些控件都給父窗口的窗口消息處理程序發送WM_COMMAND消息,該程序正是WndProc。對于建立一個包含許多子窗口的窗口來說,這是一個很好的技巧。

下面是使HEXCALC的程序代碼量下降到最少的另一種方法:或許您會注意到HEXCALC沒有表頭文件,表頭文件中通常包含對話框模板中,需要為所有子窗口控件定義的標識符。我們之所以可以不要這個文件,是因為每個按鍵控件的ID設定為出現在控件上的文字的ASCII碼。這意味著,WndProc可以完全相同地對待WM_COMMAND消息和WM_CHAR消息。在每種情況下,wParam的低字組都是按鈕的ASCII碼。

當然,對鍵盤消息進行一些處理是必要的。WndProc攔截WM_KEYDOWN消息,將左箭頭鍵轉換為Backspace鍵。在處理WM_CHAR消息時,WndProc將字符代碼轉換為大寫,Enter鍵轉換為等號鍵的ASCII碼。

WM_CHAR消息的有效性是通過呼叫GetDlgItem來檢驗的。如果GetDlgItem函數傳回0,那么鍵盤字符不是對話框模板中定義的ID之一。如果字符是ID之一,則通過給相應的按鈕發送一對BM_SETSTATE消息,來使之閃爍:

if (hButton = GetDlgItem (hwnd, wParam)){SendMessage (hButton, BM_SETSTATE, 1, 0) ;Sleep (100) ;SendMessage (hButton, BM_SETSTATE, 0, 0) ;}

這樣做,用最小的代價,卻為HEXCALC的鍵盤接口增色不少。Sleep函數將程序暫停100毫秒。這會防止按鈕被按得太快而讓人注意不到。

當WndProc處理WM_COMMAND消息時,它總是將輸入焦點設定給父窗口:

case WM_COMMAND :SetFocus (hwnd) ;

否則,一旦使用鼠標單擊某按鈕,輸入焦點就會切換到該按鈕上。

通用對話框

Windows的一個主要目的是推動標準的使用者接口。對許多常用的菜單項來說,這推行得很快,幾乎所有軟件廠商都采用Alt-File-Open選擇來打開一個文件。然而,實際的文件開啟對話框卻經常各不相同。

從Windows 3.1開始,對這個問題有了一個可行的解決方案,這是一種叫做「通用對話框鏈接庫」的增強。這個鏈接庫由幾個函數組成,這些函數啟動標準對話框來進行打開和儲存文件、搜索和替換、選擇顏色、選擇字體(我將在本章討論以上的這些內容)以及打印(我將在 第十三章討論)。

為了使用這些函數,您基本上都要初始化某一結構的各個字段,并將該結構的指針傳送給通用對話框鏈接庫的某個函數,該函數會建立并顯示對話框。當使用者關閉對話框時,被呼叫的函數將控制權傳回給程序,您可以從傳送給它的結構中獲得信息。

在使用通用對話框鏈接庫的任何C原始碼文件時,您都需要含入COMMDLG.H表頭文件。通用對話框的文件在/Platform SDK/User Interface Services/User Input/Common Dialog Box Library中。

增強POPPAD

當我們往第十章的POPPAD中增加菜單時,還有幾個標準菜單項沒有實作。現在我們已經準備好在POPPAD中加入打開文件、讀入文件以及在磁盤上儲存編輯過文件的功能。在處理中,我們還將在POPPAD中加入字體選擇和搜索替換功能。

實作POPPAD3程序的文件如程序11-6所示。

程序11-6 POPPAD3

POPPAD.C/*------------------------------------------------------------------------POPPAD.C -- Popup Editor(c) Charles Petzold, 1998-------------------------------------------------------------------------*/#include <windows.h>#include <commdlg.h>#include "resource.h"#define EDITID 1#define UNTITLED TEXT ("(untitled)")LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM) ;BOOL CALLBACK AboutDlgProc (HWND, UINT, WPARAM, LPARAM) ;// Functions in POPFILE.Cvoid PopFileInitialize (HWND) ;BOOL PopFileOpenDlg (HWND, PTSTR, PTSTR) ;BOOL PopFileSaveDlg (HWND, PTSTR, PTSTR) ;BOOL PopFileRead (HWND, PTSTR) ;BOOL PopFileWrite (HWND, PTSTR) ;// Functions in POPFIND.CHWND PopFindFindDlg (HWND) ;HWND PopFindReplaceDlg (HWND) ;BOOL PopFindFindText (HWND, int *, LPFINDREPLACE) ;BOOL PopFindReplaceText (HWND, int *, LPFINDREPLACE) ;BOOL PopFindNextText (HWND, int *) ;BOOL PopFindValidFind (void) ;// Functions in POPFONT.Cvoid PopFontInitialize (HWND) ;BOOL PopFontChooseFont (HWND) ;void PopFontSetFont (HWND) ;void PopFontDeinitialize (void) ;// Functions in POPPRNT.CBOOL PopPrntPrintFile (HINSTANCE, HWND, HWND, PTSTR) ;// Global variablesstatic HWND hDlgModeless ;static TCHAR szAppName[] = TEXT ("PopPad") ;int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance,PSTR szCmdLine, int iCmdShow){MSG msg ;HWND hwnd ;HACCEL hAccel ;WNDCLASS wndclass ;wndclass.style = CS_HREDRAW | CS_VREDRAW ;wndclass.lpfnWndProc = WndProc ;wndclass.cbClsExtra = 0 ;wndclass.cbWndExtra = 0 ;wndclass.hInstance = hInstance ;wndclass.hIcon = LoadIcon (hInstance, szAppName) ;wndclass.hCursor = LoadCursor (NULL, IDC_ARROW) ;wndclass.hbrBackground = (HBRUSH) GetStockObject (WHITE_BRUSH) ;wndclass.lpszMenuName = szAppName ;wndclass.lpszClassName = szAppName ;if (!RegisterClass (&wndclass)){MessageBox ( NULL, TEXT ("This program requires Windows NT!"),szAppName, MB_ICONERROR) ;return 0 ;}hwnd = CreateWindow (szAppName, NULL,WS_OVERLAPPEDWINDOW,CW_USEDEFAULT, CW_USEDEFAULT,CW_USEDEFAULT, CW_USEDEFAULT,NULL, NULL, hInstance, szCmdLine) ;ShowWindow (hwnd, iCmdShow) ;UpdateWindow (hwnd) ;hAccel = LoadAccelerators (hInstance, szAppName) ;while (GetMessage (&msg, NULL, 0, 0)){if (hDlgModeless == NULL || !IsDialogMessage (hDlgModeless, &msg)){if (!TranslateAccelerator (hwnd, hAccel, &msg)){TranslateMessage (&msg) ;DispatchMessage (&msg) ;}}}return msg.wParam ;}void DoCaption (HWND hwnd, TCHAR * szTitleName){TCHAR szCaption[64 + MAX_PATH] ;wsprintf (szCaption, TEXT ("%s - %s"), szAppName,szTitleName[0] ? szTitleName : UNTITLED) ;SetWindowText (hwnd, szCaption) ;}void OkMessage (HWND hwnd, TCHAR * szMessage, TCHAR * szTitleName){TCHAR szBuffer[64 + MAX_PATH] ;wsprintf (szBuffer, szMessage, szTitleName[0] ? szTitleName : UNTITLED) ;MessageBox (hwnd, szBuffer, szAppName, MB_OK | MB_ICONEXCLAMATION) ;}short AskAboutSave (HWND hwnd, TCHAR * szTitleName){TCHAR szBuffer[64 + MAX_PATH] ;int iReturn ;wsprintf (szBuffer, TEXT ("Save current changes in %s?"),szTitleName[0] ? szTitleName : UNTITLED) ;iReturn = MessageBox (hwnd, szBuffer, szAppName,MB_YESNOCANCEL | MB_ICONQUESTION) ;if (iReturn == IDYES)if (!SendMessage (hwnd, WM_COMMAND, IDM_FILE_SAVE, 0))iReturn = IDCANCEL ;return iReturn ;}LRESULT CALLBACK WndProc (HWND hwnd, UINT message, WPARAM wParam,LPARAM lParam){static BOOL bNeedSave = FALSE ;static HINSTANCE hInst ;static HWND hwndEdit ;static int iOffset ;static TCHAR szFileName[MAX_PATH], szTitleName[MAX_PATH] ;static UINT messageFindReplace ;int iSelBeg, iSelEnd, iEnable ;LPFINDREPLACE pfr ;switch (message){case WM_CREATE:hInst = ((LPCREATESTRUCT) lParam) -> hInstance ;// Create the edit control child windowhwndEdit = CreateWindow (TEXT ("edit"), NULL,WS_CHILD | WS_VISIBLE | WS_HSCROLL | WS_VSCROLL |WS_BORDER | ES_LEFT | ES_MULTILINE |ES_NOHIDESEL | ES_AUTOHSCROLL | ES_AUTOVSCROLL,0, 0, 0, 0,hwnd, (HMENU) EDITID, hInst, NULL) ;SendMessage (hwndEdit, EM_LIMITTEXT, 32000, 0L) ;// Initialize common dialog box stuffPopFileInitialize (hwnd) ;PopFontInitialize (hwndEdit) ;messageFindReplace = RegisterWindowMessage (FINDMSGSTRING) ;DoCaption (hwnd, szTitleName) ;return 0 ;case WM_SETFOCUS:SetFocus (hwndEdit) ;return 0 ;case WM_SIZE:MoveWindow (hwndEdit, 0, 0, LOWORD (lParam), HIWORD (lParam), TRUE) ;return 0 ;case WM_INITMENUPOPUP:switch (lParam){case 1: // Edit menu// Enable Undo if edit control can do itEnableMenuItem ((HMENU) wParam, IDM_EDIT_UNDO,SendMessage (hwndEdit, EM_CANUNDO, 0, 0L) ?MF_ENABLED : MF_GRAYED) ;// Enable Paste if text is in the clipboardEnableMenuItem ((HMENU) wParam, IDM_EDIT_PASTE,IsClipboardFormatAvailable (CF_TEXT) ?MF_ENABLED : MF_GRAYED) ;// Enable Cut, Copy, and Del if text is selectedSendMessage (hwndEdit, EM_GETSEL, (WPARAM) &iSelBeg,(LPARAM) &iSelEnd) ;iEnable = iSelBeg != iSelEnd ? MF_ENABLED : MF_GRAYED ;EnableMenuItem ((HMENU) wParam, IDM_EDIT_CUT, iEnable) ;EnableMenuItem ((HMENU) wParam, IDM_EDIT_COPY, iEnable) ;EnableMenuItem ((HMENU) wParam, IDM_EDIT_CLEAR, iEnable) ;break ;case 2: // Search menu// Enable Find, Next, and Replace if modeless// dialogs are not already activeiEnable = hDlgModeless == NULL ?MF_ENABLED : MF_GRAYED ;EnableMenuItem ((HMENU) wParam, IDM_SEARCH_FIND, iEnable) ;EnableMenuItem ((HMENU) wParam, IDM_SEARCH_NEXT, iEnable) ;EnableMenuItem ((HMENU) wParam, IDM_SEARCH_REPLACE, iEnable) ;break ;}return 0 ;case WM_COMMAND:// Messages from edit controlif (lParam && LOWORD (wParam) == EDITID){switch (HIWORD (wParam)){case EN_UPDATE :bNeedSave = TRUE ;return 0 ;case EN_ERRSPACE :case EN_MAXTEXT :MessageBox (hwnd, TEXT ("Edit control out of space."),szAppName, MB_OK | MB_ICONSTOP) ;return 0 ;}break ;}switch (LOWORD (wParam)){// Messages from File menucase IDM_FILE_NEW:if (bNeedSave && IDCANCEL == AskAboutSave (hwnd, szTitleName))return 0 ;SetWindowText (hwndEdit, TEXT ("\0")) ;szFileName[0] = '\0' ;szTitleName[0] = '\0' ;DoCaption (hwnd, szTitleName) ;bNeedSave = FALSE ;return 0 ;case IDM_FILE_OPEN:if (bNeedSave && IDCANCEL == AskAboutSave (hwnd, szTitleName))return 0 ;if (PopFileOpenDlg (hwnd, szFileName, szTitleName)){if (!PopFileRead (hwndEdit, szFileName)){OkMessage (hwnd, TEXT ("Could not read file %s!"),szTitleName) ;szFileName[0] = '\0' ;szTitleName[0] = '\0' ;}}DoCaption (hwnd, szTitleName) ;bNeedSave = FALSE ;return 0 ;case IDM_FILE_SAVE:if (szFileName[0]){if (PopFileWrite (hwndEdit, szFileName)){bNeedSave = FALSE ;return 1 ;}else{OkMessage (hwnd, TEXT ("Could not write file %s"),szTitleName) ;return 0 ;}}//fall throughcase IDM_FILE_SAVE_AS:if (PopFileSaveDlg (hwnd, szFileName, szTitleName)){DoCaption (hwnd, szTitleName) ;if (PopFileWrite (hwndEdit, szFileName)){bNeedSave = FALSE ;return 1 ;}else{OkMessage (hwnd, TEXT ("Could not write file %s"),szTitleName) ;return 0 ;}}return 0 ;case IDM_FILE_PRINT:if (!PopPrntPrintFile (hInst, hwnd, hwndEdit, szTitleName))OkMessage ( hwnd, TEXT ("Could not print file %s"),szTitleName) ;return 0 ;case IDM_APP_EXIT:SendMessage (hwnd, WM_CLOSE, 0, 0) ;return 0 ;// Messages from Edit menucase IDM_EDIT_UNDO:SendMessage (hwndEdit, WM_UNDO, 0, 0) ;return 0 ;case IDM_EDIT_CUT:SendMessage (hwndEdit, WM_CUT, 0, 0) ;return 0 ;case IDM_EDIT_COPY:SendMessage (hwndEdit, WM_COPY, 0, 0) ;return 0 ;case IDM_EDIT_PASTE:SendMessage (hwndEdit, WM_PASTE, 0, 0) ;return 0 ;case IDM_EDIT_CLEAR:SendMessage (hwndEdit, WM_CLEAR, 0, 0) ;return 0 ;case IDM_EDIT_SELECT_ALL:SendMessage (hwndEdit, EM_SETSEL, 0, -1) ;return 0 ;// Messages from Search menucase IDM_SEARCH_FIND:SendMessage (hwndEdit, EM_GETSEL, 0, (LPARAM) &iOffset) ;hDlgModeless = PopFindFindDlg (hwnd) ;return 0 ;case IDM_SEARCH_NEXT:SendMessage (hwndEdit, EM_GETSEL, 0, (LPARAM) &iOffset) ;if (PopFindValidFind ())PopFindNextText (hwndEdit, &iOffset) ;elsehDlgModeless = PopFindFindDlg (hwnd) ;return 0 ;case IDM_SEARCH_REPLACE:SendMessage (hwndEdit, EM_GETSEL, 0, (LPARAM) &iOffset) ;hDlgModeless = PopFindReplaceDlg (hwnd) ;return 0 ;case IDM_FORMAT_FONT:if (PopFontChooseFont (hwnd))PopFontSetFont (hwndEdit) ;return 0 ;// Messages from Help menucase IDM_HELP:OkMessage (hwnd, TEXT ("Help not yet implemented!"),TEXT ("\0")) ;return 0 ;case IDM_APP_ABOUT:DialogBox (hInst, TEXT ("AboutBox"), hwnd, AboutDlgProc) ;return 0 ;}break ;case WM_CLOSE:if (!bNeedSave || IDCANCEL != AskAboutSave (hwnd, szTitleName))DestroyWindow (hwnd) ;return 0 ;case WM_QUERYENDSESSION :if (!bNeedSave || IDCANCEL != AskAboutSave (hwnd, szTitleName))return 1 ;return 0 ;case WM_DESTROY:PopFontDeinitialize () ;PostQuitMessage (0) ;return 0 ;default:// Process "Find-Replace" messagesif (message == messageFindReplace){pfr = (LPFINDREPLACE) lParam ;if (pfr->Flags & FR_DIALOGTERM)hDlgModeless = NULL ;if (pfr->Flags & FR_FINDNEXT)if (!PopFindFindText (hwndEdit, &iOffset, pfr))OkMessage (hwnd, TEXT ("Text not found!"),TEXT ("\0")) ;if (pfr->Flags & FR_REPLACE || pfr->Flags & FR_REPLACEALL)if (!PopFindReplaceText (hwndEdit, &iOffset, pfr))OkMessage (hwnd, TEXT ("Text not found!"),TEXT ("\0")) ;if (pfr->Flags & FR_REPLACEALL)while (PopFindReplaceText (hwndEdit, &iOffset, pfr)) ;return 0 ;}break ;}return DefWindowProc (hwnd, message, wParam, lParam) ;}BOOL CALLBACK AboutDlgProc (HWND hDlg, UINT message,WPARAM wParam, LPARAM lParam){switch (message){case WM_INITDIALOG:return TRUE ;case WM_COMMAND:switch (LOWORD (wParam)){case IDOK:EndDialog (hDlg, 0) ;return TRUE ;}break ;}return FALSE ;}

POPFILE.C

/*--------------------------------------------------------------------------POPFILE.C -- Popup Editor File Functions------------------------------------------------------------------------*/#include <windows.h>#include <commdlg.h>static OPENFILENAME ofn ;void PopFileInitialize (HWND hwnd){static TCHAR szFilter[] = TEXT ("Text Files (*.TXT)\0*.txt\0") \TEXT ("ASCII Files (*.ASC)\0*.asc\0") \TEXT ("All Files (*.*)\0*.*\0\0") ;ofn.lStructSize = sizeof (OPENFILENAME) ;ofn.hwndOwner = hwnd ;ofn.hInstance = NULL ;ofn.lpstrFilter = szFilter ;ofn.lpstrCustomFilter = NULL ;ofn.nMaxCustFilter = 0 ;ofn.nFilterIndex = 0 ;ofn.lpstrFile = NULL ; // Set in Open and Close functionsofn.nMaxFile = MAX_PATH ;ofn.lpstrFileTitle = NULL ; // Set in Open and Close functionsofn.nMaxFileTitle = MAX_PATH ;ofn.lpstrInitialDir = NULL ;ofn.lpstrTitle = NULL ;ofn.Flags = 0 ; // Set in Open and Close functionsofn.nFileOffset = 0 ;ofn.nFileExtension = 0 ;ofn.lpstrDefExt = TEXT ("txt") ;ofn.lCustData = 0L ;ofn.lpfnHook = NULL ;ofn.lpTemplateName = NULL ;}BOOL PopFileOpenDlg (HWND hwnd, PTSTR pstrFileName, PTSTR pstrTitleName){ofn.hwndOwner = hwnd ;ofn.lpstrFile = pstrFileName ;ofn.lpstrFileTitle = pstrTitleName ;ofn.Flags = OFN_HIDEREADONLY | OFN_CREATEPROMPT ;return GetOpenFileName (&ofn) ;}BOOL PopFileSaveDlg (HWND hwnd, PTSTR pstrFileName, PTSTR pstrTitleName){ofn.hwndOwner = hwnd ;ofn.lpstrFile = pstrFileName ;ofn.lpstrFileTitle = pstrTitleName ;ofn.Flags = OFN_OVERWRITEPROMPT ;return GetSaveFileName (&ofn) ;}BOOL PopFileRead (HWND hwndEdit, PTSTR pstrFileName){BYTE bySwap ;DWORD dwBytesRead ;HANDLE hFile ;int i, iFileLength, iUniTest ;PBYTE pBuffer, pText, pConv ;// Open the file.if (INVALID_HANDLE_VALUE ==(hFile = CreateFile (pstrFileName, GENERIC_READ, FILE_SHARE_READ,NULL, OPEN_EXISTING, 0, NULL)))return FALSE ;// Get file size in bytes and allocate memory for read.// Add an extra two bytes for zero termination.iFileLength = GetFileSize (hFile, NULL) ;pBuffer = malloc (iFileLength + 2) ;// Read file and put terminating zeros at end.ReadFile (hFile, pBuffer, iFileLength, &dwBytesRead, NULL) ;CloseHandle (hFile) ;pBuffer[iFileLength] = '\0' ;pBuffer[iFileLength + 1] = '\0' ;// Test to see if the text is UnicodeiUniTest = IS_TEXT_UNICODE_SIGNATURE | IS_TEXT_UNICODE_REVERSE_SIGNATURE ;if (IsTextUnicode (pBuffer, iFileLength, &iUniTest)){pText = pBuffer + 2 ;iFileLength -= 2 ;if (iUniTest & IS_TEXT_UNICODE_REVERSE_SIGNATURE){for (i = 0 ; i < iFileLength / 2 ; i++){bySwap = ((BYTE *) pText) [2 * i] ;((BYTE *) pText) [2 * i] = ((BYTE *) pText) [2 * i + 1] ;((BYTE *) pText) [2 * i + 1] = bySwap ;}}// Allocate memory for possibly converted stringpConv = malloc (iFileLength + 2) ;// If the edit control is not Unicode, convert Unicode text to// non-Unicode (i.e., in general, wide character).#ifndef UNICODEWideCharToMultiByte (CP_ACP, 0, (PWSTR) pText, -1, pConv,iFileLength + 2, NULL, NULL) ;// If the edit control is Unicode, just copy the string#elselstrcpy ((PTSTR) pConv, (PTSTR) pText) ;#endif}else // the file is not Unicode{pText = pBuffer ;// Allocate memory for possibly converted string.pConv = malloc (2 * iFileLength + 2) ;// If the edit control is Unicode, convert ASCII text.#ifdef UNICODEMultiByteToWideChar (CP_ACP, 0, pText, -1, (PTSTR) pConv,iFileLength + 1) ;// If not, just copy buffer#elselstrcpy ((PTSTR) pConv, (PTSTR) pText) ;#endif}SetWindowText (hwndEdit, (PTSTR) pConv) ;free (pBuffer) ;free (pConv) ;return TRUE ;}BOOL PopFileWrite (HWND hwndEdit, PTSTR pstrFileName){DWORD dwBytesWritten ;HANDLE hFile ;int iLength ;PTSTR pstrBuffer ;WORD wByteOrderMark = 0xFEFF ;// Open the file, creating it if necessaryif (INVALID_HANDLE_VALUE ==(hFile = CreateFile (pstrFileName, GENERIC_WRITE, 0,NULL, CREATE_ALWAYS, 0, NULL)))return FALSE ;// Get the number of characters in the edit control and allocate// memory for them.iLength = GetWindowTextLength (hwndEdit) ;pstrBuffer = (PTSTR) malloc ((iLength + 1) * sizeof (TCHAR)) ;if (!pstrBuffer){CloseHandle (hFile) ;return FALSE ;}// If the edit control will return Unicode text, write the// byte order mark to the file.#ifdef UNICODEWriteFile (hFile, &wByteOrderMark, 2, &dwBytesWritten, NULL) ;#endif// Get the edit buffer and write that out to the file.GetWindowText (hwndEdit, pstrBuffer, iLength + 1) ;WriteFile (hFile, pstrBuffer, iLength * sizeof (TCHAR),&dwBytesWritten, NULL) ;if ((iLength * sizeof (TCHAR)) != (int) dwBytesWritten){CloseHandle (hFile) ;free (pstrBuffer) ;return FALSE ;}CloseHandle (hFile) ;free (pstrBuffer) ;return TRUE ;}

POPFIND.C

/*--------------------------------------------------------------------------POPFIND.C -- Popup Editor Search and Replace Functions------------------------------------------------------------------------*/#include <windows.h>#include <commdlg.h>#include <tchar.h> // for _tcsstr (strstr for Unicode & non-Unicode)#define MAX_STRING_LEN 256static TCHAR szFindText [MAX_STRING_LEN] ;static TCHAR szReplText [MAX_STRING_LEN] ;HWND PopFindFindDlg (HWND hwnd){static FINDREPLACE fr ; // must be static for modeless dialog!!!fr.lStructSize = sizeof (FINDREPLACE) ;fr.hwndOwner = hwnd ;fr.hInstance = NULL ;fr.Flags = FR_HIDEUPDOWN | FR_HIDEMATCHCASE | FR_HIDEWHOLEWORD ;fr.lpstrFindWhat = szFindText ;fr.lpstrReplaceWith = NULL ;fr.wFindWhatLen = MAX_STRING_LEN ;fr.wReplaceWithLen = 0 ;fr.lCustData = 0 ;fr.lpfnHook = NULL ;fr.lpTemplateName = NULL ;return FindText (&fr) ;}HWND PopFindReplaceDlg (HWND hwnd){static FINDREPLACE fr ; // must be static for modeless dialog!!!fr.lStructSize = sizeof (FINDREPLACE) ;fr.hwndOwner = hwnd ;fr.hInstance = NULL ;fr.Flags = FR_HIDEUPDOWN | FR_HIDEMATCHCASE | FR_HIDEWHOLEWORD ;fr.lpstrFindWhat = szFindText ;fr.lpstrReplaceWith = szReplText ;fr.wFindWhatLen = MAX_STRING_LEN ;fr.wReplaceWithLen = MAX_STRING_LEN ;fr.lCustData = 0 ;fr.lpfnHook = NULL ;fr.lpTemplateName = NULL ;return ReplaceText (&fr) ;}BOOL PopFindFindText (HWND hwndEdit, int * piSearchOffset, LPFINDREPLACE pfr){int iLength, iPos ;PTSTR pstrDoc, pstrPos ;// Read in the edit documentiLength = GetWindowTextLength (hwndEdit) ;if (NULL == (pstrDoc = (PTSTR) malloc ((iLength + 1) * sizeof (TCHAR))))return FALSE ;GetWindowText (hwndEdit, pstrDoc, iLength + 1) ;// Search the document for the find stringpstrPos = _tcsstr (pstrDoc + * piSearchOffset, pfr->lpstrFindWhat) ;free (pstrDoc) ;// Return an error code if the string cannot be foundif (pstrPos == NULL)return FALSE ;// Find the position in the document and the new start offsetiPos = pstrPos - pstrDoc ;* piSearchOffset = iPos + lstrlen (pfr->lpstrFindWhat) ;// Select the found textSendMessage (hwndEdit, EM_SETSEL, iPos, * piSearchOffset) ;SendMessage (hwndEdit, EM_SCROLLCARET, 0, 0) ;return TRUE ;}BOOL PopFindNextText (HWND hwndEdit, int * piSearchOffset){FINDREPLACE fr ;fr.lpstrFindWhat = szFindText ;return PopFindFindText (hwndEdit, piSearchOffset, &fr) ;}BOOL PopFindReplaceText (HWND hwndEdit, int * piSearchOffset, LPFIND,REPLACE pfr){// Find the textif (!PopFindFindText (hwndEdit, piSearchOffset, pfr))return FALSE ;// Replace itSendMessage (hwndEdit, EM_REPLACESEL, 0, (LPARAM) pfr->lpstrReplaceWith) ;return TRUE ;}BOOL PopFindValidFind (void){return * szFindText != '\0' ;}

POPFONT.C

/*----------------------------------------------------POPFONT.C -- Popup Editor Font Functions------------------------------------------------------*/#include <windows.h>#include <commdlg.h>static LOGFONT logfont ;static HFONT hFont ;BOOL PopFontChooseFont (HWND hwnd){CHOOSEFONT cf ;cf.lStructSize = sizeof (CHOOSEFONT) ;cf.hwndOwner = hwnd ;cf.hDC = NULL ;cf.lpLogFont = &logfont ;cf.iPointSize = 0 ;cf.Flags = CF_INITTOLOGFONTSTRUCT | CF_SCREENFONTS | CF_EFFECTS ;cf.rgbColors = 0 ;cf.lCustData = 0 ;cf.lpfnHook = NULL ;cf.lpTemplateName = NULL ;cf.hInstance = NULL ;cf.lpszStyle = NULL ;cf.nFontType = 0 ; // Returned from ChooseFontcf.nSizeMin = 0 ;cf.nSizeMax = 0 ;return ChooseFont (&cf) ;}void PopFontInitialize (HWND hwndEdit){GetObject (GetStockObject (SYSTEM_FONT), sizeof (LOGFONT),(PTSTR) &logfont) ;hFont = CreateFontIndirect (&logfont) ;SendMessage (hwndEdit, WM_SETFONT, (WPARAM) hFont, 0) ;}void PopFontSetFont (HWND hwndEdit){HFONT hFontNew ;RECT rect ;hFontNew = CreateFontIndirect (&logfont) ;SendMessage (hwndEdit, WM_SETFONT, (WPARAM) hFontNew, 0) ;DeleteObject (hFont) ;hFont = hFontNew ;GetClientRect (hwndEdit, &rect) ;InvalidateRect (hwndEdit, &rect, TRUE) ;}void PopFontDeinitialize (void){DeleteObject (hFont) ;}

POPPRNT0.C

/*------------------------------------------------------------------------POPPRNT0.C -- Popup Editor Printing Functions (dummy version)--------------------------------------------------------------------------*/#include <windows.h>BOOL PopPrntPrintFile ( HINSTANCE hInst, HWND hwnd, HWND hwndEdit,PTSTR pstrTitleName){return FALSE ;}

POPPAD.RC (摘錄)

//Microsoft Developer Studio generated resource script.#include "resource.h"#include "afxres.h"/// DialogABOUTBOX DIALOG DISCARDABLE 32, 32, 180, 100STYLE DS_MODALFRAME | WS_POPUPFONT 8, "MS Sans Serif"BEGINDEFPUSHBUTTON "OK",IDOK,66,80,50,14ICON "POPPAD",IDC_STATIC,7,7,20,20CTEXT "PopPad",IDC_STATIC,40,12,100,8CTEXT "Popup Editor for Windows",IDC_STATIC,7,40,166,8CTEXT "(c) Charles Petzold, 1998",IDC_STATIC,7,52,166,8ENDPRINTDLGBOX DIALOG DISCARDABLE 32, 32, 186, 95STYLE DS_MODALFRAME | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENUCAPTION "PopPad"FONT 8, "MS Sans Serif"BEGINPUSHBUTTON "Cancel",IDCANCEL,67,74,50,14CTEXT "Sending",IDC_STATIC,8,8,172,8CTEXT "",IDC_FILENAME,8,28,172,8CTEXT "to print spooler.",IDC_STATIC,8,48,172,8END/// MenuPOPPAD MENU DISCARDABLEBEGINPOPUP "&File"BEGINMENUITEM "&New\tCtrl+N", IDM_FILE_NEWMENUITEM "&Open...\tCtrl+O",IDM_FILE_OPENMENUITEM "&Save\tCtrl+S", IDM_FILE_SAVEMENUITEM "Save &As...", IDM_FILE_SAVE_ASMENUITEM SEPARATORMENUITEM "&Print\tCtrl+P", IDM_FILE_PRINTMENUITEM SEPARATORMENUITEM "E&xit", IDM_APP_EXITENDPOPUP "&Edit"BEGINMENUITEM "&Undo\tCtrl+Z", IDM_EDIT_UNDOMENUITEM SEPARATORMENUITEM "Cu&t\tCtrl+X", IDM_EDIT_CUTMENUITEM "&Copy\tCtrl+C", IDM_EDIT_COPYMENUITEM "&Paste\tCtrl+V", IDM_EDIT_PASTEMENUITEM "De&lete\tDel", IDM_EDIT_CLEARMENUITEM SEPARATORMENUITEM "&Select All", IDM_EDIT_SELECT_ALLENDPOPUP "&Search"BEGIN MENUITEM "&Find...\tCtrl+F",IDM_SEARCH_FINDMENUITEM "Find &Next\tF3", IDM_SEARCH_NEXTMENUITEM "&Replace...\tCtrl+R", IDM_SEARCH_REPLACEENDPOPUP "F&ormat"BEGINMENUITEM "&Font...", ENDPOPUP "&Help"BEGINMENUITEM "&Help", IDM_HELPMENUITEM "&About PopPad...", IDM_APP_ABOUTENDEND/// AcceleratorPOPPAD ACCELERATORS DISCARDABLEBEGINVK_BACK, IDM_EDIT_UNDO, VIRTKEY, ALT, NOINVERTVK_DELETE, IDM_EDIT_CLEAR, VIRTKEY, NOINVERTVK_DELETE, IDM_EDIT_CUT, VIRTKEY, SHIFT, NOINVERTVK_F1, IDM_HELP, VIRTKEY, NOINVERTVK_F3, IDM_SEARCH_NEXT, VIRTKEY, NOINVERTVK_INSERT, IDM_EDIT_COPY, VIRTKEY, CONTROL, NOINVERTVK_INSERT, IDM_EDIT_PASTE, VIRTKEY, SHIFT, NOINVERT"^C", IDM_EDIT_COPY, ASCII, NOINVERT"^F", IDM_SEARCH_FIND, ASCII, NOINVERT"^N", IDM_FILE_NEW, ASCII, NOINVERT"^O", IDM_FILE_OPEN, ASCII, NOINVERT"^P", IDM_FILE_PRINT, ASCII, NOINVERT"^R", IDM_SEARCH_REPLACE, ASCII, NOINVERT"^S", IDM_FILE_SAVE, ASCII, NOINVERT"^V", IDM_EDIT_PASTE, ASCII, NOINVERT"^X", IDM_EDIT_CUT, ASCII, NOINVERT"^Z", IDM_EDIT_UNDO, ASCII, NOINVERTEND/// IconPOPPAD ICON DISCARDABLE "poppad.ico"

RESOURCE.H (摘錄)

// Microsoft Developer Studio generated include file.// Used by poppad.rc#define IDC_FILENAME 1000#define IDM_FILE_NEW 40001#define IDM_FILE_OPEN 40002#define IDM_FILE_SAVE 40003#define IDM_FILE_SAVE_AS 40004#define IDM_FILE_PRINT 40005#define IDM_APP_EXIT 40006#define IDM_EDIT_UNDO 40007#define IDM_EDIT_CUT 40008#define IDM_EDIT_COPY 40009#define IDM_EDIT_PASTE 40010#define IDM_EDIT_CLEAR 40011#define IDM_EDIT_SELECT_ALL 40012#define IDM_SEARCH_FIND 40013#define IDM_SEARCH_NEXT 40014#define IDM_SEARCH_REPLACE 40015#define IDM_FORMAT_FONT 40016#define IDM_HELP 40017#define IDM_APP_ABOUT 40018

POPPAD.ICO

?


 

?


 

?

為了避免在第十三章中重復原始碼,我在POPPAD.RC的菜單中加入了打印項目和一些其它的支持。

POPPAD.C包含了程序中所有的基本原始碼。POPFILE.C具有啟動File Open和File Save對話框的程序代碼,它還包含文件I/O例程。POPFIND.C中包含了搜尋和替換文字功能。POPFONT.C包含了字體選擇功能。POPPRNT0.C不完成什么工作:在第十三章中將使用POPPRNT.C替換POPPRNT0.C以建立最終的POPPAD程序。

讓我們先來看一看POPPAD.C。POPPAD.C含有兩個文件名字符串:第一個,儲存在WndProc,名稱為szFileName,含有詳細的驅動器名稱、路徑名稱和文件名稱;第二個,儲存為szTitleName,是程序本身的文件名稱。它用在POPPAD3的DoCaption函數中,以便將文件名稱顯示在窗口的標題列上;也用在OKMessage函數和AskAboutSave函數中,以便向使用者顯示消息框。

POPFILE.C包含了幾個顯示「File Open」和「File Save」對話框以及實際執行文件I/O的函數。對話框是使用函數GetOpenFileName和GetSaveFileName來顯示的。這兩個函數都使用一個型態為OPENFILENAME的結構,這個結構在COMMDLG.H中定義。在POPFILE.C中,使用了一個該結構型態的整體變量,取名為ofn。ofn的大多數字段在PopFileInitialize函數中被初始化,POPPAD.C在WndProc中處理WM_CREATE消息時呼叫該函數。

將ofn作為靜態整體結構變量會比較方便,因為GetOpenFileName和GetSaveFileName給該結構傳回的一些信息,并將在以后呼叫這些函數時用到。

盡管通用對話框具有許多選項-包括設定自己的對話框模板,以及為對話框程序增加「掛勾(hook)」-POPFILE.C中使用的「File Open」和「File Save」對話框是最基本的。OPENFILENAME結構中被設定的字段只有lStructSize(結構的長度)、hwndOwner(對話框擁有者)、lpstrFilter(下面將簡要討論)、lpstrFile和nMaxFile(指向接收完整文件名稱的緩沖區指標和該緩沖區的大小)、lpstrFileTitle和nMaxFileTitle(文件名稱緩沖區及其大小)、Flags(設定對話框的選項)和lpstrDefExt(如果使用者在對話框中輸入文件名時不指定文件擴展名,那么它就是內定的文件擴展名)。

當使用者在「File」菜單中選擇「Open」時,POPPAD3呼叫POPFILE的PopFileOpenDlg函數,將窗口句柄、一個指向文件名稱緩沖區的指標和一個指向文件標題緩沖區的指標傳給它。PopFileOpenDlg恰當地設定OPENFILENAME結構的hwndOwner、lpstrFile和lpstrFileTitle字段,將Flags設定為OFN_ CREATEPROMPT,然后呼叫GetOpenFileName,顯示如圖11-6所示的普通對話框。


 

?

圖11-6 「File Open」對話框

當使用者結束這個對話框時,GetOpenFileName函數傳回。OFN_CREATEPROMPT旗標指示GetOpenFileName顯示一個消息框,詢問使用者如果所選文件不存在,是否要建立該文件。

左下角的下拉式清單方塊列出了將要顯示在文件列表中的文件型態,此清單方塊被稱為「篩選清單」。使用者可以通過從下拉式清單方塊列表中選擇另一種文件型態,來改變篩選條件。在POPFILE.C的PopFileInitialize函數中,我在變量szFilter(一個字符串數組)中為三種型態的文件定義了一個篩檢清單:帶有.TXT擴展名的文本文件、帶有.ASC擴展名的ASCII文件和所有文件。OPENFILENAME結構的lpstrFilter字段儲存指向此數組第一個字符串的指針。

如果使用者在對話框處于活動狀態時改變了篩選條件,那么OPENFILENAME的nFilterIndex字段反映出使用者的選擇。由于該結構是靜態變量,下次啟動對話框時,篩選條件將被設定為選中的文件型態。

POPFILE.C中的PopFileSaveDlg函數與此類似,它將Flags參數設定為OFN_OVERWRITEPROMPT,并呼叫GetSaveFileName啟動「File Save」對話框。OFN_OVERWRITEPROMPT旗標導致顯示一個消息框,如果被選文件已經存在,那么將詢問使用者是否覆蓋該文件。

Unicode文件I/O

對于本書中的大多數程序,您都不必注意Unicode和非Unicode版的區別。例如,在POPPAD3的Unicode中,編輯控件將保留Unicode文字和使用Unicode字符串的所有通用對話框。例如,當程序需要搜索和替換時,所有的操作都會處理Unicode字符串,而不需要轉換。

不過,POPPAD3得處理文件I/O,也就是說,程序不能閉門造車。如果Unicode版的POPPAD3獲得了編輯緩沖區的內容并將其寫入磁盤,文件將是使用Unicode存放的。如果非Unicode版的POPPAD3讀取了該文件,并將其寫入編輯緩沖區,其結果將是一堆垃圾。Unicode版讀取由非Unicode版儲存的文件時也會這樣。

解決的辦法在于辨別和轉換。首先,在POPFILE.C的PopFileWrite函數中,您將看到Unicode版的程序將在文件的開始位置寫入0xFEFF。這定義為字節順序標記,以表示文本文件含有Unicode文字。

其次,在PopFileRead函數中,程序用IsTextUnicode函數來決定文件是否含有字節順序標記。此函數甚至檢測字節順序標記是否反向了,亦即Unicode文本文件在Macintosh或者其它使用與Intel處理器相反的字節順序的機器上建立的。這時,字節的順序都經過翻轉。如果文件是Unicode版,但是被非Unicode版的POPPAD3讀取,這時,文字將被WideCharToMultiChar轉換。WideCharToMultiChar實際上是一個寬字符ANSI函數(除非您執行遠東版的Windows)。只有這時文字才能放入編輯緩沖區。

同樣地,如果文件是非Unicode文本文件,而執行的是Unicode版的程序,那么文字必須用MultiCharToWideChar轉換。

改變字體

我們將在第十七章`詳細討論字體,但那些都不能代替通用對話框函數來選擇字體。

在WM_CREATE消息處理期間,POPFONT.C中的POPPAD呼叫PopFontInitialize。這個函數取得一個依據系統字體建立的LOGFONT結構,由此建立一種字體,并向編輯控件發送一個WM_SETFONT消息來設定一種新的字體(內定編輯控件字體是系統字體,而PopFontInitialize為編輯控件建立一種新的字體,因為最終該字體將被刪除,而刪除現有系統字體是不明智的)。

當POPPAD收到來自程序的字體選項的WM_COMMAND消息時,它呼叫PopFontChooseFont。這個函數初始化一個CHOOSEFONT結構,然后呼叫ChooseFont顯示字體選擇對話框。如果使用者按下「OK」按鈕,那么ChooseFont將傳回TRUE。隨后,POPPAD呼叫PopFontSetFont來設定編輯控件中的新字體,舊字體將被刪除。

最后,在WM_DESTROY消息處理期間,POPPAD呼叫PopFontDeinitialize來刪除最近一次由PopFontSetFont建立的字體。

搜尋與替換

通用對話框鏈接庫也提供兩個用于文字搜尋和替換函數的對話框,這兩個函數(FindText和ReplaceText)使用一個型態為FINDREPLACE的結構。圖10-11中所示的POPFIND.C文件有兩個例程(PopFindFindDlg和PopFindReplaceDlg)呼叫這些函數,還有兩個函數在編輯控件中搜尋和替換文字。

使用搜尋和替換函數有一些考慮。首先,它們啟動的對話框是非模態對話框,這意味著必須改寫消息循環,以便在對話框活動時呼叫IsDialogMessage。第二,傳送給FindText和ReplaceText的FINDREPLACE結構必須是一個靜態變量,因為對話框是模態的,函數在對話框顯示之后傳回,而不是在對話框結束之后傳回;而對話框程序必須仍然能夠存取該結構。

第三,在顯示FindText和ReplaceText對話框時,它們通過一條特殊消息與擁有者窗口聯絡,消息編號可以通過以FINDMSGSTRING為參數呼叫RegisterWindowMessage函數來獲得。這是在WndProc中處理WM_CREATE消息時完成的,消息號存放在靜態變量中。

在處理內定消息時,WndProc將消息變量與RegisterWindowMessage傳回的值相比較。lParam消息參數是一個指向FINDREPLACE結構的指針,Flags字段指示使用者使用對話框是為了搜尋文字還是替換文字,以及是否要終止對話框。POPPAD3是呼叫POPFIND.C中的PopFindFindText和PopFindReplaceText函數來執行搜尋和替換功能的。

只呼叫一個函數的Windows程序

到現在為止,我們已經說明了兩個程序,讓您瀏覽選擇顏色,這兩個程序分別是第九章中的COLORS1和本章中的COLORS2。現在是講解COLORS3的時候了,這個程序只有一個Windows函數呼叫。COLORS3的原始碼如程序11-7所示。

COLORS3所呼叫的唯一Windows函數是ChooseColor,這也是通用對話框鏈接庫中的函數,它顯示如圖11-7所示的對話框。顏色選擇類似于COLORS1和COLORS2,但是它與使用者交談互動能力更強。

程序11-7 COLORS3COLORS3.C/*-------------------------------------------------------------------------COLORS3.C -- Version using Common Dialog Box(c) Charles Petzold, 1998--------------------------------------------------------------------------*/#include <windows.h>#include <commdlg.h>int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance,PSTR szCmdLine, int iCmdShow){static CHOOSECOLOR cc ;static COLORREF crCustColors[16] ;cc.lStructSize = sizeof (CHOOSECOLOR) ;cc.hwndOwner = NULL ;cc.hInstance = NULL ;cc.rgbResult = RGB (0x80, 0x80, 0x80) ;cc.lpCustColors = crCustColors ;cc.Flags = CC_RGBINIT | CC_FULLOPEN ;cc.lCustData = 0 ;cc.lpfnHook = NULL ;cc.lpTemplateName = NULL ;return ChooseColor (&cc) ;}


 

?

圖11-7 COLORS3的屏幕顯示

ChooseColor函數使用一個CHOOSECOLOR型態的結構和含有16個DWORD的數組來存放常用顏色,使用者將從對話框中選擇這些顏色之一。rgbResult字段可以初始化為一個顏色值,如果Flags字段的CC_RGBINIT旗標被設立,則顯示該顏色。通常在使用這個函數時,rgbResult將被設定為使用者選擇的顏色。

請注意,Color對話框的hwndOwner字段被設定為NULL。在ChooseColor函數呼叫DialogBox以顯示對話框時,DialogBox的第三個參數也被設定為NULL。這是完全合法的,其含義是對話框不為另一個窗口所擁有。對話框的標題將顯示在工作列中,而對話框就像一個普通的窗口那樣執行。

您也可以在自己程序的對話框中使用這種技巧。使Windows程序只建立對話框,其它事情都在對話框程序中完成,這是可能的。

總結

以上是生活随笔為你收集整理的第12章 对话框的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。

亚洲国精产品一二二线 | 亚洲精品成人福利网站 | 亚洲国产日韩a在线播放 | 色婷婷香蕉在线一区二区 | 国产成人综合色在线观看网站 | 97se亚洲精品一区 | 高中生自慰www网站 | 女人被男人躁得好爽免费视频 | 宝宝好涨水快流出来免费视频 | 蜜桃av抽搐高潮一区二区 | 蜜桃臀无码内射一区二区三区 | 成人一区二区免费视频 | 亚洲一区二区三区偷拍女厕 | 免费看少妇作爱视频 | 欧美兽交xxxx×视频 | 国产性生交xxxxx无码 | 国产精品亚洲а∨无码播放麻豆 | 亚洲精品久久久久avwww潮水 | 精品久久8x国产免费观看 | 久久国产精品二国产精品 | 高清无码午夜福利视频 | 久久久久久久久蜜桃 | 亚洲一区二区三区播放 | 亚洲一区av无码专区在线观看 | 动漫av一区二区在线观看 | 日韩欧美成人免费观看 | 在线天堂新版最新版在线8 | 婷婷六月久久综合丁香 | 精品 日韩 国产 欧美 视频 | 久久精品国产一区二区三区肥胖 | 精品午夜福利在线观看 | 日本又色又爽又黄的a片18禁 | 男女下面进入的视频免费午夜 | 国产手机在线αⅴ片无码观看 | 国产成人无码av在线影院 | 久久精品一区二区三区四区 | 麻豆果冻传媒2021精品传媒一区下载 | 国产午夜福利亚洲第一 | 波多野结衣乳巨码无在线观看 | 国产成人无码av在线影院 | 伊人久久婷婷五月综合97色 | 午夜免费福利小电影 | 精品无码成人片一区二区98 | 国产乡下妇女做爰 | 老熟女重囗味hdxx69 | 女人被男人躁得好爽免费视频 | 国产精品手机免费 | 鲁大师影院在线观看 | 毛片内射-百度 | 一本精品99久久精品77 | 亚无码乱人伦一区二区 | 久久人人97超碰a片精品 | 日韩 欧美 动漫 国产 制服 | 亚洲人成影院在线观看 | 国产精品久久久一区二区三区 | 国产精品人人爽人人做我的可爱 | 人人澡人人妻人人爽人人蜜桃 | 久久久中文久久久无码 | 国产莉萝无码av在线播放 | 精品一区二区三区波多野结衣 | 国产97人人超碰caoprom | 午夜精品一区二区三区在线观看 | 在线观看欧美一区二区三区 | 综合人妻久久一区二区精品 | 国产无遮挡又黄又爽免费视频 | 一二三四在线观看免费视频 | 亚洲熟妇色xxxxx亚洲 | 中文久久乱码一区二区 | 精品aⅴ一区二区三区 | 亚洲国产午夜精品理论片 | 久久人人爽人人人人片 | 无码帝国www无码专区色综合 | 国内精品九九久久久精品 | 亚洲精品国产精品乱码不卡 | 亚洲人成影院在线无码按摩店 | 色欲人妻aaaaaaa无码 | 国产精品人妻一区二区三区四 | 国产性生交xxxxx无码 | 又大又黄又粗又爽的免费视频 | 国产精品国产自线拍免费软件 | 午夜成人1000部免费视频 | 欧美日韩色另类综合 | 少妇性俱乐部纵欲狂欢电影 | 中文字幕久久久久人妻 | 亚洲欧美日韩综合久久久 | 四虎永久在线精品免费网址 | 四虎影视成人永久免费观看视频 | 亚洲另类伦春色综合小说 | 日本一卡2卡3卡四卡精品网站 | 全球成人中文在线 | 一二三四在线观看免费视频 | 在线天堂新版最新版在线8 | 欧美激情一区二区三区成人 | 亚洲国产一区二区三区在线观看 | 国产精华av午夜在线观看 | 久久国语露脸国产精品电影 | 国产午夜视频在线观看 | 亚洲a无码综合a国产av中文 | 国产精品美女久久久久av爽李琼 | 国产人妻久久精品二区三区老狼 | 荡女精品导航 | 天天拍夜夜添久久精品大 | 好爽又高潮了毛片免费下载 | 夜夜躁日日躁狠狠久久av | 啦啦啦www在线观看免费视频 | 亚洲区小说区激情区图片区 | 国产尤物精品视频 | 欧美放荡的少妇 | 欧美人与物videos另类 | 国产在线精品一区二区高清不卡 | 99国产欧美久久久精品 | 精品少妇爆乳无码av无码专区 | 久久午夜无码鲁丝片午夜精品 | 爽爽影院免费观看 | 国产超级va在线观看视频 | 亚洲人成无码网www | 国产xxx69麻豆国语对白 | 特黄特色大片免费播放器图片 | 纯爱无遮挡h肉动漫在线播放 | 76少妇精品导航 | 乱中年女人伦av三区 | 中文无码伦av中文字幕 | 在线a亚洲视频播放在线观看 | 5858s亚洲色大成网站www | 日韩人妻无码中文字幕视频 | www国产亚洲精品久久久日本 | 中文亚洲成a人片在线观看 | 97se亚洲精品一区 | 亚洲综合色区中文字幕 | 亚洲国产成人a精品不卡在线 | 成人无码视频在线观看网站 | 伊人色综合久久天天小片 | 国产午夜福利亚洲第一 | 亚洲一区二区三区无码久久 | 曰韩少妇内射免费播放 | 色爱情人网站 | 国产成人综合美国十次 | 亚洲男女内射在线播放 | 久久国产精品_国产精品 | 国产乱人偷精品人妻a片 | 婷婷五月综合缴情在线视频 | 夜精品a片一区二区三区无码白浆 | 中文字幕乱码亚洲无线三区 | 大地资源中文第3页 | 2019午夜福利不卡片在线 | 三上悠亚人妻中文字幕在线 | 国产午夜无码视频在线观看 | 好爽又高潮了毛片免费下载 | 欧美 日韩 亚洲 在线 | 国产成人无码av一区二区 | 中文精品久久久久人妻不卡 | 国产在线精品一区二区高清不卡 | 少妇一晚三次一区二区三区 | 亚洲精品一区二区三区大桥未久 | 性欧美疯狂xxxxbbbb | 天天摸天天透天天添 | 欧美日本精品一区二区三区 | 欧美成人高清在线播放 | a在线观看免费网站大全 | 久久精品成人欧美大片 | 国产人妻久久精品二区三区老狼 | 狂野欧美激情性xxxx | 亚洲自偷自偷在线制服 | 久久亚洲精品中文字幕无男同 | 亚洲欧洲中文日韩av乱码 | 国产精品亚洲五月天高清 | 国产色xx群视频射精 | 国产精品久久国产精品99 | 老太婆性杂交欧美肥老太 | 人人妻人人澡人人爽精品欧美 | 国产麻豆精品一区二区三区v视界 | 激情内射日本一区二区三区 | 欧美人与牲动交xxxx | 国产精品福利视频导航 | 亚洲精品一区二区三区婷婷月 | 波多野结衣高清一区二区三区 | 人妻体内射精一区二区三四 | 久久精品国产日本波多野结衣 | 色综合久久网 | 亚洲s色大片在线观看 | 十八禁视频网站在线观看 | 奇米影视7777久久精品 | 国产国产精品人在线视 | aⅴ在线视频男人的天堂 | 少妇被黑人到高潮喷出白浆 | 国产精品高潮呻吟av久久 | 精品无人区无码乱码毛片国产 | 中文字幕人妻无码一夲道 | 亚洲男人av香蕉爽爽爽爽 | 亚洲色偷偷男人的天堂 | 国产成人久久精品流白浆 | 99er热精品视频 | 乱人伦中文视频在线观看 | 久久久久久久女国产乱让韩 | 午夜理论片yy44880影院 | 亚洲一区av无码专区在线观看 | 午夜熟女插插xx免费视频 | 真人与拘做受免费视频 | 欧美日韩亚洲国产精品 | 欧美三级不卡在线观看 | 久久人人爽人人爽人人片ⅴ | 夜精品a片一区二区三区无码白浆 | 狠狠综合久久久久综合网 | 亚洲s色大片在线观看 | 老熟妇仑乱视频一区二区 | 日韩亚洲欧美中文高清在线 | 中国女人内谢69xxxxxa片 | 波多野结衣乳巨码无在线观看 | 少妇久久久久久人妻无码 | 亚洲日本一区二区三区在线 | 成 人 免费观看网站 | 日本欧美一区二区三区乱码 | 狠狠综合久久久久综合网 | 日本丰满护士爆乳xxxx | 丰满岳乱妇在线观看中字无码 | 男人扒开女人内裤强吻桶进去 | 水蜜桃亚洲一二三四在线 | 综合网日日天干夜夜久久 | 国产精品对白交换视频 | 在线播放亚洲第一字幕 | 亚洲s色大片在线观看 | 午夜成人1000部免费视频 | 无码毛片视频一区二区本码 | 久久久久成人精品免费播放动漫 | 97se亚洲精品一区 | 欧美国产日韩久久mv | 国产热a欧美热a在线视频 | 无码播放一区二区三区 | 成 人 网 站国产免费观看 | 1000部夫妻午夜免费 | 麻花豆传媒剧国产免费mv在线 | 人人超人人超碰超国产 | 永久免费观看国产裸体美女 | 国产精品鲁鲁鲁 | 美女扒开屁股让男人桶 | 影音先锋中文字幕无码 | 国产精品久久福利网站 | 亚洲成av人片天堂网无码】 | 熟女少妇在线视频播放 | 欧美一区二区三区 | 精品久久久无码人妻字幂 | 奇米综合四色77777久久 东京无码熟妇人妻av在线网址 | 无码成人精品区在线观看 | 狂野欧美激情性xxxx | 国产无遮挡又黄又爽又色 | 四虎永久在线精品免费网址 | 亚洲欧美国产精品久久 | 99久久精品无码一区二区毛片 | 久久视频在线观看精品 | 亚洲日韩中文字幕在线播放 | 白嫩日本少妇做爰 | 亚洲一区二区三区含羞草 | 亚洲综合伊人久久大杳蕉 | 日本一区二区三区免费高清 | 欧美激情一区二区三区成人 | 久久久av男人的天堂 | 午夜福利试看120秒体验区 | 色婷婷av一区二区三区之红樱桃 | 亚洲精品久久久久中文第一幕 | 亚洲成熟女人毛毛耸耸多 | 婷婷五月综合缴情在线视频 | 国产人妻精品午夜福利免费 | 亚洲爆乳大丰满无码专区 | 大色综合色综合网站 | 国产偷国产偷精品高清尤物 | 美女极度色诱视频国产 | 黑人大群体交免费视频 | 亚洲自偷自拍另类第1页 | 亚洲成a人片在线观看无码3d | 久久午夜无码鲁丝片 | 宝宝好涨水快流出来免费视频 | 国产激情一区二区三区 | av人摸人人人澡人人超碰下载 | 无码国内精品人妻少妇 | 国产97色在线 | 免 | 性生交片免费无码看人 | 狠狠色欧美亚洲狠狠色www | 久久人人爽人人爽人人片av高清 | 人妻夜夜爽天天爽三区 | 51国偷自产一区二区三区 | 18黄暴禁片在线观看 | 日日鲁鲁鲁夜夜爽爽狠狠 | 国产电影无码午夜在线播放 | 最新国产麻豆aⅴ精品无码 | 天天燥日日燥 | 夜夜躁日日躁狠狠久久av | 欧洲美熟女乱又伦 | 国内精品人妻无码久久久影院 | 又粗又大又硬毛片免费看 | 国产又爽又黄又刺激的视频 | 欧美高清在线精品一区 | 美女扒开屁股让男人桶 | 兔费看少妇性l交大片免费 | 国精产品一区二区三区 | 国产精品久久久久久无码 | 日韩人妻无码中文字幕视频 | 国产午夜福利亚洲第一 | 少妇人妻大乳在线视频 | 在线播放亚洲第一字幕 | 牲欲强的熟妇农村老妇女 | 玩弄中年熟妇正在播放 | 黑人巨大精品欧美黑寡妇 | 国产真实乱对白精彩久久 | 色婷婷欧美在线播放内射 | 成 人 网 站国产免费观看 | 国产精品理论片在线观看 | 欧洲美熟女乱又伦 | 影音先锋中文字幕无码 | 97久久国产亚洲精品超碰热 | 午夜无码人妻av大片色欲 | 亚洲精品美女久久久久久久 | 亚洲欧美中文字幕5发布 | 无遮无挡爽爽免费视频 | 国产网红无码精品视频 | 久久亚洲国产成人精品性色 | 国产人成高清在线视频99最全资源 | 亚洲乱码日产精品bd | 好爽又高潮了毛片免费下载 | 日本在线高清不卡免费播放 | 99精品无人区乱码1区2区3区 | 麻花豆传媒剧国产免费mv在线 | 99久久人妻精品免费二区 | 国产三级精品三级男人的天堂 | 美女极度色诱视频国产 | 国产办公室秘书无码精品99 | 欧美老熟妇乱xxxxx | 国产香蕉尹人综合在线观看 | 精品国产一区二区三区四区在线看 | 国产莉萝无码av在线播放 | 欧洲美熟女乱又伦 | 在线成人www免费观看视频 | 国产精品无码成人午夜电影 | 久久99热只有频精品8 | 色诱久久久久综合网ywww | 99久久无码一区人妻 | 人妻无码αv中文字幕久久琪琪布 | 欧美三级a做爰在线观看 | 天堂无码人妻精品一区二区三区 | 青草视频在线播放 | 精品无人国产偷自产在线 | 国产国产精品人在线视 | 无套内谢的新婚少妇国语播放 | 性色欲网站人妻丰满中文久久不卡 | 丰满人妻精品国产99aⅴ | 久久综合激激的五月天 | 一本久久a久久精品vr综合 | 人妻无码久久精品人妻 | 国产99久久精品一区二区 | 奇米影视7777久久精品人人爽 | 7777奇米四色成人眼影 | 天堂а√在线地址中文在线 | 亚洲成av人在线观看网址 | 樱花草在线播放免费中文 | 亚洲综合伊人久久大杳蕉 | 九一九色国产 | 亚洲精品国产a久久久久久 | 88国产精品欧美一区二区三区 | 午夜时刻免费入口 | 人人妻人人澡人人爽欧美一区 | 久久精品丝袜高跟鞋 | 欧美精品免费观看二区 | 亚洲精品久久久久中文第一幕 | 亚洲码国产精品高潮在线 | 久久国产自偷自偷免费一区调 | 国内丰满熟女出轨videos | 亚洲 a v无 码免 费 成 人 a v | 国产婷婷色一区二区三区在线 | 鲁一鲁av2019在线 | 色五月丁香五月综合五月 | 中文精品久久久久人妻不卡 | 暴力强奷在线播放无码 | 牲欲强的熟妇农村老妇女 | 日韩精品一区二区av在线 | 亚洲 日韩 欧美 成人 在线观看 | 美女毛片一区二区三区四区 | 国产一精品一av一免费 | 51国偷自产一区二区三区 | 国产精品无码久久av | 欧美老妇交乱视频在线观看 | 久久精品无码一区二区三区 | 久久综合给久久狠狠97色 | 99麻豆久久久国产精品免费 | 麻豆人妻少妇精品无码专区 | 在线精品国产一区二区三区 | 亚洲小说春色综合另类 | 久久精品女人的天堂av | 国产av剧情md精品麻豆 | 国产人妖乱国产精品人妖 | 青草视频在线播放 | 高潮毛片无遮挡高清免费视频 | 纯爱无遮挡h肉动漫在线播放 | 国产精品人人爽人人做我的可爱 | 99久久人妻精品免费一区 | 国产精品久免费的黄网站 | 无码人妻黑人中文字幕 | 亚洲 日韩 欧美 成人 在线观看 | 综合网日日天干夜夜久久 | 久久国内精品自在自线 | 人人妻人人澡人人爽欧美一区九九 | 领导边摸边吃奶边做爽在线观看 | 亚洲狠狠色丁香婷婷综合 | 久久成人a毛片免费观看网站 | 国产艳妇av在线观看果冻传媒 | 亚洲s码欧洲m码国产av | 性生交大片免费看女人按摩摩 | 一本久道久久综合婷婷五月 | 国产成人av免费观看 | 国产三级精品三级男人的天堂 | 男女下面进入的视频免费午夜 | 国产在线无码精品电影网 | 欧美人与牲动交xxxx | 亚洲成熟女人毛毛耸耸多 | 亚洲精品午夜国产va久久成人 | 女人被爽到呻吟gif动态图视看 | 久久无码中文字幕免费影院蜜桃 | 久久精品人妻少妇一区二区三区 | 呦交小u女精品视频 | 久久亚洲a片com人成 | 成人欧美一区二区三区黑人免费 | 丰满人妻精品国产99aⅴ | 99国产精品白浆在线观看免费 | 国产三级精品三级男人的天堂 | 鲁一鲁av2019在线 | 最新版天堂资源中文官网 | 久久99精品久久久久婷婷 | 99视频精品全部免费免费观看 | 久久视频在线观看精品 | 桃花色综合影院 | 亚洲国产午夜精品理论片 | 精品国偷自产在线视频 | 高中生自慰www网站 | 久久久久久亚洲精品a片成人 | 男女超爽视频免费播放 | 红桃av一区二区三区在线无码av | 久久午夜夜伦鲁鲁片无码免费 | 少妇人妻大乳在线视频 | 欧美亚洲日韩国产人成在线播放 | 国产欧美亚洲精品a | 精品偷自拍另类在线观看 | 国产99久久精品一区二区 | 国产精品久久精品三级 | 无码av免费一区二区三区试看 | 377p欧洲日本亚洲大胆 | 亚洲第一网站男人都懂 | 天堂а√在线中文在线 | 久久无码专区国产精品s | 精品无人国产偷自产在线 | 97无码免费人妻超级碰碰夜夜 | 强开小婷嫩苞又嫩又紧视频 | 老司机亚洲精品影院无码 | 欧美国产日韩久久mv | 国产人成高清在线视频99最全资源 | 四虎国产精品免费久久 | 熟女体下毛毛黑森林 | 亚洲天堂2017无码 | 欧美日韩一区二区三区自拍 | 一本大道久久东京热无码av | 亚洲一区二区三区在线观看网站 | 国产成人无码a区在线观看视频app | 97夜夜澡人人双人人人喊 | 免费网站看v片在线18禁无码 | 99久久精品日本一区二区免费 | 国产精品久久久久久亚洲影视内衣 | 国产精品美女久久久久av爽李琼 | 在线观看免费人成视频 | 亚拍精品一区二区三区探花 | 97久久精品无码一区二区 | 伊人色综合久久天天小片 | 免费人成网站视频在线观看 | 熟妇女人妻丰满少妇中文字幕 | 国产真实伦对白全集 | 久久亚洲中文字幕无码 | 国产电影无码午夜在线播放 | 99精品无人区乱码1区2区3区 | 久久久成人毛片无码 | 欧美成人免费全部网站 | 狂野欧美性猛交免费视频 | 久久午夜无码鲁丝片秋霞 | 亚洲男女内射在线播放 | 日韩在线不卡免费视频一区 | 台湾无码一区二区 | 白嫩日本少妇做爰 | 任你躁国产自任一区二区三区 | 亚洲成av人综合在线观看 | 国产精品无码久久av | 又紧又大又爽精品一区二区 | 国产无av码在线观看 | 欧美黑人巨大xxxxx | 巨爆乳无码视频在线观看 | 久久精品国产精品国产精品污 | 久久99精品国产麻豆蜜芽 | 精品国产aⅴ无码一区二区 | 伊人久久婷婷五月综合97色 | 中文无码伦av中文字幕 | 天堂а√在线地址中文在线 | 亚洲人成网站色7799 | 日本又色又爽又黄的a片18禁 | 免费无码午夜福利片69 | 色噜噜亚洲男人的天堂 | 欧洲vodafone精品性 | 欧美日韩在线亚洲综合国产人 | 欧美高清在线精品一区 | 精品无人区无码乱码毛片国产 | 国产成人午夜福利在线播放 | 亚洲成色在线综合网站 | 波多野结衣一区二区三区av免费 | 欧美阿v高清资源不卡在线播放 | 国产精品亚洲综合色区韩国 | 午夜不卡av免费 一本久久a久久精品vr综合 | 国产色视频一区二区三区 | 麻豆国产人妻欲求不满 | 5858s亚洲色大成网站www | 蜜桃av蜜臀av色欲av麻 999久久久国产精品消防器材 | 熟女少妇人妻中文字幕 | 又粗又大又硬又长又爽 | 日日碰狠狠丁香久燥 | 麻豆精产国品 | 无遮无挡爽爽免费视频 | 国产极品美女高潮无套在线观看 | 一本一道久久综合久久 | 免费中文字幕日韩欧美 | 国产欧美精品一区二区三区 | 国产真实乱对白精彩久久 | 亚洲自偷自拍另类第1页 | 麻花豆传媒剧国产免费mv在线 | 色综合久久88色综合天天 | 亚洲a无码综合a国产av中文 | 无码免费一区二区三区 | 欧美国产日产一区二区 | 国产无遮挡又黄又爽又色 | 97久久精品无码一区二区 | 无码乱肉视频免费大全合集 | 成 人 网 站国产免费观看 | 国产超级va在线观看视频 | 妺妺窝人体色www在线小说 | 牛和人交xxxx欧美 | 日本精品少妇一区二区三区 | 精品国产成人一区二区三区 | 国产成人无码区免费内射一片色欲 | 日本熟妇大屁股人妻 | 色综合天天综合狠狠爱 | 国内精品人妻无码久久久影院蜜桃 | 亚洲 a v无 码免 费 成 人 a v | 久久国产精品_国产精品 | a在线亚洲男人的天堂 | 天天摸天天透天天添 | 无遮挡国产高潮视频免费观看 | 久久国产精品_国产精品 | 亚洲国产精品久久人人爱 | 免费人成在线视频无码 | 中文字幕乱妇无码av在线 | 日本精品少妇一区二区三区 | 久久久久久久久888 | 国内精品人妻无码久久久影院 | 久久亚洲国产成人精品性色 | 男女超爽视频免费播放 | 白嫩日本少妇做爰 | 国内精品久久久久久中文字幕 | 国产成人亚洲综合无码 | 99麻豆久久久国产精品免费 | 麻花豆传媒剧国产免费mv在线 | 麻豆精品国产精华精华液好用吗 | 人妻体内射精一区二区三四 | 成人无码视频免费播放 | 国产特级毛片aaaaaa高潮流水 | 国产99久久精品一区二区 | 2020久久超碰国产精品最新 | 久久亚洲日韩精品一区二区三区 | 国产综合久久久久鬼色 | 中文字幕色婷婷在线视频 | 亚洲aⅴ无码成人网站国产app | 女高中生第一次破苞av | 国产9 9在线 | 中文 | 久久久婷婷五月亚洲97号色 | 在线观看国产午夜福利片 | 国产内射老熟女aaaa | 久久天天躁狠狠躁夜夜免费观看 | 女高中生第一次破苞av | 自拍偷自拍亚洲精品被多人伦好爽 | 日日摸日日碰夜夜爽av | 国产av一区二区三区最新精品 | 免费观看又污又黄的网站 | 久久久亚洲欧洲日产国码αv | 国产成人无码一二三区视频 | 久久久国产精品无码免费专区 | 国产sm调教视频在线观看 | 99久久精品日本一区二区免费 | 亚洲人成影院在线无码按摩店 | 又大又硬又黄的免费视频 | 福利一区二区三区视频在线观看 | 无码毛片视频一区二区本码 | 欧美三级不卡在线观看 | 精品乱子伦一区二区三区 | 国内综合精品午夜久久资源 | 亚洲 欧美 激情 小说 另类 | 久久亚洲国产成人精品性色 | 国产精品内射视频免费 | 无码人妻av免费一区二区三区 | 国产精品美女久久久久av爽李琼 | 少女韩国电视剧在线观看完整 | 成人精品视频一区二区 | 久久99久久99精品中文字幕 | 亚洲无人区午夜福利码高清完整版 | √天堂资源地址中文在线 | 欧美35页视频在线观看 | 国产精品久久久久久无码 | 色情久久久av熟女人妻网站 | 国内精品久久久久久中文字幕 | www国产精品内射老师 | 永久黄网站色视频免费直播 | 在线观看国产一区二区三区 | 亚洲一区二区三区四区 | 97夜夜澡人人爽人人喊中国片 | 欧美国产亚洲日韩在线二区 | 国产激情精品一区二区三区 | 97精品人妻一区二区三区香蕉 | 国产午夜精品一区二区三区嫩草 | 色欲久久久天天天综合网精品 | 中文字幕日产无线码一区 | 亚洲日韩av一区二区三区中文 | 在线а√天堂中文官网 | 无码免费一区二区三区 | 狂野欧美激情性xxxx | 亚洲最大成人网站 | 免费人成在线观看网站 | 国内少妇偷人精品视频免费 | 国产情侣作爱视频免费观看 | 老熟妇仑乱视频一区二区 | 日韩av无码一区二区三区 | 伊人久久大香线蕉av一区二区 | 日韩av无码一区二区三区 | 国产精品理论片在线观看 | 纯爱无遮挡h肉动漫在线播放 | 久久视频在线观看精品 | 天天躁日日躁狠狠躁免费麻豆 | 亚洲欧美中文字幕5发布 | 成人性做爰aaa片免费看不忠 | 精品日本一区二区三区在线观看 | 欧美大屁股xxxxhd黑色 | 日本在线高清不卡免费播放 | 国产免费久久久久久无码 | 亚洲国产精品毛片av不卡在线 | 色一情一乱一伦一视频免费看 | 国产乱人偷精品人妻a片 | 日韩人妻无码一区二区三区久久99 | 中文字幕人成乱码熟女app | 亚洲成在人网站无码天堂 | 亚洲精品一区二区三区大桥未久 | 精品一区二区三区波多野结衣 | 秋霞成人午夜鲁丝一区二区三区 | 欧美阿v高清资源不卡在线播放 | 性生交大片免费看女人按摩摩 | 国产绳艺sm调教室论坛 | 国产明星裸体无码xxxx视频 | 国内老熟妇对白xxxxhd | 久久久久久a亚洲欧洲av冫 | 无码一区二区三区在线观看 | 小泽玛莉亚一区二区视频在线 | 日韩欧美中文字幕在线三区 | 欧美丰满熟妇xxxx性ppx人交 | 性欧美大战久久久久久久 | 波多野结衣一区二区三区av免费 | 国产办公室秘书无码精品99 | 成人无码视频在线观看网站 | 久久国内精品自在自线 | 少妇邻居内射在线 | 亚洲精品久久久久久一区二区 | 精品少妇爆乳无码av无码专区 | 台湾无码一区二区 | 精品久久久久香蕉网 | 国产色xx群视频射精 | 日本www一道久久久免费榴莲 | 国产人成高清在线视频99最全资源 | 正在播放老肥熟妇露脸 | 久久久久免费看成人影片 | 波多野结衣av一区二区全免费观看 | 老太婆性杂交欧美肥老太 | 精品一区二区三区波多野结衣 | 精品无码av一区二区三区 | 国产av人人夜夜澡人人爽麻豆 | 国产乱码精品一品二品 | 玩弄少妇高潮ⅹxxxyw | 动漫av网站免费观看 | а天堂中文在线官网 | 欧美野外疯狂做受xxxx高潮 | 人人妻人人澡人人爽欧美一区 | 国产三级精品三级男人的天堂 | 久久午夜无码鲁丝片午夜精品 | 久久精品国产日本波多野结衣 | 丰满护士巨好爽好大乳 | 国产精品无码mv在线观看 | 成人毛片一区二区 | 内射欧美老妇wbb | 中文字幕乱码人妻无码久久 | 高清国产亚洲精品自在久久 | 在线视频网站www色 | 亚洲日韩一区二区三区 | 色综合天天综合狠狠爱 | 精品国产一区二区三区四区在线看 | 成人亚洲精品久久久久软件 | 色综合久久久久综合一本到桃花网 | 色诱久久久久综合网ywww | 国产精品对白交换视频 | 国产区女主播在线观看 | 色欲人妻aaaaaaa无码 | 国产av一区二区精品久久凹凸 | 国产精品永久免费视频 | 欧美精品一区二区精品久久 | 国产高潮视频在线观看 | 国产免费无码一区二区视频 | 帮老师解开蕾丝奶罩吸乳网站 | 无码国产色欲xxxxx视频 | 最新国产乱人伦偷精品免费网站 | 国产精品人人妻人人爽 | 午夜福利一区二区三区在线观看 | 国产精品资源一区二区 | 人人爽人人澡人人高潮 | 午夜精品久久久久久久久 | 兔费看少妇性l交大片免费 | 亚洲爆乳无码专区 | 成人片黄网站色大片免费观看 | 中文字幕 亚洲精品 第1页 | 波多野42部无码喷潮在线 | 天天燥日日燥 | 一区二区三区高清视频一 | 99精品视频在线观看免费 | 熟女俱乐部五十路六十路av | 99久久久国产精品无码免费 | 激情五月综合色婷婷一区二区 | 国产精品久久久久久亚洲影视内衣 | 国产精品毛多多水多 | 人人妻人人澡人人爽人人精品 | 亚洲人亚洲人成电影网站色 | 久久亚洲国产成人精品性色 | 亚洲国产精品毛片av不卡在线 | 国产成人综合色在线观看网站 | 国产香蕉尹人视频在线 | 爆乳一区二区三区无码 | 无遮无挡爽爽免费视频 | 成人一在线视频日韩国产 | 一个人看的视频www在线 | 成人动漫在线观看 | 国产乱码精品一品二品 | 日本丰满熟妇videos | 中文字幕无码免费久久99 | 少妇性l交大片欧洲热妇乱xxx | 亚洲中文字幕乱码av波多ji | 亚拍精品一区二区三区探花 | 奇米影视7777久久精品人人爽 | 成 人 免费观看网站 | 伊在人天堂亚洲香蕉精品区 | 国产精品久久久 | 日本肉体xxxx裸交 | 欧美成人午夜精品久久久 | 亚洲a无码综合a国产av中文 | 成人无码视频免费播放 | 日本www一道久久久免费榴莲 | 麻豆蜜桃av蜜臀av色欲av | 两性色午夜视频免费播放 | 欧美人与动性行为视频 | 在线a亚洲视频播放在线观看 | 国产超级va在线观看视频 | 中文字幕无码人妻少妇免费 | 动漫av网站免费观看 | 牲欲强的熟妇农村老妇女视频 | 国产亚洲人成在线播放 | 少女韩国电视剧在线观看完整 | 亚洲成熟女人毛毛耸耸多 | 久久综合香蕉国产蜜臀av | 精品国精品国产自在久国产87 | 久久www免费人成人片 | 亚洲国产精品一区二区第一页 | 少妇久久久久久人妻无码 | 免费无码的av片在线观看 | 人人妻人人澡人人爽精品欧美 | 亚洲性无码av中文字幕 | 欧美成人家庭影院 | 国产高清av在线播放 | 日本高清一区免费中文视频 | 无码av免费一区二区三区试看 | 扒开双腿疯狂进出爽爽爽视频 | 国产精品久久久久无码av色戒 | 精品厕所偷拍各类美女tp嘘嘘 | 亚洲爆乳精品无码一区二区三区 | 国产农村妇女高潮大叫 | 免费中文字幕日韩欧美 | 欧美亚洲日韩国产人成在线播放 | 曰韩无码二三区中文字幕 | 波多野结衣一区二区三区av免费 | 色五月丁香五月综合五月 | 久久国产自偷自偷免费一区调 | 强奷人妻日本中文字幕 | 国产精品资源一区二区 | 双乳奶水饱满少妇呻吟 | 亚洲成a人片在线观看无码3d | 55夜色66夜色国产精品视频 | 荫蒂添的好舒服视频囗交 | 性生交片免费无码看人 | 亚洲中文字幕无码中文字在线 | 久久综合色之久久综合 | 自拍偷自拍亚洲精品被多人伦好爽 | 国产莉萝无码av在线播放 | 亚洲乱亚洲乱妇50p | 国产va免费精品观看 | 老头边吃奶边弄进去呻吟 | 狠狠噜狠狠狠狠丁香五月 | 夜夜高潮次次欢爽av女 | 又大又硬又黄的免费视频 | 国产亚洲tv在线观看 | 久在线观看福利视频 | 国产精品.xx视频.xxtv | 中文字幕无码免费久久9一区9 | 精品一区二区三区无码免费视频 | 免费观看激色视频网站 | 成人精品一区二区三区中文字幕 | 亚洲色欲久久久综合网东京热 | 国产黄在线观看免费观看不卡 | 精品无人国产偷自产在线 | 动漫av一区二区在线观看 | 成人av无码一区二区三区 | 丰满少妇熟乱xxxxx视频 | 欧美精品国产综合久久 | 国产人妻精品午夜福利免费 | 国精产品一品二品国精品69xx | 亚洲精品国产品国语在线观看 | 亚洲a无码综合a国产av中文 | 免费国产成人高清在线观看网站 | 2020最新国产自产精品 | 在线播放无码字幕亚洲 | 国色天香社区在线视频 | 波多野结衣乳巨码无在线观看 | 最新版天堂资源中文官网 | 无码播放一区二区三区 | 亚洲精品久久久久久久久久久 | 精品国产精品久久一区免费式 | 欧美日韩综合一区二区三区 | 蜜桃视频插满18在线观看 | 麻豆精产国品 | 99精品久久毛片a片 | 亚洲第一网站男人都懂 | 国产av无码专区亚洲a∨毛片 | 天下第一社区视频www日本 | 丰满少妇熟乱xxxxx视频 | 51国偷自产一区二区三区 | 男女下面进入的视频免费午夜 | 亚洲午夜无码久久 | 日本xxxx色视频在线观看免费 | 欧美猛少妇色xxxxx | 成年美女黄网站色大免费全看 | 色欲av亚洲一区无码少妇 | 又大又黄又粗又爽的免费视频 | 一本色道婷婷久久欧美 | 中文字幕无线码免费人妻 | 强开小婷嫩苞又嫩又紧视频 | 国产精品久久久久久无码 | 67194成是人免费无码 | 国产精品久久久久久久影院 | 少妇性l交大片 | 亚洲精品综合五月久久小说 | 日本高清一区免费中文视频 | 国产国产精品人在线视 | 亲嘴扒胸摸屁股激烈网站 | 中文字幕日韩精品一区二区三区 | 亚洲成熟女人毛毛耸耸多 | 樱花草在线社区www | 国产精品久久久久影院嫩草 | 色欲人妻aaaaaaa无码 | 在线欧美精品一区二区三区 | 中文字幕无码视频专区 | 亚洲国产精品一区二区美利坚 | 国产精品成人av在线观看 | 99久久久无码国产aaa精品 | 国产精品久久国产精品99 | 久久午夜夜伦鲁鲁片无码免费 | 国产美女极度色诱视频www | 亚洲精品美女久久久久久久 | 中国大陆精品视频xxxx | 色综合久久久久综合一本到桃花网 | 丝袜足控一区二区三区 | 国内老熟妇对白xxxxhd | 娇妻被黑人粗大高潮白浆 | 国产在线一区二区三区四区五区 | 成人欧美一区二区三区 | 性色欲情网站iwww九文堂 | 2020最新国产自产精品 | 国精产品一品二品国精品69xx | 欧美日韩一区二区免费视频 | 欧美刺激性大交 | 伊人久久大香线蕉av一区二区 | 熟妇人妻无码xxx视频 | 18精品久久久无码午夜福利 | 亚洲日韩乱码中文无码蜜桃臀网站 | 人人妻人人澡人人爽欧美一区九九 | 日本一卡2卡3卡4卡无卡免费网站 国产一区二区三区影院 | 丰满少妇弄高潮了www | 国产9 9在线 | 中文 | 激情五月综合色婷婷一区二区 | 精品一区二区三区无码免费视频 | 九九久久精品国产免费看小说 | 国产va免费精品观看 | 国精产品一品二品国精品69xx | 国产精品va在线观看无码 | 牛和人交xxxx欧美 | 亚洲国产精品美女久久久久 | 一本无码人妻在中文字幕免费 | 国产99久久精品一区二区 | 99精品国产综合久久久久五月天 | 97夜夜澡人人双人人人喊 | 国产黄在线观看免费观看不卡 | 水蜜桃色314在线观看 | 中文亚洲成a人片在线观看 | 免费无码肉片在线观看 | 一个人免费观看的www视频 | 国产肉丝袜在线观看 | 亚洲国产一区二区三区在线观看 | 亚洲中文字幕在线观看 | 日韩精品成人一区二区三区 | 少妇人妻av毛片在线看 | 国产一区二区不卡老阿姨 | 丁香啪啪综合成人亚洲 | 天天综合网天天综合色 | 国产成人精品优优av | 人妻尝试又大又粗久久 | 国产真人无遮挡作爱免费视频 | 老熟女乱子伦 | 色综合久久中文娱乐网 | 激情内射亚州一区二区三区爱妻 | 精品久久久久香蕉网 | 欧美日韩久久久精品a片 | 国产精品内射视频免费 | 国产精品久久久久久亚洲影视内衣 | av无码电影一区二区三区 | 蜜桃av抽搐高潮一区二区 | 国产真实伦对白全集 | 国产精品欧美成人 | 国产在线aaa片一区二区99 | 少妇久久久久久人妻无码 | 久热国产vs视频在线观看 | 免费观看激色视频网站 | 久久99精品国产麻豆蜜芽 | 精品乱子伦一区二区三区 | 色噜噜亚洲男人的天堂 | 欧美高清在线精品一区 | 性色欲情网站iwww九文堂 | 无码纯肉视频在线观看 | 成熟女人特级毛片www免费 | 性欧美大战久久久久久久 | 人妻体内射精一区二区三四 | 国产av人人夜夜澡人人爽麻豆 | 国产av人人夜夜澡人人爽麻豆 | 欧美性猛交xxxx富婆 | 欧美熟妇另类久久久久久不卡 | 少妇无码吹潮 | 水蜜桃亚洲一二三四在线 | 国产一区二区不卡老阿姨 | 又大又黄又粗又爽的免费视频 | 99久久人妻精品免费二区 | 精品乱子伦一区二区三区 | 一本色道婷婷久久欧美 | 日本乱人伦片中文三区 | 国产精品久久精品三级 | 无码吃奶揉捏奶头高潮视频 | 国产农村妇女aaaaa视频 撕开奶罩揉吮奶头视频 | 我要看www免费看插插视频 | 黑人巨大精品欧美一区二区 | 亚洲第一无码av无码专区 | 蜜桃av抽搐高潮一区二区 | 装睡被陌生人摸出水好爽 | 丰满人妻精品国产99aⅴ | 日本精品高清一区二区 | 无码人妻精品一区二区三区不卡 | 波多野结衣高清一区二区三区 | 亚洲成a人片在线观看日本 | 漂亮人妻洗澡被公强 日日躁 | 成人性做爰aaa片免费看 | 人人爽人人澡人人人妻 | 国产精品久久久久久亚洲影视内衣 | 少妇无套内谢久久久久 | 噜噜噜亚洲色成人网站 | 欧美成人免费全部网站 | 国产乡下妇女做爰 | 中文字幕人妻丝袜二区 | 国产精品亚洲lv粉色 | 亚洲综合伊人久久大杳蕉 | 377p欧洲日本亚洲大胆 | 在线欧美精品一区二区三区 | 正在播放老肥熟妇露脸 | 无码播放一区二区三区 | 精品国产av色一区二区深夜久久 | 国产人妻人伦精品 | 国产后入清纯学生妹 | 欧美老人巨大xxxx做受 | 思思久久99热只有频精品66 | 久久国语露脸国产精品电影 | 国产无遮挡又黄又爽免费视频 | 欧洲美熟女乱又伦 | 中文字幕无码视频专区 | 国产精品无码一区二区桃花视频 | 亚洲国产精品久久久天堂 | 国产精品国产自线拍免费软件 | 亚洲区小说区激情区图片区 | 精品aⅴ一区二区三区 | 亚洲欧洲中文日韩av乱码 | 国产亚洲精品久久久ai换 | v一区无码内射国产 | 天天拍夜夜添久久精品 | 爽爽影院免费观看 | 扒开双腿疯狂进出爽爽爽视频 | 亚洲 a v无 码免 费 成 人 a v | 国产精品高潮呻吟av久久 | 午夜精品久久久内射近拍高清 | 精品少妇爆乳无码av无码专区 | 色婷婷综合中文久久一本 | 国产av无码专区亚洲a∨毛片 | 999久久久国产精品消防器材 | 国产亚洲美女精品久久久2020 | 少妇无套内谢久久久久 | 国精品人妻无码一区二区三区蜜柚 | 天天摸天天碰天天添 | 国产午夜福利亚洲第一 | 国产一区二区不卡老阿姨 | 国产真实伦对白全集 | 2020久久超碰国产精品最新 | 色婷婷香蕉在线一区二区 | 国精品人妻无码一区二区三区蜜柚 | 久久久久久久人妻无码中文字幕爆 | 无套内谢的新婚少妇国语播放 | 网友自拍区视频精品 | 青春草在线视频免费观看 | 丰满少妇高潮惨叫视频 | 亚洲日本va中文字幕 | 伊人久久婷婷五月综合97色 | 少妇厨房愉情理9仑片视频 | 国精产品一品二品国精品69xx | 午夜福利试看120秒体验区 | 噜噜噜亚洲色成人网站 | 亚洲欧美综合区丁香五月小说 | 国产成人无码午夜视频在线观看 | 中文字幕色婷婷在线视频 | 国产 浪潮av性色四虎 | 国产精品亚洲专区无码不卡 | 国内精品人妻无码久久久影院蜜桃 | 欧美日韩一区二区三区自拍 | 精品一区二区三区无码免费视频 | 免费人成网站视频在线观看 | 亚洲中文字幕在线观看 | 人妻中文无码久热丝袜 | 婷婷丁香六月激情综合啪 | 国产精品无码久久av | 欧美丰满熟妇xxxx | 玩弄中年熟妇正在播放 | 在线播放免费人成毛片乱码 | 男人扒开女人内裤强吻桶进去 | 2019nv天堂香蕉在线观看 | 国产在线aaa片一区二区99 | 激情国产av做激情国产爱 | 国产av剧情md精品麻豆 | 色窝窝无码一区二区三区色欲 | 亚洲日本va午夜在线电影 | 麻豆国产人妻欲求不满谁演的 | 久青草影院在线观看国产 | 天堂无码人妻精品一区二区三区 | 日韩欧美成人免费观看 | 人人妻人人藻人人爽欧美一区 | 麻豆果冻传媒2021精品传媒一区下载 | 亚洲gv猛男gv无码男同 | 99久久99久久免费精品蜜桃 | 7777奇米四色成人眼影 | 久久久久久久女国产乱让韩 | 夜夜夜高潮夜夜爽夜夜爰爰 | 偷窥日本少妇撒尿chinese | 亚洲色大成网站www | 午夜肉伦伦影院 | 麻花豆传媒剧国产免费mv在线 | 无码人妻丰满熟妇区毛片18 | 亚洲综合色区中文字幕 | 日韩人妻少妇一区二区三区 | 欧美三级a做爰在线观看 | 少妇被粗大的猛进出69影院 | 成人综合网亚洲伊人 | 天天爽夜夜爽夜夜爽 | 国产真实伦对白全集 | 国产免费久久久久久无码 | 老太婆性杂交欧美肥老太 | 玩弄人妻少妇500系列视频 | 黑人玩弄人妻中文在线 | 女人和拘做爰正片视频 | 狠狠色欧美亚洲狠狠色www | 国产精品亚洲五月天高清 | 久久久久久久久888 | 日本丰满熟妇videos | 国产色视频一区二区三区 | 欧洲欧美人成视频在线 | 亚洲成a人片在线观看无码3d | 高清无码午夜福利视频 | 熟妇激情内射com | 日韩 欧美 动漫 国产 制服 | 99精品无人区乱码1区2区3区 | 乱码av麻豆丝袜熟女系列 | 国精产品一品二品国精品69xx | 丰满人妻一区二区三区免费视频 | 中文精品无码中文字幕无码专区 | 妺妺窝人体色www婷婷 | 中文字幕久久久久人妻 | 亚洲无人区午夜福利码高清完整版 | 欧洲欧美人成视频在线 | 国产精华av午夜在线观看 | 粉嫩少妇内射浓精videos | 九月婷婷人人澡人人添人人爽 | 久久五月精品中文字幕 | 久久无码中文字幕免费影院蜜桃 | 国产性生交xxxxx无码 | 国产精品美女久久久 | 国产成人一区二区三区别 | 精品乱码久久久久久久 | 久久久精品成人免费观看 | 国产精品国产三级国产专播 | 国产 浪潮av性色四虎 | 婷婷五月综合激情中文字幕 | 少妇无套内谢久久久久 | 18禁黄网站男男禁片免费观看 | 麻豆精产国品 | 国产精品视频免费播放 | 亚洲欧美日韩国产精品一区二区 | 国产超碰人人爽人人做人人添 | 天堂无码人妻精品一区二区三区 | 午夜精品久久久久久久久 | 亚洲精品午夜无码电影网 | 国内揄拍国内精品少妇国语 | 久精品国产欧美亚洲色aⅴ大片 | 久久人妻内射无码一区三区 | 一区二区三区乱码在线 | 欧洲 | 亚洲国产精品久久久久久 | 精品 日韩 国产 欧美 视频 | 玩弄人妻少妇500系列视频 | 天堂无码人妻精品一区二区三区 | 女人和拘做爰正片视频 | 又色又爽又黄的美女裸体网站 | 中文字幕无线码 | 国产无遮挡又黄又爽免费视频 | 性色av无码免费一区二区三区 | 国产亚洲视频中文字幕97精品 | 少女韩国电视剧在线观看完整 | 国产人成高清在线视频99最全资源 | 18无码粉嫩小泬无套在线观看 | 四十如虎的丰满熟妇啪啪 | 久久久久99精品国产片 | 国产成人精品三级麻豆 | a在线亚洲男人的天堂 | 亚洲精品综合一区二区三区在线 | 精品熟女少妇av免费观看 | 亚洲日韩中文字幕在线播放 | 人妻少妇被猛烈进入中文字幕 | 亚洲色大成网站www | 国产极品视觉盛宴 | 国产色xx群视频射精 | 亚洲七七久久桃花影院 | 亚洲人成网站色7799 | 亚洲精品久久久久久一区二区 | 久久人人97超碰a片精品 | 老司机亚洲精品影院无码 | 亚洲人成人无码网www国产 | 亚洲精品成人福利网站 | 国产成人午夜福利在线播放 | 国产69精品久久久久app下载 | 色综合久久久久综合一本到桃花网 | 国产成人综合色在线观看网站 | 中文字幕av无码一区二区三区电影 | 国产乱人伦偷精品视频 | 性欧美疯狂xxxxbbbb | 国产精品二区一区二区aⅴ污介绍 | 亚洲日韩一区二区三区 | 乱码av麻豆丝袜熟女系列 | 色欲久久久天天天综合网精品 | 又大又硬又黄的免费视频 | 福利一区二区三区视频在线观看 | 六月丁香婷婷色狠狠久久 | 成人女人看片免费视频放人 | 激情人妻另类人妻伦 | av无码电影一区二区三区 | 亚洲国产欧美日韩精品一区二区三区 | 西西人体www44rt大胆高清 | 国产极品视觉盛宴 | 免费无码的av片在线观看 | 日韩欧美中文字幕公布 | 国产精品人妻一区二区三区四 | 亚洲自偷自拍另类第1页 | 欧美35页视频在线观看 | 欧美激情内射喷水高潮 | 久久久久99精品国产片 | 国产精品资源一区二区 | 青青青爽视频在线观看 | 中文字幕日韩精品一区二区三区 | 欧美自拍另类欧美综合图片区 | 欧美丰满少妇xxxx性 | 国产av一区二区三区最新精品 | 亚洲色偷偷男人的天堂 | 性色欲情网站iwww九文堂 | 中文字幕人妻丝袜二区 | 久久久久成人片免费观看蜜芽 | 欧美刺激性大交 | 又大又硬又黄的免费视频 | 中文字幕av日韩精品一区二区 | 亚洲国产精品久久人人爱 | 亚洲啪av永久无码精品放毛片 | 熟妇人妻无乱码中文字幕 | 亚洲一区av无码专区在线观看 | 免费观看的无遮挡av | 又紧又大又爽精品一区二区 | 欧美日韩一区二区免费视频 | 人人爽人人澡人人高潮 | 国产亚洲美女精品久久久2020 | 亚洲精品午夜无码电影网 | 日本熟妇乱子伦xxxx | 一本色道久久综合狠狠躁 | 久久国产精品偷任你爽任你 | 亚洲毛片av日韩av无码 | 免费观看又污又黄的网站 | 精品人妻中文字幕有码在线 | 免费观看黄网站 | 大屁股大乳丰满人妻 | 中文字幕av日韩精品一区二区 | 水蜜桃av无码 | 熟女体下毛毛黑森林 | 免费无码av一区二区 | 国产精品久久久久久亚洲影视内衣 | 欧美日韩在线亚洲综合国产人 | 亚洲无人区午夜福利码高清完整版 | 亚洲成色在线综合网站 | 强开小婷嫩苞又嫩又紧视频 | 夜先锋av资源网站 | 国产精品亚洲lv粉色 | 午夜嘿嘿嘿影院 | 人妻插b视频一区二区三区 | 亚洲の无码国产の无码步美 | 欧美人与牲动交xxxx | 天天摸天天透天天添 | 亚洲精品一区二区三区四区五区 | 我要看www免费看插插视频 | www一区二区www免费 | 亚洲日韩av一区二区三区四区 | 99久久精品国产一区二区蜜芽 | 亚洲国产一区二区三区在线观看 | 国产免费观看黄av片 | 国精产品一品二品国精品69xx | 成熟人妻av无码专区 | 丝袜人妻一区二区三区 | 乱人伦人妻中文字幕无码 | 久久99精品国产.久久久久 | 中文久久乱码一区二区 | 青青草原综合久久大伊人精品 | 亚洲中文字幕久久无码 | 国产农村妇女高潮大叫 | 欧美怡红院免费全部视频 | 亚洲七七久久桃花影院 | 欧美精品国产综合久久 | 国产精品久久国产精品99 | 内射巨臀欧美在线视频 | 欧美xxxx黑人又粗又长 | 久久久久成人精品免费播放动漫 | 日韩成人一区二区三区在线观看 | 76少妇精品导航 | 久久久国产一区二区三区 | 亚洲欧美综合区丁香五月小说 | 国产av人人夜夜澡人人爽麻豆 | 日本精品久久久久中文字幕 | 夜精品a片一区二区三区无码白浆 | 成人精品一区二区三区中文字幕 | 妺妺窝人体色www婷婷 | 天天躁日日躁狠狠躁免费麻豆 | 国内少妇偷人精品视频 | 无码福利日韩神码福利片 | 日日碰狠狠躁久久躁蜜桃 | 亚洲一区二区观看播放 | 黑人玩弄人妻中文在线 | 亚洲综合色区中文字幕 | 丰满少妇女裸体bbw | 日韩精品久久久肉伦网站 | 少妇无套内谢久久久久 | 午夜精品一区二区三区的区别 | 亚洲精品成人av在线 | 国产精品亚洲lv粉色 | 国产9 9在线 | 中文 | 国产人妻人伦精品1国产丝袜 | 男女下面进入的视频免费午夜 | 福利一区二区三区视频在线观看 | 国产电影无码午夜在线播放 | 精品无人区无码乱码毛片国产 | 麻花豆传媒剧国产免费mv在线 | 亚洲国产成人av在线观看 | 亚洲乱亚洲乱妇50p | 日韩人妻无码中文字幕视频 | 久久婷婷五月综合色国产香蕉 | 伊人久久大香线蕉亚洲 | 少妇性俱乐部纵欲狂欢电影 | 成年美女黄网站色大免费视频 | 国产成人综合在线女婷五月99播放 | 中国女人内谢69xxxxxa片 | 国内精品久久毛片一区二区 | 亚洲欧美精品伊人久久 | 青青青手机频在线观看 | 国产无套粉嫩白浆在线 | 国内精品久久久久久中文字幕 | 国产超级va在线观看视频 | 1000部夫妻午夜免费 | 天干天干啦夜天干天2017 | 久久亚洲中文字幕精品一区 | 国产成人综合色在线观看网站 | 老子影院午夜精品无码 | 精品无码一区二区三区爱欲 | 亚洲欧美色中文字幕在线 | 久久精品国产精品国产精品污 | 一个人看的视频www在线 | 久久久久久久久888 | 粉嫩少妇内射浓精videos | 成人精品视频一区二区三区尤物 | 天天摸天天碰天天添 | 在线成人www免费观看视频 | 国产小呦泬泬99精品 | 秋霞成人午夜鲁丝一区二区三区 | 国产成人无码av一区二区 | 国产九九九九九九九a片 | av在线亚洲欧洲日产一区二区 | 亚洲人成网站在线播放942 | 国产成人精品优优av | 亚洲伊人久久精品影院 | 久久精品视频在线看15 | 成人免费视频视频在线观看 免费 | 国産精品久久久久久久 | 东京热一精品无码av | 亚洲欧美中文字幕5发布 | 女人和拘做爰正片视频 | 国产欧美精品一区二区三区 | 亚洲国产精品无码一区二区三区 | 精品久久久久久人妻无码中文字幕 | 欧美freesex黑人又粗又大 | 久久精品人妻少妇一区二区三区 | 牲欲强的熟妇农村老妇女 | 国产午夜无码视频在线观看 | 双乳奶水饱满少妇呻吟 | 欧美性猛交xxxx富婆 | 亚洲人成影院在线观看 | 亚洲无人区一区二区三区 | 婷婷六月久久综合丁香 | 欧美成人免费全部网站 | 久久99精品国产麻豆蜜芽 | 国产欧美熟妇另类久久久 | 欧美性生交活xxxxxdddd | 亚洲另类伦春色综合小说 | 美女极度色诱视频国产 | 久久 国产 尿 小便 嘘嘘 | 大乳丰满人妻中文字幕日本 | 婷婷六月久久综合丁香 | 丰满少妇熟乱xxxxx视频 | 少妇无套内谢久久久久 | 国产精品18久久久久久麻辣 | 精品亚洲韩国一区二区三区 | 色一情一乱一伦一区二区三欧美 | 少妇的肉体aa片免费 | 日韩亚洲欧美中文高清在线 | 亚洲成在人网站无码天堂 | 国产精品沙发午睡系列 | 亚洲精品一区二区三区在线 | 日韩精品a片一区二区三区妖精 | 亚洲欧美日韩国产精品一区二区 | 99麻豆久久久国产精品免费 | 亚洲国产av精品一区二区蜜芽 | 东京热无码av男人的天堂 | 欧洲vodafone精品性 | 久久五月精品中文字幕 | 久久人人爽人人爽人人片av高清 | 亚洲一区二区三区四区 | 国产xxx69麻豆国语对白 | 亚洲综合伊人久久大杳蕉 | 欧美xxxx黑人又粗又长 | 少妇被黑人到高潮喷出白浆 | 色综合视频一区二区三区 | 久久久久免费精品国产 | 97精品国产97久久久久久免费 | 好屌草这里只有精品 | 国产人妻精品一区二区三区不卡 | 午夜无码人妻av大片色欲 | 最近的中文字幕在线看视频 | 一区二区三区高清视频一 | 欧美精品免费观看二区 | 久久综合激激的五月天 | 中文字幕无码av激情不卡 | 丰满人妻一区二区三区免费视频 | 欧美野外疯狂做受xxxx高潮 | 欧美日韩色另类综合 | 亚洲男人av香蕉爽爽爽爽 | 成人精品视频一区二区 | 又大又紧又粉嫩18p少妇 | 国产亚洲精品久久久闺蜜 | 夜夜躁日日躁狠狠久久av | 日本www一道久久久免费榴莲 | 丁香花在线影院观看在线播放 | 欧美激情内射喷水高潮 | 男人和女人高潮免费网站 | 漂亮人妻洗澡被公强 日日躁 | 人妻少妇精品无码专区二区 | 婷婷色婷婷开心五月四房播播 | 国产人妻人伦精品1国产丝袜 | 亚洲精品欧美二区三区中文字幕 | 麻豆国产人妻欲求不满 | 色婷婷av一区二区三区之红樱桃 | 俺去俺来也在线www色官网 | 精品一区二区三区波多野结衣 | 中文字幕无码免费久久9一区9 | 熟妇人妻激情偷爽文 | 欧美丰满熟妇xxxx性ppx人交 | 2020久久超碰国产精品最新 | 亚洲综合在线一区二区三区 | 免费国产成人高清在线观看网站 | 无码毛片视频一区二区本码 | 国产肉丝袜在线观看 | 狠狠噜狠狠狠狠丁香五月 | 亚洲码国产精品高潮在线 | 久久国产自偷自偷免费一区调 | 久久成人a毛片免费观看网站 | 国产乱人无码伦av在线a | 日产国产精品亚洲系列 | 国产一区二区三区四区五区加勒比 | 国产精品无码成人午夜电影 | 亚洲人成无码网www | 巨爆乳无码视频在线观看 | 红桃av一区二区三区在线无码av | 国产精品人人爽人人做我的可爱 | 熟女俱乐部五十路六十路av | 人人澡人摸人人添 | 精品欧美一区二区三区久久久 | 亚洲中文字幕无码中文字在线 | 伊人久久婷婷五月综合97色 | 久久国产精品_国产精品 | 波多野结衣av在线观看 | 强奷人妻日本中文字幕 | 精品成在人线av无码免费看 | 国产午夜手机精彩视频 | 欧美日韩久久久精品a片 | 国产偷自视频区视频 | 波多野结衣av在线观看 | 久久久精品成人免费观看 | 妺妺窝人体色www婷婷 | 国产三级精品三级男人的天堂 | 97久久精品无码一区二区 | 精品午夜福利在线观看 | 自拍偷自拍亚洲精品10p | 最近免费中文字幕中文高清百度 | 亚洲成av人片在线观看无码不卡 | 亚洲gv猛男gv无码男同 | 99久久久无码国产精品免费 | 欧美午夜特黄aaaaaa片 | 国内精品久久久久久中文字幕 | 97夜夜澡人人爽人人喊中国片 | 荫蒂添的好舒服视频囗交 | 久在线观看福利视频 | 亚洲中文字幕无码一久久区 | 亚洲国产一区二区三区在线观看 | 大乳丰满人妻中文字幕日本 | 图片小说视频一区二区 | 亚无码乱人伦一区二区 | 亚洲精品无码人妻无码 | 小鲜肉自慰网站xnxx | 亚洲综合无码一区二区三区 | 最近的中文字幕在线看视频 | 国产精品对白交换视频 | 女人高潮内射99精品 | 国产在线精品一区二区高清不卡 | 18无码粉嫩小泬无套在线观看 | 国产av一区二区精品久久凹凸 | 国产国语老龄妇女a片 | 国产精品成人av在线观看 | 伊人久久大香线蕉午夜 | 国产精品久久久久无码av色戒 | 少妇性俱乐部纵欲狂欢电影 | 99麻豆久久久国产精品免费 | 性啪啪chinese东北女人 | 久久亚洲中文字幕无码 | 国产精品高潮呻吟av久久 | 欧美35页视频在线观看 | 少妇无码av无码专区在线观看 | 国产精品无码永久免费888 | 樱花草在线社区www | 久久精品人人做人人综合试看 | 自拍偷自拍亚洲精品10p | 久久天天躁狠狠躁夜夜免费观看 | 色五月丁香五月综合五月 | 一本精品99久久精品77 | 欧美丰满老熟妇xxxxx性 | 国产在线精品一区二区高清不卡 | 国产无遮挡又黄又爽又色 | 97精品国产97久久久久久免费 | 亚洲伊人久久精品影院 | 久久亚洲中文字幕无码 | 亚洲国产精品成人久久蜜臀 | 无码人妻av免费一区二区三区 | 色婷婷久久一区二区三区麻豆 | 搡女人真爽免费视频大全 | 亚洲精品无码国产 | 国产三级精品三级男人的天堂 | 日韩av无码一区二区三区 | 性欧美熟妇videofreesex | 黑森林福利视频导航 | 欧美性黑人极品hd | 激情五月综合色婷婷一区二区 | 亚洲男人av香蕉爽爽爽爽 | 少妇被黑人到高潮喷出白浆 | 又色又爽又黄的美女裸体网站 | 免费人成网站视频在线观看 | 国产一区二区三区四区五区加勒比 | 国产性生大片免费观看性 | 99国产精品白浆在线观看免费 | 兔费看少妇性l交大片免费 | 精品国产一区二区三区四区在线看 | 人人妻人人澡人人爽欧美一区 | 对白脏话肉麻粗话av | 熟妇人妻激情偷爽文 | 国产精品18久久久久久麻辣 | 思思久久99热只有频精品66 | 天天燥日日燥 | 免费中文字幕日韩欧美 | 一个人免费观看的www视频 | 18精品久久久无码午夜福利 | 老熟妇乱子伦牲交视频 | 中文字幕乱码人妻二区三区 | 国产亚洲精品精品国产亚洲综合 | 少妇无套内谢久久久久 | 熟女少妇人妻中文字幕 | 51国偷自产一区二区三区 | 一个人免费观看的www视频 | 国产偷国产偷精品高清尤物 | 欧洲vodafone精品性 | 国产av一区二区精品久久凹凸 | 国产熟妇高潮叫床视频播放 | 欧美肥老太牲交大战 | 国产一区二区三区影院 | 无码人妻久久一区二区三区不卡 | 人妻与老人中文字幕 | 精品人妻人人做人人爽 | 欧美亚洲日韩国产人成在线播放 | 天堂а√在线地址中文在线 | 久久久成人毛片无码 | 欧美精品一区二区精品久久 | 99精品视频在线观看免费 | 97久久国产亚洲精品超碰热 | 日产精品高潮呻吟av久久 | 蜜桃av蜜臀av色欲av麻 999久久久国产精品消防器材 | 免费观看的无遮挡av | 亚洲中文字幕乱码av波多ji | 国产精品国产三级国产专播 | 中文字幕av无码一区二区三区电影 | 性啪啪chinese东北女人 | www国产亚洲精品久久久日本 | 日本一本二本三区免费 | 国内精品九九久久久精品 | 日本精品少妇一区二区三区 | 清纯唯美经典一区二区 | 国产区女主播在线观看 | 亚洲 a v无 码免 费 成 人 a v | 人人妻人人澡人人爽精品欧美 | 欧美三级a做爰在线观看 | 国产真实夫妇视频 | 日本xxxx色视频在线观看免费 | 国产乱码精品一品二品 | 中国大陆精品视频xxxx | 久久人妻内射无码一区三区 | 99久久精品日本一区二区免费 | 日韩精品无码一区二区中文字幕 | 青青青爽视频在线观看 | 露脸叫床粗话东北少妇 | 狠狠躁日日躁夜夜躁2020 | 久久精品国产99久久6动漫 | 在线观看免费人成视频 | 国产莉萝无码av在线播放 | 国产国产精品人在线视 | 3d动漫精品啪啪一区二区中 | 强奷人妻日本中文字幕 | 国内少妇偷人精品视频 | 亚洲成av人综合在线观看 | 天堂亚洲2017在线观看 | 丰满岳乱妇在线观看中字无码 | a片在线免费观看 | 久久亚洲精品中文字幕无男同 | 亚洲综合无码久久精品综合 | 天干天干啦夜天干天2017 | 夫妻免费无码v看片 | 国产成人人人97超碰超爽8 | 亚洲色www成人永久网址 | 又紧又大又爽精品一区二区 | 色婷婷香蕉在线一区二区 | 久久久亚洲欧洲日产国码αv | 小sao货水好多真紧h无码视频 | 久久国产精品偷任你爽任你 | 国产成人精品三级麻豆 | 少女韩国电视剧在线观看完整 | 99久久人妻精品免费二区 | 狠狠亚洲超碰狼人久久 | 国产精品.xx视频.xxtv | 免费男性肉肉影院 | 爆乳一区二区三区无码 | 国产精品va在线观看无码 | 国产麻豆精品一区二区三区v视界 | 亚洲一区二区三区偷拍女厕 | 国产又爽又黄又刺激的视频 | 亚洲人成无码网www | 澳门永久av免费网站 | 99久久精品国产一区二区蜜芽 | 中文字幕色婷婷在线视频 | 亚洲精品美女久久久久久久 | 2020最新国产自产精品 | 无码精品国产va在线观看dvd | 国产精品内射视频免费 | 亚洲精品一区二区三区大桥未久 | 国产人妻大战黑人第1集 | 97夜夜澡人人爽人人喊中国片 | 2019午夜福利不卡片在线 | 大乳丰满人妻中文字幕日本 | aⅴ亚洲 日韩 色 图网站 播放 | 亚洲成a人片在线观看日本 | 亚洲精品无码人妻无码 | 97久久精品无码一区二区 | 久久99精品国产麻豆蜜芽 | 少女韩国电视剧在线观看完整 | 国内综合精品午夜久久资源 | 激情亚洲一区国产精品 | 欧美喷潮久久久xxxxx | 久久久无码中文字幕久... | 强开小婷嫩苞又嫩又紧视频 | 激情人妻另类人妻伦 | 福利一区二区三区视频在线观看 | 国产福利视频一区二区 | 99久久无码一区人妻 | 精品亚洲成av人在线观看 | 99久久婷婷国产综合精品青草免费 | 精品一区二区三区波多野结衣 | 日韩精品无码一本二本三本色 | 99久久久国产精品无码免费 | 波多野结衣 黑人 | 欧美精品无码一区二区三区 | 亚洲成av人片天堂网无码】 | 亚洲第一无码av无码专区 | 99久久精品无码一区二区毛片 | 任你躁在线精品免费 | 丝袜 中出 制服 人妻 美腿 | 中文字幕无码免费久久9一区9 | 人人澡人摸人人添 | 成人一在线视频日韩国产 | ass日本丰满熟妇pics | 又大又硬又爽免费视频 | 在线 国产 欧美 亚洲 天堂 | 国产精品va在线播放 | 中文亚洲成a人片在线观看 | 精品人妻av区 | 自拍偷自拍亚洲精品被多人伦好爽 | 无码人妻av免费一区二区三区 | 未满成年国产在线观看 | 国产乡下妇女做爰 | 国产精品久久国产精品99 | 久久国语露脸国产精品电影 | 亚洲熟妇色xxxxx亚洲 | 亚洲自偷精品视频自拍 | 日韩无套无码精品 | 午夜精品一区二区三区在线观看 | 国产免费无码一区二区视频 | 内射爽无广熟女亚洲 | 国产人妻久久精品二区三区老狼 | 亚洲精品美女久久久久久久 | 粗大的内捧猛烈进出视频 | 永久免费观看美女裸体的网站 | 女人被男人爽到呻吟的视频 | 国产免费久久精品国产传媒 | 天堂а√在线地址中文在线 | 中文字幕无码乱人伦 | 伊人久久大香线蕉av一区二区 | 少妇性l交大片欧洲热妇乱xxx | 精品少妇爆乳无码av无码专区 |