C語言Windows程序設計-> 第八天-> 滾動條


對上一天學習的回顧:

  1>.  TextOut函數的使用

    TextOut函數的作用是使用系統當前選擇的字體、背景顏色以及正文顏色將一個字符串輸出到指定位置, 函數原型:

  BOOL TextOut(
      HDC hdc,                 //設備環境句柄
      int nXStart,             //字符串開始輸出的x坐標
      int nYStart,             // 字符串開始輸出的y坐標   
      LPCTSTR lpString,        //需要輸出的字符串
      int cbString             // 字符串的長度
  );

    當函數調用成功時返回一個非零的值, 調用失敗時, 返回值為0。

 

  2>. 取得當前系統字體信息:

    使用GetTextMetrics函數可以取得當前字體信息, 函數原型如下:

  BOOL GetTextMetrics(
    HDC hdc,                   // 設備環境句柄
    LPTEXTMETRIC lptm       // 指向一個TEXTMETRIC結構的指針, 該結構用於存放字體信息。 
  );

    參數二LPTEXTMETRIC指向TEXTMETRIC結構, 在函數調用成功時, 函數將系統當前字體的各種信息復制到TEXTMETRIC結構中。

 

 

Windows滾動條介紹

  滾動條由滾動滑塊以及兩端的滾動箭頭組成, 滾動條的作用是當需要顯示的內容超過窗口客戶區大小時提供上下/左右的翻頁使用戶能夠完整的閱讀顯示信息, 滾動條的圖示:

 

 

滾動條理論基礎

  1>. 上下滾動?

    以垂直方向的滾動條為例, 當用戶向下滾動滾動條時目的是想看到下方更多的的信息, 因此我們需要將下方的信息顯示出來, 如何顯示更多的信息?

    解決方案: 將不需要被顯示的信息顯示到客戶區外, 令信息自動被Windows截掉, 圖示說明:

 

    由圖示看出, 當用戶向下翻動滾動條實際上我們是將起始輸出部分的y坐標設為負數, 使已經顯示過的信息輸出到客戶區的上部, 我們知道, 輸出到客戶區外部的信息會被Windows自動截掉, 所以用戶不會再次看到已經顯示過的信息, 取而代之的就是下方等待顯示的信息, 上翻以及左右翻動的顯示思路與下翻相同, 不再介紹。

 

  2>. 如何創建一個帶有滾動條的窗口?

     創建帶有水平/垂直的滾動條的窗口十分簡單, 在CreateWindow函數中說明下即可, CreateWindow函數的原型回顧:

  HWND CreateWindow(
    LPCTSTR lpClassName,               //窗口類名稱
    LPCTSTR lpWindowName,              //窗口標題
    DWORD dwStyle,                     //窗口樣式
    int x,                             //窗口初始x坐標
    int y,                             //窗口初始y坐標
    int nWidth,                        //窗口初始x方向尺寸
    int nHeight,                       //窗口初始y方向尺寸
    HWND hWndParent,                   //父窗口句柄
    HMENU hMenu,                       //窗口菜單句柄
    HANDLE hlnstance,                  //程序實例句柄
    LPVOID lpParam                     //創建參數
  );

     要窗口帶有滾動條的窗口, 只需要在第三個參數

   DWORD dwStyle,                     //窗口樣式

      也就是在窗口樣式的屬性中使用位或( | )運算對相關的標識符進行組合即可得到一個帶有垂直/水平滾動條的窗口,

        WS_HSCROLL    //水平滾動條的標識符
        WS_VSCROLL    //垂直滾動條的標識符                

     例如要創建一個既含有垂直滾動條又含有水平滾動條的組合:

    WS_OVERLAPPEDWINDOW | WS_VSCROLL | WS_HSCROLL

 

  3>. 對於滾動條, Windows需要做哪些事?

    當帶有滾動條的窗口創建好后, Windows就會做一些必要的處理來協助我們更好的使用滾動條, Windows需要做的事情如下:

      1>. 處理滾動條中的所有鼠標消息;

      2>. 當用戶單擊滾動條時提供被單擊部分的輕微閃爍;

      3>. 當用戶拖動滑塊時在滾動條內移動滑塊;

      4>. 當窗口大小被調整時, 自動調整滾動條的長度;

      5>. 向滾動條所在的窗口發送滾動條的相關消息。

 

 

  4>. 我們要做的事情:

    相對於系統我們需要做的事情已經較為輕松了主要有4項任務:

      1>. 初始化滾動條的位置和滾動條的范圍;

      2>. 處理系統發來的消息;

      3>. 根據發來的消息重置滑塊的位置;

      4>. 根據滾動條消息重繪客戶區顯示的內容。

 

  5>. 將會收到哪些滾動條消息?

    滾動條消息來源同其他消息一樣, 伴隨着wParam與lParam消息機制, 當窗口為父窗口時消息的來源為wParam, 此時可忽略lParam的值, lParam用於子窗口消息。

    wParam參數分為兩部分, 高位字與低位字, 其中高位字代表用戶松開鼠標鍵時滑塊的最終位置, 低位字上代表鼠標在滾動條上的動作, 以一個值的形式表現出來, 同樣, 為了方便記憶, 有不同的標識符對這些值進行區分, 這些標識符定義在WINUSER.H頭文件中, 以SB_開頭, 有關滾動條的消息標識符如下:

#define SB_LINEUP           0        //上翻一個單位
#define SB_LINELEFT         0        //左翻一個單位
#define SB_LINEDOWN         1        //下翻一個單位
#define SB_LINERIGHT        1        //右翻一個單位
#define SB_PAGEUP           2        //上翻一頁
#define SB_PAGELEFT         2        //左翻一頁
#define SB_PAGEDOWN         3        //下翻一頁
#define SB_PAGERIGHT        3        //右翻一頁
#define SB_THUMBPOSITION    4        //當鼠標放下滑塊時
#define SB_THUMBTRACK       5        //移動滑塊時
#define SB_TOP              6        //滑塊到了頂端
#define SB_LEFT             6        //滑塊到了左端
#define SB_BOTTOM           7        //滑塊到了底端
#define SB_RIGHT            7        //滑塊到了右端
#define SB_ENDSCROLL        8        //釋放鼠標

 

  6>.  需要使用到的新函數:

    ①. SetScrollRange

      SetScrollRange函數的作用是設置所指定滾動條范圍的最小值和最大值, 其函數的原型如下:

  BOOL SetScrollRange(
    HWND hWnd,            //窗口句柄
    int nBar,              //被設置的滾動條類型
    int nMinPos,           //滾動條的最小位置
    int nMaxPos,           //滾動條的最大位置
    BOOL bRedraw          //重繪標志
  );

    參數二int nBar為被設置的滾動條類型, SB_HORZ表示該窗口的水平滾動條, SB_VERT表示垂直滾動條;

    參數四BOOL bRedraw指定滾動條是否被重繪以反映變化, 當參數為TRUE, 滾動條被重繪, FALSE則不被重繪。

 

    ②. SetScrollPos

      SetScrollPos函數的作用是設置所指定滾動條中的滾動按鈕的位置, 函數原型:

  int SetScrollPos(
    HWND hWnd,     //窗體句柄
    int nBar,        //被設置的滾動條類型
    int nPos,           //滾動條的新位置
    BOOL bRedraw      //重繪標志
  );

 

 

 

實戰滾動條

  下面我們嘗試着輸出一些文字, 使其上下、左右均超過客戶區的尺寸, 這樣我們就可以實際練習下水平滾動條以及垂直滾動條了,  我們准備了很多行文字, 筆者也不知道到底有多少行, 而且最長的那行文字有多少個也不知道, 我們把他放在一個text.h頭文件中, 並計算他到底有多少行以及最長的那行有多少字, 由於文字行數較多, 這里將它在代碼框里折疊顯示, 定義的頭文件如下:

View Code - text.h
#include<string.h>

#define NUMLINES ( (int)(sizeof(statement) / sizeof(statement[0]) ) )        //計算總行數

TCHAR *statement[] = {
    TEXT("沒有一種不通過蔑視、忍受和奮斗就可以征服的命運。"),
    TEXT("偉人之所以偉大,是因為他與別人共處逆境時,別人失去了信心,他卻下決心實現自己的目標。"),
    TEXT("世上沒有絕望的處境,只有對處境絕望的人。"),
    TEXT("當你感到悲哀痛苦時,最好是去學些什么東西。學習會使你永遠立於不敗之地。"),
    TEXT("給自己定目標,一年,兩年,五年,也許你出生不如別人好,通過努力,往往可以改變70%的命運。破罐子破摔只能和懦弱做朋友。"),
    TEXT("知道自己要干什么,夜深人靜,問問自己,將來的打算,並朝着那個方向去實現。而不是無所事事和做一些無謂的事。"),
    TEXT("夢想無論怎么模糊,它總潛伏在我們心底,使我們的心境永遠得不到寧靜,直到夢想成為事實。"),
    TEXT("夢是一種欲望,想是一種行動。夢想是夢與想的結晶。"),
    TEXT("生氣,就是拿別人的過錯來懲罰自己。原諒別人,就是善待自己。"),
    TEXT("一個實現夢想的人,就是一個成功的人。"),
    TEXT("大事堅持原則,小事學會變通。"),
    TEXT("一個人有錢沒錢不一定,但如果這個人沒有了夢想,這個人窮定了。"),
    TEXT("不要急於表態不急於表態或發表意見,可以使人對你揣摸猜測。謹慎的沉默是精明人的幫手。一旦表態,你的決定就容易受到批評和非議。如果這些決定是以失敗告終,你就會更加倒霉。"),
    TEXT("平凡朴實的夢想,我們用那唯一的堅持信念去支撐那夢想。"),
    TEXT("不要總談論自己你若總是談論自己,那么不是吹噓自己,就是貶低自己;前者是虛榮的表現,后者是卑微的表現。"),
    TEXT("千萬不要抱怨,抱怨會使你丟丑。抱怨會使人對你傲慢無禮,並促使別人如你所抱怨的那么做。要贏得別人的幫助,最好的辦法是表揚他人。"),
    TEXT("凡事終了時務必小心謹慎,順利抽身退出要比順利地進入時更難。"),
    TEXT("很難說什么是辦不到的事情,因為昨天的夢想,可以是今天的希望,並且還可以成為明天的現實。"),
    TEXT("當你再也沒有什么可以失去的時候,就是你開始得到的時候。"),
    TEXT("生命猶如一片綠葉,隨着時間的流逝,慢慢變的枯黃,但他的葉脈還是那么清晰可見。"),
    TEXT("我們可以躲開大家,卻躲不開一只蒼蠅。生活中使我們不快樂的常是一些芝麻小事。"),
    TEXT("人生的意義不在於拿一手好牌,而在於打好一手壞牌。"),
    TEXT("一個人想平庸,阻攔者很少;一個人想出眾,阻攔者很多。不少平庸者與周圍人關系融洽,不少出眾者與周圍人關系緊張。"),
    TEXT("“危機”兩個字,一個意味着危險,另外一個意味着機會,不要放棄任何一次努力。"),
    TEXT("世上沒有絕望的處境,只有對處境絕望的人。"),
    TEXT("再長的路,一步步也能走完,再短的路,不邁開雙腳也無法到達。"),
    TEXT("有志者自有千計萬計,無志者只感千難萬難。"),
    TEXT("成功與不成功之間有時距離很短只要后者再向前幾步。"),
    TEXT("世界會向那些有目標和遠見的人讓路。"),
    TEXT("世界青睞有雄心壯志的人。成功所依靠的惟一條件就是思考。當你的思維以最高速度運轉時,樂觀歡快的情緒就會充斥全身。一個人最完美的作品都是在充滿愉快、樂觀、深情的狀態下完成的。學會讓自己安靜,把思維沉浸下來,慢慢降低對事物的欲望。把自我經常歸零,每天都是新的起點,沒有年齡的限制,只要你對事物的欲望適當的降低,會贏得更多的求勝機會。"),
    TEXT("學會讓自己安靜,把思維沉浸下來,慢慢降低對事物的欲望。把自我經常歸零,每天都是新的起點,沒有年齡的限制,只要你對事物的欲望適當的降低,會贏得更多的求勝機會。"),
    TEXT("楊瀾:年輕時候應該能夠作一些冒險。年輕時候最大的財富,不是你的青春,不是你的美貌,也不是你充沛的精力,而是你有犯錯誤的機會。"),
    TEXT("沒有十全十美的東西,沒有十全十美的人,關鍵是清楚到底想要什么。得到想要的,肯定會失去另外一部分。如果什么都想要,只會什么都得不到。"),
    TEXT("做自己的決定。然后准備好承擔后果。從一開始就提醒自己,世上沒有后悔葯吃。"),
    TEXT("在你內心深處,還有無窮的潛力,有一天當你回首看時,你就會知道這絕對是真的。"),
    TEXT("不為模糊不清的未來擔憂,只為清清楚楚的現在努力。"),
    TEXT("沒有一種不通過蔑視、忍受和奮斗就可以征服的命運。"),
    TEXT("偉人之所以偉大,是因為他與別人共處逆境時,別人失去了信心,他卻下決心實現自己的目標。"),
    TEXT("世上沒有絕望的處境,只有對處境絕望的人。"),
    TEXT("當你感到悲哀痛苦時,最好是去學些什么東西。學習會使你永遠立於不敗之地。"),
    TEXT("給自己定目標,一年,兩年,五年,也許你出生不如別人好,通過努力,往往可以改變70%的命運。破罐子破摔只能和懦弱做朋友。"),
    TEXT("知道自己要干什么,夜深人靜,問問自己,將來的打算,並朝着那個方向去實現。而不是無所事事和做一些無謂的事。"),
    TEXT("夢想無論怎么模糊,它總潛伏在我們心底,使我們的心境永遠得不到寧靜,直到夢想成為事實。"),
    TEXT("夢是一種欲望,想是一種行動。夢想是夢與想的結晶。"),
    TEXT("生氣,就是拿別人的過錯來懲罰自己。原諒別人,就是善待自己。生氣,就是拿別人的過錯來懲罰自己。原諒別人,就是善待自己。生氣,就是拿別人的過錯來懲罰自己。"),
    TEXT("一個實現夢想的人,就是一個成功的人。"),
    TEXT("大事堅持原則,小事學會變通。"),
    TEXT("一個人有錢沒錢不一定,但如果這個人沒有了夢想,這個人窮定了。"),
    TEXT("不要急於表態不急於表態或發表意見,可以使人對你揣摸猜測。謹慎的沉默是精明人的幫手。一旦表態,你的決定就容易受到批評和非議。"),
    TEXT("平凡朴實的夢想,我們用那唯一的堅持信念去支撐那夢想。"),
    TEXT("不要總談論自己你若總是談論自己,那么不是吹噓自己,就是貶低自己;前者是虛榮的表現,后者是卑微的表現。"),
    TEXT("千萬不要抱怨,抱怨會使你丟丑。抱怨會使人對你傲慢無禮,並促使別人如你所抱怨的那么做。要贏得別人的幫助,最好的辦法是表揚他人。"),
    TEXT("凡事終了時務必小心謹慎,順利抽身退出要比順利地進入時更難。"),
    TEXT("很難說什么是辦不到的事情,因為昨天的夢想,可以是今天的希望,並且還可以成為明天的現實。"),
    TEXT("當你再也沒有什么可以失去的時候,就是你開始得到的時候。"),
    TEXT("生命猶如一片綠葉,隨着時間的流逝,慢慢變的枯黃,但他的葉脈還是那么清晰可見。"),
    TEXT("我們可以躲開大家,卻躲不開一只蒼蠅。生活中使我們不快樂的常是一些芝麻小事。"),
    TEXT("人生的意義不在於拿一手好牌,而在於打好一手壞牌。"),
    TEXT("一個人想平庸,阻攔者很少;一個人想出眾,阻攔者很多。不少平庸者與周圍人關系融洽,不少出眾者與周圍人關系緊張。"),
    TEXT("“危機”兩個字,一個意味着危險,另外一個意味着機會,不要放棄任何一次努力。"),
    TEXT("世上沒有絕望的處境,只有對處境絕望的人。"),
    TEXT("再長的路,一步步也能走完,再短的路,不邁開雙腳也無法到達。"),
    TEXT("有志者自有千計萬計,無志者只感千難萬難。"),
    TEXT("成功與不成功之間有時距離很短只要后者再向前幾步。"),
    TEXT("世界會向那些有目標和遠見的人讓路。"),
    TEXT("世界青睞有雄心壯志的人。成功所依靠的惟一條件就是思考。當你的思維以最高速度運轉時,樂觀歡快的情緒就會充斥全身。一個人最完美的作品都是在充滿愉快、樂觀、深情的狀態下完成的。"),
    TEXT("學會讓自己安靜,把思維沉浸下來,慢慢降低對事物的欲望。把自我經常歸零,每天都是新的起點,沒有年齡的限制,只要你對事物的欲望適當的降低,會贏得更多的求勝機會。"),
    TEXT("楊瀾:年輕時候應該能夠作一些冒險。年輕時候最大的財富,不是你的青春,不是你的美貌,也不是你充沛的精力,而是你有犯錯誤的機會。"),
    TEXT("沒有十全十美的東西,沒有十全十美的人,關鍵是清楚到底想要什么。得到想要的,肯定會失去另外一部分。如果什么都想要,只會什么都得不到。"),
    TEXT("做自己的決定。然后准備好承擔后果。從一開始就提醒自己,世上沒有后悔葯吃。"),
    TEXT("在你內心深處,還有無窮的潛力,有一天當你回首看時,你就會知道這絕對是真的。"),
    TEXT("不為模糊不清的未來擔憂,只為清清楚楚的現在努力。"),
    TEXT("沒有一種不通過蔑視、忍受和奮斗就可以征服的命運。"),
    TEXT("偉人之所以偉大,是因為他與別人共處逆境時,別人失去了信心,他卻下決心實現自己的目標。"),
    TEXT("世上沒有絕望的處境,只有對處境絕望的人。"),
    TEXT("當你感到悲哀痛苦時,最好是去學些什么東西。學習會使你永遠立於不敗之地。"),
    TEXT("給自己定目標,一年,兩年,五年,也許你出生不如別人好,通過努力,往往可以改變70%的命運。破罐子破摔只能和懦弱做朋友。"),
    TEXT("知道自己要干什么,夜深人靜,問問自己,將來的打算,並朝着那個方向去實現。而不是無所事事和做一些無謂的事。"),
    TEXT("夢想無論怎么模糊,它總潛伏在我們心底,使我們的心境永遠得不到寧靜,直到夢想成為事實。"),
    TEXT("夢是一種欲望,想是一種行動。夢想是夢與想的結晶。"),
    TEXT("生氣,就是拿別人的過錯來懲罰自己。原諒別人,就是善待自己。"),
    TEXT("一個實現夢想的人,就是一個成功的人。"),
    TEXT("大事堅持原則,小事學會變通。"),
    TEXT("一個人有錢沒錢不一定,但如果這個人沒有了夢想,這個人窮定了。"),
    TEXT("不要急於表態不急於表態或發表意見,可以使人對你揣摸猜測。謹慎的沉默是精明人的幫手。一旦表態,你的決定就容易受到批評和非議。如果這些決定是以失敗告終,你就會更加倒霉。"),
    TEXT("平凡朴實的夢想,我們用那唯一的堅持信念去支撐那夢想。"),
    TEXT("不要總談論自己你若總是談論自己,那么不是吹噓自己,就是貶低自己;前者是虛榮的表現,后者是卑微的表現。"),
    TEXT("千萬不要抱怨,抱怨會使你丟丑。抱怨會使人對你傲慢無禮,並促使別人如你所抱怨的那么做。要贏得別人的幫助,最好的辦法是表揚他人。"),
    TEXT("凡事終了時務必小心謹慎,順利抽身退出要比順利地進入時更難。"),
    TEXT("很難說什么是辦不到的事情,因為昨天的夢想,可以是今天的希望,並且還可以成為明天的現實。"),
    TEXT("當你再也沒有什么可以失去的時候,就是你開始得到的時候。"),
    TEXT("生命猶如一片綠葉,隨着時間的流逝,慢慢變的枯黃,但他的葉脈還是那么清晰可見。"),
    TEXT("我們可以躲開大家,卻躲不開一只蒼蠅。生活中使我們不快樂的常是一些芝麻小事。"),
    TEXT("人生的意義不在於拿一手好牌,而在於打好一手壞牌。"),
    TEXT("一個人想平庸,阻攔者很少;一個人想出眾,阻攔者很多。不少平庸者與周圍人關系融洽,不少出眾者與周圍人關系緊張。"),
    TEXT("“危機”兩個字,一個意味着危險,另外一個意味着機會,不要放棄任何一次努力。"),
    TEXT("世上沒有絕望的處境,只有對處境絕望的人。"),
    TEXT("再長的路,一步步也能走完,再短的路,不邁開雙腳也無法到達。"),
    TEXT("有志者自有千計萬計,無志者只感千難萬難。"),
    TEXT("成功與不成功之間有時距離很短只要后者再向前幾步。"),
    TEXT("世界會向那些有目標和遠見的人讓路。"),
    TEXT("世界青睞有雄心壯志的人。成功所依靠的惟一條件就是思考。當你的思維以最高速度運轉時,樂觀歡快的情緒就會充斥全身。一個人最完美的作品都是在充滿愉快、樂觀、深情的狀態下完成的。"),
    TEXT("學會讓自己安靜,把思維沉浸下來,慢慢降低對事物的欲望。把自我經常歸零,每天都是新的起點,沒有年齡的限制,只要你對事物的欲望適當的降低,會贏得更多的求勝機會。"),
    TEXT("楊瀾:年輕時候應該能夠作一些冒險。年輕時候最大的財富,不是你的青春,不是你的美貌,也不是你充沛的精力,而是你有犯錯誤的機會。"),
    TEXT("沒有十全十美的東西,沒有十全十美的人,關鍵是清楚到底想要什么。得到想要的,肯定會失去另外一部分。如果什么都想要,只會什么都得不到。"),
    TEXT("做自己的決定。然后准備好承擔后果。從一開始就提醒自己,世上沒有后悔葯吃。"),
    TEXT("在你內心深處,還有無窮的潛力,有一天當你回首看時,你就會知道這絕對是真的。"),
    TEXT("不為模糊不清的未來擔憂,只為清清楚楚的現在努力。")
};

//計算statement所有句子中最長語句的長度
int GetMaxLength()
{
    /*
    *計算statement所有句子中最長語句的長度
    *返回值: int GetMaxLength(void) -> int
    */
    int maxLength = 0 ;
    int i ;
    for( i = 0; i < NUMLINES; i++ )
    {
        if( wcslen(statement[i]) > maxLength )
            maxLength = wcslen(statement[i]) ;
    }
    return maxLength ;
}

 

  在這個頭文件中, 其中有兩句是十分重要的, 一是計算總行數:

 #define NUMLINES ( (int)(sizeof(statement) / sizeof(statement[0]) ) )        //計算總行數

  另一個是計算最長串字符個數的函數GetMaxLength, 該函數的定義如下:

int GetMaxLength()
{
    /*
    *計算statement所有句子中最長語句的長度
    *返回值: int GetMaxLength(void) -> int
    */
    int maxLength = 0 ;
    int i ;
    for( i = 0; i < NUMLINES; i++ )
    {
        if( wcslen(statement[i]) > maxLength )
            maxLength = wcslen(statement[i]) ;
    }
    return maxLength ;
}

 

  下面編寫我們的源文件, LearnScroll.c, 先看一下完整的代碼, 稍后我們詳細解釋, 代碼如下:

 

  1 #include<windows.h>
  2 #include"text.h"
  3 
  4 LRESULT CALLBACK WndProc( HWND, UINT, WPARAM, LPARAM ) ;        //聲明窗口過程函數
  5 
  6 int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdLine, int iCmdShow )
  7 {
  8     static TCHAR szAppName[] = TEXT("LearnScroll") ;
  9     HWND hwnd ;
 10     MSG msg ;
 11     WNDCLASS wndclass ;
 12 
 13     //窗口類成員屬性
 14     wndclass.lpfnWndProc = WndProc ;
 15     wndclass.style = CS_HREDRAW | CS_VREDRAW ;
 16     wndclass.hInstance = hInstance ;
 17     wndclass.lpszClassName = szAppName ;
 18     wndclass.lpszMenuName = NULL ;
 19     wndclass.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH) ;
 20     wndclass.hCursor = LoadCursor(NULL, IDI_APPLICATION) ;
 21     wndclass.hIcon = LoadIcon(NULL, IDC_ARROW) ;
 22     wndclass.cbClsExtra = 0 ;
 23     wndclass.cbWndExtra = 0 ;
 24 
 25     //注冊窗口類
 26     if( !RegisterClass(&wndclass) )
 27     {
 28         MessageBox( NULL, TEXT("無法注冊窗口類!"), TEXT("錯誤"), MB_OK | MB_ICONERROR ) ;
 29         return 0 ;
 30     }
 31 
 32     //創建窗口
 33     hwnd = CreateWindow(
 34         szAppName, TEXT("滾動條示例"),
 35         WS_OVERLAPPEDWINDOW | WS_VSCROLL | WS_HSCROLL,
 36         CW_USEDEFAULT, CW_USEDEFAULT,
 37         CW_USEDEFAULT, CW_USEDEFAULT,
 38         NULL, NULL, hInstance, NULL
 39     ) ;
 40 
 41     //顯示窗口
 42     ShowWindow( hwnd, iCmdShow ) ;
 43     UpdateWindow( hwnd ) ;
 44 
 45     //獲取、翻譯、分發消息
 46     while( GetMessage( &msg, NULL, 0, 0 ) )
 47     {
 48         TranslateMessage( &msg ) ;
 49         DispatchMessage( &msg ) ;
 50     }
 51     
 52     return msg.wParam ;
 53 }
 54 
 55 LRESULT CALLBACK WndProc( HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam )
 56 {
 57     static int cxChar, cxCaps, cyChar, cyClient, cxClient, iVscrollPos, iHscrollPos ;
 58     //cxChar:平均字符寬度; cxCaps: 大寫字母平均寬度; cyChar: 字符高; cyClient、cxClient: 客戶區y、x方向尺寸; 
 59     //iVscrollPos: 豎直方向滾動條滑塊位置; iHscrollPos: 水平方向滾動條滑塊位置
 60 
 61     HDC hdc ;
 62     RECT rect ;            //記錄客戶區RECT結構
 63     int i, x, y;        //i循環控制, x記錄水平方向坐標, y豎直方向坐標
 64     PAINTSTRUCT ps ;
 65     TEXTMETRIC tm ;
 66 
 67     switch(message)
 68     {
 69     case WM_CREATE:        //處理WM_CREATE消息
 70         hdc = GetDC(hwnd) ;
 71         GetTextMetrics( hdc, &tm ) ;    //獲取系統字體信息
 72         cxChar = tm.tmAveCharWidth ;    //獲取平均寬度
 73         cxCaps = (tm.tmPitchAndFamily & 1 ? 3 : 2) * cxChar / 2 ;        //大寫字母平均寬度
 74         cyChar = tm.tmHeight + tm.tmExternalLeading ;                    //字符高度
 75         ReleaseDC( hwnd, hdc );
 76 
 77         SetScrollRange( hwnd, SB_VERT, 0, NUMLINES - 1, FALSE ) ;        //設置豎直滾動條范圍的最小值和最大值
 78         SetScrollRange( hwnd, SB_HORZ, 0, GetMaxLength() - 1, FALSE ) ;    //設置水平滾動條范圍的最小值和最大值
 79         SetScrollPos( hwnd, SB_VERT, iVscrollPos, TRUE ) ;        //設置豎直滾動條中的滾動按鈕的位置
 80         SetScrollPos( hwnd, SB_HORZ, iHscrollPos, TRUE ) ;        //設置水平定滾動條中的滾動按鈕的位置
 81 
 82         return 0 ;
 83 
 84     case WM_SIZE:        //處理WM_SIZE
 85         GetClientRect( hwnd, &rect ) ;
 86         cyClient = rect.bottom ;            //得到客戶區y方向尺寸
 87         cxClient = rect.right ;                //得到客戶區x方向尺寸
 88         return 0 ;
 89 
 90     case WM_VSCROLL:    //處理垂直滾動條消息
 91         switch( LOWORD(wParam) )
 92         {
 93         case SB_LINEUP:            //上翻一行
 94             iVscrollPos -= 1 ;
 95             break ;
 96 
 97         case SB_LINEDOWN:          //下翻一行
 98             iVscrollPos += 1 ;
 99             break ;
100 
101         case SB_PAGEUP:            //向上翻一整頁
102             iVscrollPos -= cyClient / cyChar ;
103             break ;
104 
105         case SB_PAGEDOWN:          //向下翻一整頁
106             iVscrollPos += cyClient / cyChar ;
107             break ;
108 
109         case SB_THUMBPOSITION:     //拖動滑塊滑塊被放下時
110             iVscrollPos = HIWORD(wParam) ;
111             break ;
112 
113         default:
114             break;
115         }
116         iVscrollPos = max( 0, min(iVscrollPos, NUMLINES -1) ) ;
117         if( iVscrollPos != GetScrollPos(hwnd, SB_VERT) )        //當滑塊位置改變時重置滑塊位置
118         {
119             SetScrollPos( hwnd, SB_VERT, iVscrollPos, TRUE ) ;
120             InvalidateRect( hwnd, NULL, TRUE ) ;                //使客戶區無效等待重繪
121         }
122         return 0 ;
123     
124     case WM_HSCROLL:    //處理水平滾動條消息
125         switch( LOWORD(wParam) )
126         {
127         case SB_LINELEFT:        //左翻一行
128             iHscrollPos -= 1 ;
129             break ;
130 
131         case SB_LINERIGHT:        //右翻一行
132             iHscrollPos += 1 ;
133             break ;
134 
135         case SB_PAGELEFT:        //左翻一頁
136             iHscrollPos -= cxClient / cxCaps ;
137             break ;
138 
139         case SB_PAGERIGHT:        //右翻一頁
140             iHscrollPos += cxClient / cxCaps ;
141             break ;
142 
143         case SB_THUMBPOSITION:    //拖動滑塊滑塊被放下時
144             iHscrollPos = HIWORD(wParam) ;
145             break ;    
146         
147         default:
148             break ;
149         }
150         iHscrollPos = max( 0, min( iHscrollPos, GetMaxLength() -1 ) ) ;
151         if( iHscrollPos != GetScrollPos( hwnd, SB_HORZ ) )
152         {
153             SetScrollPos( hwnd, SB_HORZ, iHscrollPos, TRUE ) ;
154             InvalidateRect( hwnd, NULL, TRUE ) ;
155         }
156         return 0 ;
157 
158     case WM_PAINT:        //處理WM_PAINT消息
159         hdc = BeginPaint( hwnd, &ps ) ;
160 
161         for( i= 0; i < NUMLINES; i++ )
162         {
163             y = cyChar * ( i -iVscrollPos ) ;
164             x = cxCaps * ( 0 - iHscrollPos ) ;
165             TextOut( hdc, x, y, statement[i], lstrlen(statement[i]) ) ;        //輸出文字
166         }
167         
168         EndPaint( hwnd, &ps ) ;
169         return 0 ;
170 
171     case WM_DESTROY:    //處理WM_DESTROY消息
172         PostQuitMessage( 0 ) ;
173         return 0 ;
174     }
175     
176     return DefWindowProc( hwnd, message, wParam, lParam ) ;
177 }

 

  編譯運行, 看下成果:

 

  看起來還算不錯, 當滾動條向下翻時文字就隨着向上滾動, 使下面的文字能夠顯示出來, 水平的滾動條也是這樣, 下面詳細說說重點部分的代碼:

    1>. 創建一個帶有垂直滾動條以及水平滾動條的窗口:

  hwnd = CreateWindow(
        szAppName, TEXT("滾動條示例"),
        WS_OVERLAPPEDWINDOW | WS_VSCROLL | WS_HSCROLL,
        CW_USEDEFAULT, CW_USEDEFAULT,
        CW_USEDEFAULT, CW_USEDEFAULT,
        NULL, NULL, hInstance, NULL
    ) ;

    

    2>. 設置垂直滾動條、水平滾動條的范圍以及初始位置:

        SetScrollRange( hwnd, SB_VERT, 0, NUMLINES - 1, FALSE ) ;          //設置垂直滾動條范圍的最小值和最大值
        SetScrollRange( hwnd, SB_HORZ, 0, GetMaxLength() - 1, FALSE ) ;     //設置水平滾動條范圍的最小值和最大值
        SetScrollPos( hwnd, SB_VERT, iVscrollPos, TRUE ) ;              //設置垂直滾動條中的滾動按鈕的位置
        SetScrollPos( hwnd, SB_HORZ, iHscrollPos, TRUE ) ;              //設置水平定滾動條中的滾動按鈕的位置

     可以看出, 垂直滾動條的范圍為0到行數-1, 這就意味這, 每下翻/上翻一個單位, 客戶區顯示的文字就會向上//向下滾動一行; 

    水平滾動條的范圍為0到最長那行文字的長度-1, 每左翻/右翻一個單位, 客戶區顯示的文字就會向右//向左滾動一個字符的寬度

 

    3>. 當窗口大小調整時重新獲取客戶區尺寸數據:

      case WM_SIZE:        //處理WM_SIZE
          GetClientRect( hwnd, &rect ) ;
          cyClient = rect.bottom ;              //得到客戶區y方向尺寸
          cxClient = rect.right ;                //得到客戶區x方向尺寸
          return 0 ;

 

    4>. 處理滾動條消息:

      case WM_VSCROLL:    //處理水平滾動條消息
          switch( LOWORD(wParam) )
          {
        case 滾動條消息:
          [處理滾動條消息]
          }

 

    5>. 重置滾動條滑塊位置:

        iVscrollPos = max( 0, min(iVscrollPos, NUMLINES -1) ) ;        //確保滾動條的位置在設置的范圍內。

        if( iVscrollPos != GetScrollPos(hwnd, SB_VERT) )            //當滑塊位置改變時重置滑塊位置
        {
            SetScrollPos( hwnd, SB_VERT, iVscrollPos, TRUE ) ;
            InvalidateRect( hwnd, NULL, TRUE ) ;                    //使客戶區無效等待重繪
        }
        return 0 ;    

      這一句注釋上已經描述的是否清楚了, 當滑塊位置改變時重置滑塊位置並使客戶區無效等待重繪。

 

    6>. 處理重繪消息:

    case WM_PAINT:        //處理WM_PAINT消息
          hdc = BeginPaint( hwnd, &ps ) ;

          for( i= 0; i < NUMLINES; i++ )
          {
              y = cyChar * ( i -iVscrollPos ) ;
              x = cxCaps * ( 0 - iHscrollPos ) ;
              TextOut( hdc, x, y, statement[i], lstrlen(statement[i]) ) ;        //輸出文字
          }
        
          EndPaint( hwnd, &ps ) ;
          return 0 ;

 

      目的是重繪客戶區內容並使其有效, 注意這里的

            y = cyChar * ( i -iVscrollPos ) ;
            x = cxCaps * ( 0 - iHscrollPos ) ;

      這是計算從起始輸出的坐標, 每行對應一個y值, 當滑塊的位置向下滾動1時, y的值就會減去一個字符的高度, 使該行顯示到窗口外部, 這樣新的一行就會被顯示出來, 同樣, 翻動一整夜的計算思路同一行; x是指水平起始輸出位置, 計算思路相同。

 

這樣, 一個簡單的帶有滾動條的窗口就完成了! 看起來挺不錯的, 不是嗎? 可以先稍微休息一下, 下面我們還有事要做。

 

 

但這還不夠好!

  在上面我們使用的滾動條中, 雖說能夠滾動文字, 但是依然存在許多小問題:

    問題一滑塊的大小是固定的, 而我見到的應用軟件滑塊能夠根據內容的多少自動調整滑塊大小;

    問題二: 當我拖動滑塊時只有當滑塊釋放時頁面才會滾動, 我想要的是當滑塊被拖動時頁面也同樣跟着滾動;

    問題三: 當滑塊滾動到底部時最后一行顯示到了客戶區頂部, 下面留有一大片的空白, 而我並不需要保留下面的空白, 也就是說最后一行在滑塊拖動到底部后它只顯示在底部就行。

 

  幸運的是, 我們依然有解決方案:

     問題一: 自行設置滑塊的大小;

     問題二根據SB_THUMBTRACK消息處理頁面的滾動, SB_THUMBTRACK消息是當滑塊被拖動時就會源源不斷的發來;

        問題三重新設置滾動的范圍。

 

 

 

更好的滾動條:

  在使用更好的滾動條之前我們首先要認識三個新函數: SetScrollInfo、GetScrollInfo以及ScrollWindow.

  1>. SetScrollInfo

    函數功能: 用於設置滾動條的相關參數, 包括滾動范圍的最大值和最小值, 頁面大小, 滑塊的位置, 函數的原型:

  int SetScrollInfo(
    HWND hWnd;              //窗口句柄
    int fnBar,               //指定被設定參數的滾動條的類型
    LPSCROLLINFO lpsi,       //指向一個SCROLLINFO結構
    BOOL fRedraw             //重繪標志
  ) ;

  穿插講述: 什么是SCROLLINFO結構?

    SCROLLINFO的成員記錄有關滾動條的信息, 其結構定義如下:

  typedef struct tagSCROLLINFO 
  { 
    UINT cbSize ;             //設置為sizeof (SCROLLINFO), 表示該結構的大小
    UINT fMask ;             //要設置或獲取的值
    int nMin ;               //滾動條范圍的最小值
    int nMax ;               //滾動條范圍的最大值
    UINT nPage ;            //頁面大小
    int nPos ;               //當前位置 
    int nTrackPos ;           //當前追蹤位置 
  }SCROLLINFO;

    成員一UINT cbSize :  該參數必須在函數調用之前設置,  cbSize表示該結構的大小, 用sizeof (SCROLLINFO)表示即可。

    成員二UINT fMask: 用於指定指定結構中的哪些成員是有效的, 通過位或運算進行組合可組合的標識符如下:

  SIF_ALL                     //整個結構都有效
  SIF_DISABLENOSCROLL
//禁用滾動條   SIF_PAGE      //用於指定或獲取頁面的大小, 在SetScrollInfo中用於設定頁面的大小, 在GetScrollInfo用於獲取頁面的大小
  SIF_POS      
//設置/取得滾動條滑塊當前的位置
  SIF_RANGE     
//滾動條的范圍   SIF_TRACKPOS //僅在GetScrollInfo函數中使用, 並且僅用在處理SB_THUMBTRACK或者SB_THUMBPOSITION的WM_VSCROLL消息或WM_HSCROLL消息時使用。取得當前滑塊的跟蹤位置。

     穿插講述完畢! 繼續講解第二個新函數。

 

  2>. GetScrollInfo

    用於取得滾動條的相關參數, 包括滾動范圍的最大值和最小值, 頁面大小, 滑塊的位置, 函數的原型:

  BOOL GetScrollInfo( 
    HWND hWnd,                    //窗口句柄
    int fnBar,                     //指定被設定參數的滾動條的類型
    LPSCROLLINFO lpsi              //指向一個SCROLLINFO結構
   );

 

  3>. ScrollWindow

    該函數的作用是滾動所指定的窗口客戶區域內容, 原型如下:

  BOOL ScrollWindow(
    HWND hWnd,               //窗口句柄
    int XAmount,                //指定水平滾動的距離
      int YAmount,                //指定垂直滾動的距離
      CONST RECT *IpRect,         //指向RECT結構的指針, 該結構指定了將要滾動的客戶區范圍。若此參數為NULL,則整個客戶區域將被滾動。 
    CONST RECT *lpClipRect      //指向RECT結構的指針, 該結構指定了要滾動的裁剪區域。只有這個矩形中才會被滾動。
);

  好了, 說的差不多夠多了, 研究代碼才是更好的溝通方式, 下面我們實際實踐一下更好的滾動條, 更多的細節請在代碼中體會, 限於篇幅的長度, 這里將WinMain函數折疊顯示, 僅將窗口過程函數部分的代碼全部顯示出來:

View Code - Function - WinMain
 1 #include<windows.h>
 2 #include"text.h"
 3 
 4 LRESULT CALLBACK WndProc( HWND, UINT, WPARAM, LPARAM ) ;        //聲明窗口過程函數
 5 
 6 int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdLine, int iCmdShow )
 7 {
 8     static TCHAR szAppName[] = TEXT("LearnScroll") ;
 9     HWND hwnd ;
10     MSG msg ;
11     WNDCLASS wndclass ;
12 
13     //窗口類成員屬性
14     wndclass.lpfnWndProc = WndProc ;
15     wndclass.style = CS_HREDRAW | CS_VREDRAW ;
16     wndclass.hInstance = hInstance ;
17     wndclass.lpszClassName = szAppName ;
18     wndclass.lpszMenuName = NULL ;
19     wndclass.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH) ;
20     wndclass.hCursor = LoadCursor(NULL, IDI_APPLICATION) ;
21     wndclass.hIcon = LoadIcon(NULL, IDC_ARROW) ;
22     wndclass.cbClsExtra = 0 ;
23     wndclass.cbWndExtra = 0 ;
24 
25     //注冊窗口類
26     if( !RegisterClass(&wndclass) )
27     {
28         MessageBox( NULL, TEXT("無法注冊窗口類!"), TEXT("錯誤"), MB_OK | MB_ICONERROR ) ;
29         return 0 ;
30     }
31 
32     //創建窗口
33     hwnd = CreateWindow(
34         szAppName, TEXT("滾動條示例"),
35         WS_OVERLAPPEDWINDOW | WS_VSCROLL | WS_HSCROLL,
36         CW_USEDEFAULT, CW_USEDEFAULT,
37         CW_USEDEFAULT, CW_USEDEFAULT,
38         NULL, NULL, hInstance, NULL
39     ) ;
40 
41     //顯示窗口
42     ShowWindow( hwnd, iCmdShow ) ;
43     UpdateWindow( hwnd ) ;
44 
45     //獲取、翻譯、分發消息
46     while( GetMessage( &msg, NULL, 0, 0 ) )
47     {
48         TranslateMessage( &msg ) ;
49         DispatchMessage( &msg ) ;
50     }
51     
52     return msg.wParam ;
53 }

 

  窗口過程部分的代碼:

  1 LRESULT CALLBACK WndProc( HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam )
  2 {
  3     static int cxChar, cxCaps, cyChar, cyClient, cxClient, iVscrollPos, iHscrollPos ;
  4     //cxChar:平均字符寬度; cxCaps: 大寫字母平均寬度; cyChar: 字符高; cyClient、cxClient: 客戶區y、x方向尺寸; 
  5     //iVscrollPos: 豎直方向滾動條滑塊位置; iHscrollPos: 水平方向滾動條滑塊位置
  6     HDC hdc ;
  7     RECT rect ;            //記錄客戶區RECT結構
  8     int i, x, y;        //i循環控制, x記錄水平方向坐標, y豎直方向坐標
  9     PAINTSTRUCT ps ;
 10     TEXTMETRIC tm ;
 11     SCROLLINFO  si ;    //SCROLLINFO結構對象
 12     int iMaxLength ;    //所有語句中的最大長度
 13 
 14     iMaxLength = GetMaxLength() ;        //取得最大長度
 15 
 16     switch(message)
 17     {
 18     case WM_CREATE:        //處理WM_CREATE消息
 19         hdc = GetDC(hwnd) ;
 20         GetTextMetrics( hdc, &tm ) ;    //獲取系統字體信息
 21         cxChar = tm.tmAveCharWidth ;    //獲取平均寬度
 22         cxCaps = (tm.tmPitchAndFamily & 1 ? 3 : 2) * cxChar / 2 ;        //大寫字母平均寬度
 23         cyChar = tm.tmHeight + tm.tmExternalLeading ;                    //字符高度
 24         ReleaseDC( hwnd, hdc );
 25
 26         return 0 ;
 27 
 28     case WM_SIZE:        //處理WM_SIZE
 29         GetClientRect( hwnd, &rect ) ;
 30         cxClient = LOWORD (lParam) ;
 31         cyClient = HIWORD (lParam) ;
 32 
 33         //設置垂直滾動條相關參數
 34         si.cbSize = sizeof (si) ;
 35         si.fMask  = SIF_RANGE | SIF_PAGE ;
 36         si.nMin   = 0 ;
 37         si.nMax   = NUMLINES - 1 ;
 38         si.nPage  = cyClient / cyChar ;
 39         SetScrollInfo(hwnd, SB_VERT, &si, TRUE) ;
 40 
 41         //設置水平滾動條相關參數
 42         si.cbSize = sizeof (si) ;
 43         si.fMask  = SIF_RANGE | SIF_PAGE ;
 44         si.nMin   = 0 ;
 45         si.nMax   = 2 + iMaxLength ;
 46         si.nPage  = cxClient / cxCaps ;
 47         SetScrollInfo(hwnd, SB_HORZ, &si, TRUE) ;
 48 
 49         return 0 ;
 50 
 51     case WM_VSCROLL:    //處理垂直滾動條消息
 52         si.cbSize = sizeof (si) ;
 53         si.fMask  = SIF_ALL ;
 54         GetScrollInfo(hwnd, SB_VERT, &si) ;
 55 
 56         iVscrollPos = si.nPos ;    //記錄當前滑塊位置
 57 
 58         switch( LOWORD(wParam) )    //處理滾動條消息
 59         {
 60         case SB_TOP:            //到達頂部
 61             si.nPos = si.nMin ;
 62             break ;
 63                
 64         case SB_BOTTOM:            //到達底部
 65             si.nPos = si.nMax ;
 66             break ;
 67 
 68         case SB_LINEUP:            //上翻一行
 69             si.nPos -= 1 ;
 70             break ;
 71 
 72         case SB_LINEDOWN:        //下翻一行
 73             si.nPos += 1 ;
 74             break ;
 75 
 76         case SB_PAGEUP:            //向上翻一整頁
 77             si.nPos -= si.nPage ;
 78             break ;
 79 
 80         case SB_PAGEDOWN:        //向下翻一整頁
 81             si.nPos += si.nPage ;
 82             break ;
 83 
 84         case SB_THUMBTRACK:        //移動滑塊時
 85             si.nPos = si.nTrackPos ;
 86             break ;
 87 
 88         default:
 89             break;
 90         }
 91         si.fMask = SIF_POS ;
 92         SetScrollInfo(hwnd, SB_VERT, &si, TRUE) ;        //重置滑塊位置
 93         GetScrollInfo(hwnd, SB_VERT, &si) ;
 94         if( si.nPos != iVscrollPos )
 95         {
 96                ScrollWindow(hwnd, 0, cyChar * (iVscrollPos - si.nPos), NULL, NULL) ;        //滾動內容
 97                UpdateWindow(hwnd) ;
 98         }
 99         return 0 ;
100     
101     case WM_HSCROLL:    //處理水平滾動條消息
102         si.cbSize = sizeof (si) ;
103         si.fMask  = SIF_ALL ;
104         GetScrollInfo (hwnd, SB_HORZ, &si) ;
105 
106         iHscrollPos = si.nPos ;    //記錄當前滑塊位置
107         switch( LOWORD(wParam) )
108         {
109         case SB_TOP:            //到達頂部
110             si.nPos = si.nMin ;
111             break ;
112                
113         case SB_BOTTOM:            //到達底部
114             si.nPos = si.nMax ;
115             break ;
116             
117         case SB_LINELEFT:        //左翻一行
118             si.nPos -= 1 ;
119             break ;
120 
121         case SB_LINERIGHT:        //右翻一行
122             si.nPos += 1 ;
123             break ;
124 
125         case SB_PAGELEFT:        //左翻一頁
126             si.nPos -= si.nPage ;
127             break ;
128 
129         case SB_PAGERIGHT:        //右翻一頁
130             si.nPos += si.nPage ;
131             break ;
132 
133         case SB_THUMBTRACK:        //移動滑塊時
134             si.nPos = si.nTrackPos ;
135             break ;    
136         
137         default:
138             break ;
139         }
140         si.fMask = SIF_POS ;
141         SetScrollInfo(hwnd, SB_HORZ, &si, TRUE) ;        //重置滑塊位置
142         GetScrollInfo(hwnd, SB_HORZ, &si) ;
143         if( si.nPos != iHscrollPos )
144         {
145                ScrollWindow(hwnd, cxCaps * (iHscrollPos - si.nPos), 0, NULL, NULL) ;        //滾動內容
146                UpdateWindow(hwnd) ;
147         }
148         return 0 ;
149 
150     case WM_PAINT:        //處理WM_PAINT消息
151         hdc = BeginPaint( hwnd, &ps ) ;
152 
153         si.cbSize = sizeof (si) ;
154         si.fMask  = SIF_POS ;
155         GetScrollInfo(hwnd, SB_VERT, &si) ;
156         iVscrollPos = si.nPos ;                    //獲取當前垂直滑塊位置
157 
158         GetScrollInfo(hwnd, SB_HORZ, &si) ;
159         iHscrollPos = si.nPos ;                    //獲取當前水平滑塊位置
160 
161         for( i= 0; i < NUMLINES; i++ )
162         {
163             y = cyChar * ( i - iVscrollPos ) ;
164             x = cxCaps * ( 0 - iHscrollPos ) ;
165             TextOut( hdc, x, y, statement[i], lstrlen(statement[i]) ) ;        //輸出文字
166         }
167         
168         EndPaint( hwnd, &ps ) ;
169         return 0 ;
170 
171     case WM_DESTROY:    //處理WM_DESTROY消息
172         PostQuitMessage( 0 ) ;
173         return 0 ;
174     }
175     
176     return DefWindowProc( hwnd, message, wParam, lParam ) ;
177 }

 

看一下成果:

嗯, 這樣看起來就好多了, 如果嫌行間距太擠的話我們可以調節字符的高度

        cyChar = tm.tmHeight + tm.tmExternalLeading ;                    //字符高度

使行間距增大些, 這樣看起來會更舒服。

好了, 到這里, 一個較為完善的滾動條就完成了。

 

--------------------

 

wid, 2012.10.31

 

上一篇: C語言Windows程序設計->第七天->TextOut與系統字體

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM