PB從入坑到放棄(五)視窗使用技巧

2023-07-21 06:00:58

PB應用程式就是由許多共同共同作業完成特定任務的視窗組成的集合。

視窗在應用程式的開發工作中佔有很大的比重,是非常重要的一個 PB 物件

一、視窗型別

視窗型別 描述
Main ①可以覆蓋其他視窗,也可以被其他視窗覆蓋
②可以最大、最小化,可以用滑鼠改變其 大小
③可以有邊框,可以有選單
④常用於應用主操作視窗
Child ①可以被其他視窗覆蓋,也可以覆蓋其他視窗
②可以最大或最小化,也可以用滑鼠改變 其大小,父視窗最小或最大化時, Child 型別的視窗也相應的被最大或最小化
③可以 有邊框,但不能有選單
④位置是相對於其父視窗而言,用滑鼠拖放其位置不能超過其 父視窗的範圍
⑤常被其他視窗開啟
Response ①不能最大、最小化,沒有邊框,不能用滑鼠改變其大小
②在繼續執行程式之前,必須 對這種型別的視窗做出響應
③常用於顯示非常重要的資訊
Popup ①可以最大或最小化,在對這種型別的視窗響應之前,可以操作其他視窗
②不隨父視窗 最大和最小化
③可以擁有選單、邊框
④常用於顯示其他視窗的輔助資訊

二、視窗屬性

-- General 屬性頁屬性 --Scroll 屬性頁屬性
-- ToolBar 屬性頁屬性 --Other 屬性頁

2.1 General 屬性頁屬性

屬性 簡介
Title 視窗的標題
Tag 和視窗相關的一個文字屬性,可以理解成視窗的註釋
MenuName 和視窗相連的選單
Visibl 視窗是否可見
Enabled 確定視窗的功能是否可用
TitleBar 確定視窗是否有標題列
ControlMenu, MaxBox, MinBox 三個屬性確定在視窗的標題列是否顯示最大、最小化以及關閉按鈕
ClientEdge 是否顯示使用者工作區邊框
PaletteWindow 該屬性只用於 Popup 型別的視窗,確定是否顯示標題列圖示和最大、最小化按鈕
ContextHelp 只用在 Response 型別的視窗上。確定是否支援上下文幫助。如果支援就在視窗的右上角顯示一個問號圖示
RightToLeft 確定由右到左顯示
Center 視窗居中顯示,不管螢幕解析度如何設定。該屬性在 PB 7.0 中沒有,新增
Resizable 決定視窗在執行時是否可以改變其大小
Border 決定視窗是否有邊框
WindowType 確定視窗的型別,可用取值的含義,從字面意思可以理解。該屬性在視窗開啟時起作用
BackColor 設定視窗的背景顏色
MdiClientColor 用來確定 MDI 使用者區的顏色
Icon 用來設定視窗標題列的圖示

2.2 Scroll 屬性頁屬性

屬性 簡介
HscrollBar, VscrollBar 是否需要顯示水平或垂直卷軸
UnitsPerLine 使用者在垂直卷軸上每次單擊移動的 PB單位數。預設為 0,表示每次 捲動視窗高度的 1/100
UnitsPerColumn 使用者在水平卷軸上每次單擊向左或者向右移動的 PB單位數。預設為 0,表示每次捲動視窗寬度的 1/100
ColumnsPerPage 表示每頁顯示的列數。預設為 0,表示顯示 10 列
LinesPerPage 表示每頁顯示的行數。預設為 0,表示顯示 10 行

2.3 ToolBar 屬性頁屬性

屬性 簡介
ToolBarVisible 工具條是否可見
ToolBarAlignment 確定工具條在視窗上的初始位置。 有 5 個可用的列舉型取值: AlignAtBootom!(底部)、AlignAtLeft!(左)、 AlignAtRight!(右)、 AlignAtTop!(頂部)、 Floating!(浮動)
ToolBarX, ToolBarY, ToolBa rHeight 和 ToolBarWidth 用來設定當工具條浮動顯示時工具條的起始位置及高寬。預設取值都是 0

2.4 Other 屬性頁中的屬性

用來設定視窗的真實座標、寬高和滑鼠的預設指標。

當螢幕解析度是 800 × 600 時,全螢幕顯示的視窗 X, Y, Width 和 Height 分別是 0, 0, 3658, 2407。

三、11種常用控制元件

視窗可以看作是一個容器,所以在視窗上可以放各種控制元件,來完成使用者與程式的互動

控制元件 說明
Button 用來執行特定的命令,接受使用者的鍵盤或滑鼠操作。
該控制元件的 Text 屬性一般用來 向用戶說明該按鈕的功能。
典型事件是 Clicked
Picture button 和控制元件 Button 相同,只是在它的上面還可以顯示圖形,提供更漂亮友好的介面。
典型事件也是 Clicked
Static text 顯示指示性或解釋性資訊,文字只能單行顯示。
一般很少在該控制元件上程式設計
Singlelineedit 單行編輯,一般用來接受使用者比較少的文字輸入。
典型事件是 Modified,通常在該 事件上判斷使用者的輸入是否符合既定規則,如是否是數位等
Multilineedit 多行編輯,用來接收使用者比較多的文字輸入。
典型事件也是 Modified,通常在該事 件上判斷使用者的輸入是否符合既定規則
Check box 核取方塊,用來界定使用者的輸入,一般輸入資訊作為其他命令執行時的選項。
當有多個核取方塊時,它們間的連線符習慣性認為是「 and」。
重要屬性是 Checked,通常用來判斷該控制元件是否選中
Radio box 無線電鈕,用來界定使用者的輸入只能是給定值中的一個。
它的輸入資訊通 常用來選擇其他命令互斥的執行方式。
重要屬性 是 Checked,true 表示選中
Dropdownlistbox 以緊湊列表框的形式給使用者顯示資訊,通常在該控制元件中進行單選。
經常和遊標 ( cursor ) 配 合 使 用 , 顯 示 從 數 據 庫 中 提 取 的 數 據 。
典 型 事 件 是 SelectionChanged,通常在該事件上讀取當前使用者的選擇資訊
Listbox 列表框,是顯示資訊的,但其顯示格式不如 Dropdownlistbox 緊湊,也通常和光 標(cursor)配合使用,顯示從資料庫中提取的資料。但是,該控制元件經常可以同時 選中多行。典型事件也是 SelectionChanged,通常在該事件上讀取當前使用者的選 擇資訊
DataWindow 是 PB中最重要的、也是 PB最具特色的一個控制元件。
可以 完成對資料庫中的所有資料操作
Group box 將功能類似的或在功能模組上關係緊密的部件放置在一起,介面美觀,可以 給使用者一個操作上的暗示。很少在該控制元件上程式設計

四、視窗事件

4.1 常用事件

事件 事件說明
open 在視窗開啟之後,顯示之前發生。
調整視窗大小、設定視窗範例變數、初始化一些控制元件
close 視窗被關閉時發生。觸發該事件後,沒有辦法阻止視窗的關閉
closequery 在開始關閉視窗時發生,該事件返回一個0或1的返回值
如果返回值為1,則視窗不關閉,close事件也不會產生
如果返回值為0,則視窗被關閉
resize 當視窗大小發生變化時變化,視窗被開啟時也觸發此事件
key 當用戶在鍵盤上按下一個鍵且插入點不在編輯區域時發生
activate 在視窗成為活動視窗之前、 Open 事件觸發完後觸發 ,此事件發生後,Tab 值最小的物件得到焦點,如果沒有 排序號,則視窗本身獲得焦點
deactivate 視窗變為不活動時發生
clicked 戶單擊視窗的空白區域時發生,此區域不能有任何的空間或資料視窗
doubleclicked 使用者雙擊視窗的空白區域時發生,此區域不能有任何的空間或資料視窗
dragleave 當可拖放物件或控制元件,離開空白區時發生
dragleave 當可拖放物件或控制元件,在視窗中被拖動時發生
hotlinkalarm 在動態資料交換(DDE)伺服器應用傳送了新的(修改後)的資料,且客 戶DDE應用程式已經接收到資料時發生
mousedown 空白區,單擊滑鼠左擊時發生。
此事件與click事件相同,flags的值總為1
mousemove 滑鼠在視窗中移動時發生
mouseup 放開滑鼠左鍵時發生
rbuttondown 空白區,按下滑鼠右鍵時發生
remoteexec 當一個DDE客戶應用程式傳送了一條命令時發生
remotehotlinkstart 當一個DDE客戶應用程式要開始一個熱連線(hotlink)時發生
remotehotlinkstop 當一個DDE客戶應用程式要結束一個熱連結時發生
remoterequest 當一個DDE客戶應用程式請求資料時發生
remotesend 當一個DDE客戶應用程式已經傳送了資料時產生
systemkey 當插入點不在編輯框中且使用者按下【alt】或【alt+其他組合鍵時發生】
timer 呼叫timer函數,啟動定時器、設定時間後發生
toolbarmoved 當MDI視窗中的工具列被移動時發生

4.2 舉個栗子

① open事件

Cb_Save.Enabled=False //禁用「儲存」按鈕的功能
SetPointer(HourGlass!) //將滑鼠形狀置為沙漏形
Dw_1.SetTransObject(SQLCA) //為資料視窗設定事務物件
Dw_1.Retrieve() //檢索資料

② 避免使用者因疏忽退出視窗而丟掉在資料視窗中的修改資料 ,通常在 CloseQuery 事件中判
斷某些工作是否完成,並顯示一個提示視窗詢問使用者,根據使用者的確認,返回一個值來決定
是否觸發視窗的 Close 事件。

返回值為 1,表示取消關閉動作;返回值為 0,表示繼續執行Close 事件

Int li_flag
//如果資料視窗中沒有修改,則允許執行 Close,直接返回
If dw_1.ModifiedCount() <= 0 And dw_1.DeletedCount() <= 0 Then Return 0
//如果資料視窗有修改,詢問使用者是否儲存
li_flag = MessageBox("提示","資料已經修改,是否儲存? ",Question!,YesNoCancel!,1)
Choose Case flag_i //根據使用者選擇執行
	Case 1 //使用者選擇要儲存資料
		If dw_1.Update() = 1 Then //如果修改資料成功
			Commit; //提交
			Return 0 //繼續執行 Close 事件
		Else //修改資料不成功
			rollback; //回退事務
			li_flag = MessageBox("提示","資料錯誤,是否繼續關閉!",Question!,YesNoCancel!,2) //顯示錯誤
			If li_flag = 1 Then
				Return 0 //允許關閉
			Else
				Return 1 //不允許關閉
			End If
		End If
	Case 2 //使用者選擇不儲存資料
        Rollback; //回退事務
        Return 0 //允許執行 Close 事件
	Case 3 //使用者選擇取消
		Return 1 //不允許關閉
End Choose //使用者所有的選擇情況處理完畢

③ 在視窗的 Resize 事件中編寫指令碼,當用戶調整視窗大小時,根據使用者調整的
比例對視窗上的控制元件大小進行調整。首先定義兩個範例變數用來儲存調整之前的視窗大小

//open事件中程式碼
ii_width = This.width
ii_height = This.height
//Resize事件中程式碼
Int li_index //迴圈變數
DragObject lobj_every //用來獲取視窗上的控制元件
For li_index = 1 To UpperBound(this.control[]) //對視窗中的所有控制元件逐一處理
    lobj_every = control[li_index] //儲存當前控制元件
    lobj_every.x = lw_obj.x * (newwidth / ii_width) //重新設定 x 座標
    lobj_every.width = lobj_every.width * (newwidth / ii_width) //重新設定寬度
    lobj_every.y = lobj_every.y * (newheight / ii_height) //重新設定 y 座標
    lobj_every.height = lobj_every.height * (newheight / ii_height) //重新設定高度
Next
ii_width = newwidth //儲存當前寬度
ii_height = newheight //儲存當前高度

五、視窗常用函數

5.1 open 函數

函數用來開啟其他的視窗

① 語法

Open ( Windowvar)

② 引數

  • Windowvar 是一個 Window 型別的引數,是要開啟的視窗名稱

③ 返回

  • 成功時返回1,否則返回-1
  • 數為 Null, 則返回 Null

④ 舉個栗子

//開啟主視窗
Open(w_main)

注:連續呼叫Open函數,視窗不會被開啟兩次,只是第二次呼叫時會再次觸發視窗 Activate 事件。為了避免這種情況,可以將指令碼修改如下

If Not IsValid(w_main) Then //如果視窗沒有開啟
	Open(w_main) //則開啟該視窗
Else //如果視窗已經開啟
	W_main.BringToTop = True //將該視窗顯示在最頂層
End If

竟然open函數呼叫兩次也不能開啟兩個視窗,那麼問題來了,怎麼才能開啟多個視窗呢?程式碼如下

① 開啟和 w_edit 完全相同的一個視窗

w_edit lws_edit  //定義一個視窗變數
Open(lws_edit)   //建立視窗範例

②通過陣列,開啟多個範例視窗

w_edit lws_edit[3]
Int li_index
For li_index =1 To 3
	Open(lws_edit[li_index])
Next

5.2 close 函數

函數的作用是關閉視窗,釋放視窗及其上面的控制元件所佔用的記憶體空間,視窗的CloseQuery 事件和 Close 事件觸發

① 語法

Close ( Windowname )

② 引數

  • Windowname 是要關閉的視窗名稱,是一個 Window 型別的變數
  • 成功返回 1,否則返回-1
  • 引數為 Null,則返回 Null

5.3 MessageBox 函數

函數可以開啟一個小資訊視窗,不僅可以以多種方式給使用者顯示提示資訊,還可以將使用者的選擇資訊返回 。

小資訊視窗有標題、提示資訊、圖示、按鈕等 4 個元素,可以通過不同的引數來決定顯示哪些或者顯示哪種樣式。

① 簡單模式

只能顯示提示資訊,並有一個確認按鈕,不能讓使用者進行選擇

  • 語法
MessageBox(title,text)
  • 引數

    • title 為資訊視窗標題
    • text 為提示資訊
  • 舉個栗子

    MessageBox(「錯誤提示」 ,「資料儲存錯誤! 」 )
    

② 複雜模式

  • 語法

    MessageBox ( title, text {, icon {, button {, default } } } )
    
  • 引數

    • title 為資訊視窗標題
    • text 為提示資訊
    • icon 用來表示使用哪種圖示
    • button 用來表示提供哪些按鈕

圖示icon取值如下

按鈕button取值如下

引數取值 顯示樣式 返回值
OK! 顯示 【確定】 按鈕,該取值為預設值 總返回 1
OKCancel! 顯示 【確定】 和 【取消】按鈕 1-【確定】, 2-【取消】
YesNo! 顯示 【是】 和 【否】按鈕 1-【是】, 2-【否】
YesNoCancel! 顯示 【是】、 【否】 和 【取消】 三個按鈕 1-【是】, 2-【否】, 3-【取消】
RetryCancel! 顯示 【重試】 和 【取消】按鈕 1-【重試】, 2-【取消】
AbortRetryIgnore! 顯示 【放棄】、 【重試】和 【忽略】三個按鈕 1-【放棄】, 2-【重試】, 3-【忽略】
  • 舉個栗子

    li_flag = MessageBox("提示","是否儲存資料? ",Question!,YesNoCancel!,1)
    Choose Case li_flag
    Case 1 //使用者選擇 Yes,儲存資料
    . . . . . . //處理語句
    Case 2 //使用者選擇 No,不儲存
    . . . . . . //處理語句
    Case 3 //使用者選擇了 Cancel,不進行任何操作
    . . . . . . //處理語句
    End Choose
    

六、值傳遞與接收

6.1 字串

① 開啟一個視窗,並傳遞一個字串

string ls_str
ls_str = '個人公眾號:XiezhrSpace'
openwithparm( 視窗名,ls_str)

② 接收字串

string ls_str
ls_str = message.stringparm

6.2 數值

① 開啟一個視窗,並傳遞一個數值

openwithparm( 視窗名,1)

② 接收數值

long ll_row
ll_row = message.doubleparm

6.3 結構體

① 開啟一個視窗,並傳遞一個結構體

st_parameter s_pm
s_pm.a = 1
openwithparm( 視窗名,s_pm)

② 接收結構體

st_parameter s_pm
s_pm = message.powerobjectparm
messagebox('提示',s_pm.a)

6.4 使用者物件

① 開啟一個視窗,並傳遞一個使用者物件

//(1)、建立類使用者物件u_parameter,並定義範例變數
//(2)、給使用者物件賦值,並傳遞該使用者物件
u_parameter u_pm
u_pm.a = '1'
openwithparm(視窗名,u_pm)

② 接收使用者物件

u_parameter u_pm
u_pm = message.powerobjectparm
messagebox('提示',u_pm.a)

6.5 exe應用程式

① 開啟一個exe,並傳遞引數

ls_exe = 'a.exe' + '|值1|' + '|值2|'
run(ls_exe)

② 接收exe傳遞的引數

string ls_parameter
ls_parameter = commandparm()

6.6 傳遞訊息和接收訊息

① 傳遞string訊息

w_main.triggerevent('ue_open',0,'test')
//在使用者自定義事件ue_open中接收訊息:
string ls_msg
ls_msg = string(message.longparm,'address')

② 傳遞long訊息

w_main.triggerevent('ue_open',100,0)
//在使用者自定義事件ue_open中接收訊息:
long ll_msg
ll_msg = message.wordparm

6.7 視覺化類傳值

u_customvisual u_cv
u_cv = create u_customvisual
openuserobjectwithparm(u_cv,1)

6.8 關閉視窗傳遞資料

① 語法

CloseWithReturn ( windowname, Returnvalue )

② 引數

  • windowname 是要關閉的視窗的名稱
  • Returnvalue 要返回的數值

注:只有被關閉的視窗是 response 型別才能有效地獲取返回引數

七、觸發事件

7.1 立即觸發

obj.triggerevent(clicked!)
obj.trigger event ue_init()
obj.trigger function wf_init()

7.2 在事件佇列最後觸發

obj.postevent(clicked!)
obj.post event ue_init()
obj.post function wf_init()

7.3 動態繫結觸發

obj.dynamic event ue_init()
obj.dynamic function wf_init()
注:
動態呼叫和靜態呼叫的區別
靜態呼叫就是在編譯程式碼時就對函數進行徹底編譯
動態呼叫就是在程式執行的時候才回去查詢和呼叫相應的函數

7.4 規定的時間內觸發某事件

idle(60) //如果60秒沒有操作的話就觸發application物件的idle事件
timer(60) //每隔60秒就觸發一次視窗的timer事件

八、視窗使用技巧

8.1 建立視窗範例

① 建立視窗範例

w_edit lws_edit
Open(lws_edit)

② 開啟多個視窗範例

w_edit lws_edit[3]
Int li_index
For li_index =1 To 3
	Open(lws_edit[li_index])
Next

8.2 使用視窗屬性程式設計

根據 tag 的取值對各個控制元件進行初始化

Int li_index,li_total
DataWindow ldw_temp
DropDownListbox lddlb_temp
RadioButton lrb_temp
li_total = Upperbound(Parent.Control[])
For li_index = 1 To li_total
	Choose Case Lower(Parent.Control[li_index].Tag)
	Case "DataWindow"
		ldw_temp = Parent.Control[li_index]
		ldw_temp.Reset()
	Case "DropDownListbox"
        lddlb_temp = Parent.Control[li_index]
        lddlb_temp.SelectItem(0)
	Case "radiobutton"
        lrb_temp = Parent.Control[li_index]
        lrb_temp.Checked = False
	Case Else
	…
	End Choose
Next

8.3 視窗最小化時設定動態圖示

當程式最小化時開啟 timer(在 deactive 中加入 timer(1)語句),並在 timer 事件中編寫

程式啟用時關閉 Timer 事件(在 Active 事件中加入 timer(0)語句)

If This.Icon = "appico.ico" Then
	This.Icon = "reverse.ico"
Else
	This.Icon = "appico.ico"
End If

8.4 放置閃爍文字

以閃爍文字顯示重要資訊可以吸參照戶的注意力,避免這些重要資訊被忽略。通過週期性修改 visible 屬性,可以實現閃爍效果

在視窗中,假設放置一個靜態文字 st_1,在視窗的 Open 事件中定義 Timer 事件的間隔:Timer(1)

Timer 事件 中程式碼如下

If Mod(Second(Now()),2) = 1 Then
	st_1.visible = False
Else
	st_1.Visible = True
End If

8.5 提高視窗的開啟速度

視窗的 Open 事件中經常編寫指令碼來進行初始處理工作,如果這些工作花費的時間比較長,在視窗顯示之前使用者就得等待很長的時間

這時,可以如下指令碼優化

第一種方案:

open事件中新增程式碼

PostEvent("ue_openpost")

ue_openpost 事件中新增程式碼

SetPoInter(HourGlass!)
dw_1.SetTransObject(SQLCA)
dw_1.Retrieve()
SetPoInter(Arrow!)

第二種方案:

open事件中新增如下程式碼

dw_1.SetRedraw(False)
TriggerEvent("ue_openpost")
Dw_1.SetRedraw(True)

8.6 移動不帶標題列的視窗

在開發應用程式中,可能要用到不帶標題列的視窗,而帶有標題列的視窗可以通過拖放
標題列來移動視窗,如何移動沒有標題列視窗呢?

在要拖放視窗的 MouseDown 事件中編寫程式碼,程式碼如下

Send(handle(this),274,61458,0)

8.7 給視窗新增自動卷軸功能

視窗的 Resize 事件中編寫指令碼,根據當前視窗的大小來設定是否顯示卷軸

① 宣告函數

Subroutine GetScrollRange(Uint hWindow,Int nScrollBarFlag,ref Int nMin,ref Int nMax) Library "user.exe"
Function Int GetScrollPos(Uint hWindow,Int nScrollBarFlag) Library "user.exe"

② 定義範例變數

Int ii_width,ii_height

open事件中新增程式碼

ii_width = This.Width
ii_height = This.Height

Resize 事件中實現

Uint hwindow
Int nScrollPos,nMinPos,nMaxPos
If This.WindowState = Minimized! Then //如果正在進行最小化,則直接返回
	Return
End If
HWindow = Handle(This) //獲取當前視窗的控制程式碼
//下面開始處理水平卷軸
If This.Width < i_Width Then //如果小於開啟時的寬度
	This.HscrollBar = True //則顯示水平卷軸
Elseif This.HscrollBar Then //如果大於或等於開啟時的寬度,並且已經有卷軸
    NScrollPos = GetScrollPos(hwindow,0) //使用 API 函數獲取當前卷軸位置
    GetScrollRange(hwindow,0,nMinPos,nMaxPos) //使用 API 函數獲取捲動範圍
    If nScrollPos > nMinPos Then //如果使用者捲動了卷軸並且此時不需要顯示卷軸
    	Post(hwindow,276,6,0) //則在水平方向調整視窗中的內容到原來的位置
    End If
	This.HscrollBar = False //取消卷軸特性
End If
//下面開始處理垂直卷軸
If This.Height < i_Height Then //如果小於開啟時的高度
	This.VscrollBar = True //則顯示垂直卷軸
Elseif This.VscrollBar Then //如果大於或等於開啟時的高度,並且已經有卷軸
    NScrollPos = GetScrollPos(hWindow,1) //使用 API 函數獲取當前卷軸位置
    GetScrollRange(hwindow,1,nMinpos,nMaxPos) //使用 API 函數獲取捲動範圍
    If nScrollPos > nMinPos Then //如果使用者捲動了垂直卷軸且不需再顯示
    	Post(hwindow,277,6,0) //則垂直調整視窗中的內容到原來的位置
    End If
	This.VscrollBar = False //取消卷軸特性
End If

8.8 自動調整視窗

實現視窗居中

//***************************************************************
//* 功能: 將視窗移到螢幕的中央
//* 引數 1: aw_window 要處理的視窗
//* 返回值: (none)
//* 呼叫舉例: gf_window_center(w_pay_mode) //將視窗置於螢幕的中央
//***************************************************************
Environment le_env
Int li_ScreenHeight, li_ScreenWidth
Long ll_posx,ll_posy
GetEnvironment(le_env)
li_ScreenHeight = PixelsToUnits(le_env.ScreenHeight,YPixelsToUnits!)
li_screenwidth = PixelsToUnits(le_env.ScreenWidth,XPixelsToUnits!)
If aw_window.width > li_ScreenWidth Then //如果視窗超寬
	ll_posx = 1
Else
	ll_posx = (li_ScreenWidth - aw_window.Width) / 2
End If
If aw_window.height > li_ScreenHeight Then //如果視窗超高
	ll_posy = 1
Else
	ll_posy = (li_ScreenHeight - aw_window.Height) / 2
End If

aw_window.Move(ll_posx ,ll_posy)

以上就是本期內容的全部,希望對你有所幫助。我們下期再見 (●'◡'●)