1.4?Tab?控件?
1.4.1?Tab?控件設計方法?
Tab?控件類似分隔標簽或一組文件夾上的標簽,使用Tab?控件可以在應用程序窗口或對話框中的同一區域定義多個頁面。該控件包含了一個或多個panel?面板集合。在設計時和運行時,可以設置其屬性來控制Tab?控件的外觀,如可設置標簽以多行形式顯示,將標簽放在Tab?控件的上下左右各種位置,并可在標簽上顯示圖標。
LabWindows/CVI?中的Tab?控件最初是由Panel?面板轉換而來,控件中的每個頁面都有自己的句柄(類似于面板句柄),可以認為Tab?控件是一個容器,里面包含了面板的集合,因此,Tab?控件內也可重復內嵌Tab?控件。正是由于Tab?控件具有這些特點與屬性,在使用某些函數時,如SetCtrlVal?、GetCtrlVal?等時,參數中的面板句柄設置應區分是一般面板句柄還是Tab?頁面句柄。
在LabWindows/CVI?中,Tab?控件是最為特殊的一個控件,這主要是因為引入了容器中的容器概念(筆者定義)。與其他語言最大的區別是,在LabWindows/CVI?中,一個面板即為一個容器,所有的控件必須放置在這個容器內,不允許在這個面板上存在其他的容器,如果換個角度,把容器看作句柄,也就是說,一個面板只有一個句柄,控件沒有句柄,只有ID(ActiveX?控件除外),即使是Radio?Group?這類控件,嚴格上來說也是由Tree?控件衍生而來,不具有控件句柄。這樣設計的好處是,LabWindows/CVI?的消息傳遞是扁平化的,只在一個容器中進行消息循環,事件響應及時;缺點也顯而易見,不能設計出像Visual?Basic?或Delphi?中非常完美的控件組或頁框(如Frame),但這也是LabWindows/CVI?設計的一大優點,簡化設計思路,使程序設計者更加專注于邏輯系統設計而非語言本身。舉例說明,如果在Visual?Basic?或Delphi?中建有一個工程,Form?(窗體)里面有一個Tab?控件,Tab?控件內嵌套一個Numeric?控件,可以對其進行屬性操作,即獲得或設置屬性值;同理,在LabWindows/CVI?集成開發環境中也建有一個工程,Panel(面板)里面有一個Tab?控件,控件內嵌套一個Numeric?控件,則引用方法如下:
(1)Visual Basic 語句 ??? ?
賦值??
Form.Tab.Numeric.value = 1 ??? ?
取值??
x = Form.Tab.Numeric.value ??? ?
(2)Delphi 語句??
賦值??
Form.Tab.Numeric.value := 1 ?? ?
取值??
x := Form.Tab.Numeric.value ?? ?
(3)LabWindows/CVI 函數 ? ?
賦值(函數語句取自本例程)
panelHandle = LoadPanel (0, "Tab 控件.uir", PANEL); GetPanelHandleFromTabPage (panelHandle, PANEL_TAB, 0, &TabPanelHandle); SetCtrlVal (TabPanelHandle, TABPANEL_NUMERICTHERM, temp); 取值(函數語句取自本例程)
panelHandle = LoadPanel (0, "Tab 控件.uir", PANEL); GetPanelHandleFromTabPage (panelHandle, PANEL_TAB, 0, &TabPanelHandle); GetCtrlVal (TabPanelHandle, TABPANEL_NUMERICKNOB, &temp); ???
有意思的是,Visual?Basic?和Delphi?對控件屬性的引用中用了3?個“.”,這3?個“.”對應于LabWindows/CVI?的3?個函數語句,三種語言之間,屬性獲得或設置都是從容器到實例的逐次遞進引用,邏輯上是相同的,只是表現方式不同,其邏輯關系如圖1-9?所示。
(a)?Visual?Basic?或Delphi?(b)LabWindows/CVI
圖1-9?屬性獲得或設置邏輯關系
與Visual Basic 和Delphi 的差別也可以很清晰地發現,LabWindows/CVI 的編碼量巨大,函數量眾多,而且大量的編碼是用在屬性設置上的,為了克服編碼量大、函數多程序員不容易記,書寫容易出現錯誤的缺點,LabWindows/CVI 引入函數面板(Function Panel),即我們常用的.fp 文件,使得對于屬性的編程只需要用函數面板輸入幾個參數就可以實現大部分功能,簡單、易操作。
在編輯狀態,雙擊面板中的Tab 控件,彈出Edit Tab 對話框,如圖1-10 所示。在Control Apperarance 框中,Tabs Fit Mode 用來設置單行、多行顯示等,默認為單行顯示,Tab Location 用來設置Tab 標簽的位置,默認放置在控件的左上角。在Tab 框中,可以設置每一個頁面的標簽、常量名等,可以進行添加、剪切、復制、粘貼,頁面的前后移動,對頁面標簽添加圖標以及設置每個頁面控件的焦點轉移次序Tab Order 等操作。
圖1-10 Edit Tab 對話框
1.4.2 Tab 控件程序設計
(1)面板設計
編寫一個Tab 控件屬性設置程序,能夠響應頁面(標簽頁)內的事件、設置標簽、計算Tab 頁面數目,改變標簽的顯示風格,并且在標簽上可以設置顯示圖標。在頁面中,有兩個Command Button 控件,擁有相同的常量值和回調函數名,在調用時,需要通過標題字符來區分所要響應的事件。面板設計如圖1-11 所示,面板中主要控件屬性設置如表1-11 所示。
圖1-11 Tab 控件面板
表1-11 控件屬性設置表
(2)程序源代碼
//頭文件聲明
#include 《formatio.h》
#include 《cvirte.h》
#include 《userint.h》
#include “Tab 控件.h”
//Tab 頁面句柄聲明
static int TabPanelHandle;
//Tab 頁面索引聲明
static int index = 0;
static int panelHandle; //主函數
int main (int argc, char *argv[])
{
if (InitCVIRTE (0, argv, 0) == 0)
return –1; /* out of memory */ if ((panelHandle = LoadPanel (0, “Tab 控件.uir”, PANEL)) 《 0)
return –1;
DisplayPanel (panelHandle);
RunUserInterface ();
DiscardPanel (panelHandle);
return 0;
} //面板回調函數
?int CVICALLBACK PanelCB (int panel, int event, void *callbackData,
int eventData1, int eventData2)
{
switch (event)
{
case EVENT_CLOSE:
QuitUserInterface (0);
break;
}
return 0;
}
//事件響應頁面
int CVICALLBACK valset (int panel, int control, int event,
void *callbackData, int eventData1, int eventData2)
{
int temp;
switch (event)
{
case EVENT_COMMIT:
// 獲得Tab 頁面句柄
GetPanelHandleFromTabPage (panelHandle, PANEL_TAB, 0, &TabPanelHandle);
// 獲得Tab 頁面中控件值
GetCtrlVal (TabPanelHandle, TABPANEL_NUMERICKNOB, &temp);
// 設置Tab 頁面中控件值
SetCtrlVal (TabPanelHandle, TABPANEL_NUMERICTHERM, temp);
break;
}
return 0;
}
//頁面中的Command Button 按鈕
int CVICALLBACK OkCallback (int panel, int control, int event,
void *callbackData, int eventData1, int eventData2)
{
int area;
char s[20];
char label[20];
int count;
switch (event)
{
case EVENT_COMMIT:
// 獲得控件屬性
GetCtrlAttribute (panel, control, ATTR_LABEL_TEXT, label);
// 獲得當前激活頁面索引
GetActiveTabPage (panelHandle, PANEL_TAB, &index);
// 通過索引獲得當前頁面句柄
GetPanelHandleFromTabPage (panelHandle, PANEL_TAB, index, &TabPanelHandle);
? // 如果當前頁面中的按鈕為“設置”時,執行以下操作
if (CompareStrings (label, 0, “ 設置”, 0, 1) == 0)
{
// 獲得頁面中指定值
GetCtrlVal (TabPanelHandle, TABPANEL_2_STRING, s);
// 設置當前頁面中標簽為指定值
SetTabPageAttribute (panelHandle, PANEL_TAB, index, ATTR_LABEL_TEXT, s);
}
// 如果當前頁面中的按鈕為“獲取”時,執行以下操作
if (CompareStrings (label, 0, “ 獲取”, 0, 1) == 0)
{
// 獲得Tab 控件中頁面數目
GetNumTabPages (panelHandle, PANEL_TAB, &count);
// 顯示頁面數目
SetCtrlVal (TabPanelHandle, TABPANEL_3_NUMERIC, count);
}
break;
}
return 0;
}
//顯示風格回調函數
int CVICALLBACK style (int panel, int control, int event,
void *callbackData, int eventData1, int eventData2)
{
int kind;
switch (event)
{
case EVENT_COMMIT:
// 獲得控件值
GetCtrlVal (panel, control, &kind);
// 設置Tabp 控件風格
SetCtrlAttribute (panelHandle, PANEL_TAB, ATTR_TABS_LOCATION, kind);
break;
}
return 0;
}
3:程序注釋
① GetPanelHandleFromTabPage 函數獲得Tab 頁面句柄。函數原型為:
int GetPanelHandleFromTabPage (int Panel_Handle, int Control_ID, int Index, int
*Tab_Panel_Handle);
Panel_Handle:Tab 控件所在面板句柄。
Control_ID:Tab 控件ID。
Index:Tab 頁面(標簽頁)索引,第一個頁面為0,依次遞增。注意,此值需要用戶設定而非系統返回值。
*Tab_Panel_Handle :返回Tab 頁面索引指定的頁面句柄。
② GetActiveTabPage 函數獲得當前激活的Tab 頁面索引。函數原型為:int GetActiveTabPage (int Panel_Handle, int Control_ID, int *Active_Index);
*Active_Index :當前激活頁面索引值,從0 開始計,–1 表示沒有活動頁面。
③ CompareStrings 函數
字符串比較函數。將字符串1 中從某一位置開始所組成的子串與字符串2 中從某一位置開始所組成的子串進行比較。函數原型為:
int CompareStrings (char String_1[], int String_1_Index, char String_2[], int String_2_Index, int
Case_Sensitive);
String_1[] :字符串1。
String_1_Index :子串1 起始位置。
String_2[] :字符串2。
String_2_Index :子串2 起始位置。
Case_Sensitive:是否區分大小寫。
返回值:0 表示兩子串相等,–1 表示子串1 小于子串2,1 表示子串1 大于子串2。
④ valset 回調函數
在valset 回調函數中編寫了GetCtrlVal (TabPanelHandle, TABPANEL_NUMERICKNOB, &temp) 語句,值得注意的是,GetCtrlVal 函數Panel Handle 參數并非在程序前面聲明的panelHandle, 而是采用了GetPanelHandleFromTabPage 函數的返回Tab 頁面句柄TabPanelHandle,也可以利用控件回調函數傳回的函數句柄panel 。Control ID 參數可以寫成TABPANEL_NUMERICKNOB ,也可以利用控件回調函數傳回的控件常量control 參數。此外,為了使數值設置與數值顯示看起來同步,可以將event 事件中的EVENT_COMMIT 改寫為EVENT_VAL_CHANGED ,修改后的回調函數如下:
int CVICALLBACK valset (int panel, int control, int event,
void *callbackData, int eventData1, int eventData2)
{
int temp;
switch (event)
{
case EVENT_VAL_CHANGED:
// 獲得Tab 頁面句柄
GetPanelHandleFromTabPage (panelHandle, PANEL_TAB, 0, &TabPanelHandle);
// 獲得Tab 頁面中控件值
GetCtrlVal (panel, control, &temp);
// 設置Tab 頁面中控件值
SetCtrlVal (panel, TABPANEL_NUMERICTHERM, temp);
break;
}
return 0;
}
⑤ 具有相同常量名和回調函數的Command Button 按鈕事件響應方式設計在面板設計中,Command Button 按鈕在標簽設置和Tab 數目頁面出現了兩次,分別為“設置”和“獲取”按鈕,其回調函數名稱和常量名完全一致,只是標題不同,這是由于Tab 控件本身就是多面板的集合,類似于一個容器控件,Tab 頁面以子面板的方式出現,造成了可以重命名不同控件的問題,雖然不利于控件事件的區分,但卻能減少回調函數的數量,利于代碼的優化。面板和控件在程序頭文件中定義如下:
#define PANEL 1
#define PANEL_TAB 2
#define TABPANEL_NUMERICTHERM 2
#define TABPANEL_NUMERICKNOB 3
#define TABPANEL_2_STRING 2
#define TABPANEL_2_OKBUTTON 3
#define TABPANEL_3_NUMERIC 2
#define TABPANEL_3_OKBUTTON 3
#define TABPANEL_4_RING 2
從以上代碼可以看出,雖然兩個控件在界面設計階段具有相同的常量名,在由于所在的Tab 頁面不同,系統生成的常量名并不相同,分別為TABPANEL_2_OKBUTTON 和TABPANEL_3_OKBUTTON ,但常量值是相同的且均為3,在相同回調函數下,只能通過控件的不同標題來區分所響應的事件。
(4)運行效果圖
點擊工具欄中的Debug Project 按鈕,程序開始運行,其效果如圖1-12 所示。
圖1-12 運行效果圖
評論
查看更多