윈도우간 여러변수 전달하기
윈도우 오픈시 값을 전달할때 여러 값을 구조체 하나로 공용해서 사용하는 방법
윈도우 오픈시 값을 전달하기 위해서 OpenWithParm()이라는 함수를 사용하게 됩니다
여러값을 전달하고자 할때는 구조체를 정의해서 해당 구조체에 값을 넣어 전달하고, 받는 쪽에선
Message.PowerObjectParm을 이용해서 받지요.
그런데 여러값을 전달하는 윈도우가 많게 되면 구조체도 많이 정의해야 됩니다. 구조체를 많이 정의
하면 프로그램 크기가 커지고 해서 어떤 프로그래머는 구조체에 의한 값 전달보다는 전역변수를 사용
하여 값을 전달하는 방식으로 사용하기도 하죠.
하지만 아래와 같이 활용하면 구조체를 하나만 만들고도 여러값을 전달하는 경우 각 구조가 달라도
서로 공유해서 사용 할 수가 있습니다.
먼저 구조체를 다음과 같이 정의합니다.
Variable Name에는 "s[]" , Type에는 "string" 으로 구조체를 정의합니다.
이렇게 하면 가변적 문자열 배열이 생성되죠.
계속해서 double형 변수 d[], datetime형 변수 dt[] 를 만듭니다.
이것을 str_parms라는 구조체로 저장합니다.
오픈하는 스크립트는 다음과 같이 작성하면 됩니다.
str_parms l_str_parms
//전달에 필요한 값을 정의
l_str_parms.s[1] = "홍길동"
l_str_parms.s[2] = "대풍시스템"
OpenWithParm(w_abc, l_str_parms)
받는 쪽의 스크립트는 다음과 같이 작성합니다.
str_parms l_str_parms
l_str_parms = Message.PowerObjectParm
//받은 값을 가지고 사용
// if l_str_parms.s[1] = "홍길동" then ....
위와 같이 사용하면 전체시스템에서 윈도우간에 여러 값을 전달받을때
구조체 하나만을 이용해서 자유로이 값을 전달받을 수 있게 된다.
─────────────────────────────────────
PowerBuilder [Tip63] row의 순서를 바꿔보자.
dragdrop을 이용하여 row의 순서를 바꾸게 해보겠습니다.
데이타 윈도우에서 row를 선택하면 화살표가 나타나게 합니다.
이부분은 timer event를 이용합니다.
//window의 timer event
if ib_mousedown then
dw_1.Drag(Begin!)
end if
Timer(0)
//delcare instance variable
boolean ib_mousedown = False
//declare userevent
pbm_dwnlbuttonup을 이용하여 ue_lbuttonup event를 만든다.
//dw_1의 ue_lbuttonup event
ib_mousedown = False
//dw_1의 clicked event
if row = 0 then Return
this.SelectRow(0, False)
this.SelectRow(row, True)
ib_mousedown = true
Timer(0.2)
//dw_1의 dragdrop event
long ll_selrow, ll_newrow
stringls_row
IF DraggedObject() = this THEN
parent.SetRedraw(false)
ll_selrow = this.GetSelectedRow(0)
ls_row = this.GetObjectAtPointer()
if ls_row = '' then
ll_newrow = this.RowCount() + 1
else
ll_newrow = Long(Mid(ls_row, Pos(ls_row, "~t") + 1))
end if
this.RowsMove(ll_selrow, ll_selrow, Primary!, this, ll_newrow, Primary!)
this.SelectRow(0, False)
if ll_newrow > ll_selrow then ll_newrow --
this.SelectRow(ll_newrow, True)
parent.SetRedraw(true)
END IF
─────────────────────────────────────
선택된 ROW를 입체적으로 보이게...
이 함수는 script에서 사용하는 함수가 아니라 datawindow에서 사용하는 함수입니다.
이함수는 focus가 어떤 row에 위치했을때 그 row number를 넘겨줍니다.
이번팁은 currentrow를 이용해서 현재의 row를 선택했을때 3d효과를 보여주게 만들어주는 것입니다.
우린 현재 row를 선택할때 datawindow의 rowfocuschanged event에
dw_1.SelectRow(0, False)
dw_1.SelectRow(currentrow,True)
를 써서 파란색 바를 나타나게 하죠~
아니면
dw_1.SetRowFocusIndicator(Hand!)
를 사용해서 손가락 모양으로 현재의 row를 나타나게 하든지...
3D효과를 한번 나타나게 해보자구요~ 자 DataWindow로 가보세요~ 그리고 칼럼을 선택했죠?
좀 귀찮긴 하지만 각각의 칼럼위에 TEXT OBJECT를 하나씩 가져다 놔야해요.
물론 TEXT는 없게 하구요.
그리고 각 칼럼의 위에 있는 TEXT OBJEC의 BORDER TYPE을 3D-Raised로 만들구요..
그리고 각각의 text object의 property에서 expresstions tab으로 들어갑니다.
visible 항목이 보이죠? 그 항목에 If(CurrentRow( ) = GetRow( ), 1, 0)
라고 적습니다.
───────────────────────────────────────
datawindow----->html table로
datawindow를 html(hyper text makeup language) table형식으로
파워빌더 5.0에서는 기존에 4.0 까지 지원되지 않았던 html포멧이 지원되죠.
우린 기존에 excel이나 txt형식으로는 export를 많이 했었죠.
하지만 html format이 있는걸 보신분은 많이 없으실거예요.
자 datawidow로 가보세요.
칼럼을 선택하고 preview를 하세요.
File menu의 save rows as..를 선택하시구~
file 형식을 HTML Table을 선택하시구 이름을 주세요~
그럼...xxxx.html이라는 화일이 생성이 됩니다.
그리구 웹브라우저에서 이 화일을 열어보시면..
데이타들이 html의 테이블로 구성이 되어 나옵니다~~!
───────────────────────────────────────
유도라를 이용한 email 전송
유도라는 인터넷에서 email을 받고 보내는 가장 보편적인 프로그램이지요.
유도라 pro2.2를 가지고 설명합니다.
먼저 유도라의 옵션을 다음과 같이 맞춰놔야 합니다.
옵션에서 왼쪽의 MAPI를 선택하고 "Use Eudora MAPI Server"에서 "Always"를 선택.
메시지를 전송하는 함수는 다음과 같습니다.
매개변수:a_s_recipient, a_s_subject, a_s_notetext.
리턴값 : boolean (함수성공여부)
mailSession m_mail_session
mailReturnCode m
mailMessage m_message
m_mail_session = CREATE mailSession
m = m_mail_session.MailLogon()
IF m <> mailReturnSuccess! THEN
DESTROY m_mail_session
MessageBox('Mail', 'Could not connect to the mail program. Make sure the mail program is
running.', StopSign!)
return FALSE
END IF
// Populate the mailMessage structure
m_message.Recipient[1].name = a_s_recipient
m_message.Subject = a_s_subject
m_message.NoteText = a_s_notetext
// create the mail message
m = m_mail_session.mailSend(m_message)
IF m <> mailReturnSuccess! THEN
m_mail_session.MailLogoff()
DESTROY m_mail_session
MessageBox('Mail', 'There was an error sending the mail message.', StopSign!)
return FALSE
END IF
m_mail_session.MailLogoff()
DESTROY m_mail_session
return TRUE // success
───────────────────────────────────────
More Speed From Oracle
More Speed From Oracle
SQL문장을 Oracle에 보낼때 파빌은 모든 변수값을 다음과같이 치환시킨다.
String ls_Ken = "Ken%"
SELECT * FROM employee WHERE name LIKE :ls_Ken;
변수값 치환
SELECT * FROM employee WHERE name LIKE 'Ken%';
Oracle로 보낸다.
SQL이 Oracle에서 실행될때 몇가지 단계를 거치게되는데 그중 한 단계가 explain plan 단계다.
oracle parses가 SQL을 분석하여 최적의 검색경로를 찾는 과정으로써 SQL실생시간의 약 25%를 차지한
다.
oracle은 보다 빠른 수행속도를 얻기위해 cache를 갖고있다. 즉 똑같은 SQL 질의가 있을경우 SQL
parse와 explain plan설정의 과정을 생략하고 cache에 있는 내용을 재사용하게 된다. 따라서 똑같은
SQL문장을 사용할경우 25%의 수행속도 상승효과를 볼 수 있다.
하지만 똑같은 SQL문장이라 할지라도 ls_ken의 내용이 다를경우 oracle은 같은 SQL문장으로 인식하지
않는다. 따라서 Explain plan을 산출하는 과정을 거치게 된다.
Explain plan과정을 생략하고자 한다면...즉 binding되는 변수가 일정하다
면 변수의 값을 SQL문장에 치환시키지말고 변수관련 정보를 Oracle에 보내도록한다. 이럴경우 변수의
값이 다를지라도 oracle은 cache에 있는 Explain plan을 재사용하게된다.
파빌이 변수치환과정을 거치지 않게 하기위한 방법은 pb.ini내용에 다음 문장을 추가하면된다.
StaticBind = 0
또는
StaticBind = 'No'
Script에서 기술할경우
SQLCA.dbParm = "StaticBind = 0"
또는
SQLCA.dbParm = "StaticBind = 'No'"
이렇게 하면 파워빌더는 변수값까지 치환한 SQL문장을 만들지 않고 binding변수 정보를 oracle에 보
내게 된다. 그렇게 함으로써 cache에 있는 Explain plan정보 재사용율을 높일 수 있다.
───────────────────────────────────────
title로 sort를
datawindow를 만들면 COLUME NAME은 있지만 칼럼의 HEADER부분에 그칼럼의
LABEL들은 이름이 없죠.
DEFAULT로 <NO NAME>이렇게 잡혀 있어요.
그부분에 COL_TEXT_1, COL_TEXT_2,COL_TEXT_3이라고 이름을 주겠습니다.
그리고 dw_1의 click event에 다음과 같이 적어보세요~
CHOOSE CASE dwo.name
CASE "col_text_1"
dw_1.SetSort("col_1 A")
dw_1.Sort()
CASE "col_text_2"
dw_1.SetSort("col_2 A")
dw_1.Sort()
CASE "col_text_3"
dw_1.SetSort("col_3 A")
dw_1.Sort()
END CHOOSE
그러면 header부분을 눌렀을때..선택된 칼럼을 키값으로 sort가 되겠죠?
저는 microhelp를 적용했지요. 어떤내용의 칼럼인지를 설명했어요~
CHOOSE CASE dwo.name
CASE "col_text_1"
w_mdi.SetMicroHelp("c = a + b")
CASE "col_text_2"
w_mdi.SetMicroHelp("d = a / b")
CASE "col_text_3"
w_mdi.SetMicroHelp("d = a * b")
CASE ELSE
w_mdi.SetMicroHelp(" ")
END CHOOSE
───────────────────────────────────────
누적치 구하기
DataWindow에서 우리가 어떤 값의 누적치를 구하고 싶을때 간단하게 함수 하나만 사용을 해주면 됩니
다.
어떤것이냐..바로 CumulativeSum입니다.
다음과 같은 레포트로 출력을 하고 싶습니다.
Quantity Running Total
5,000 5,000
2,500 7,500
3,000 10,500
12,000 22,500
제가 구하고 싶은것은 quantity의 running total이죠.
그럼 running total은 다음과 같은 컴퓨트 필드를 하나 만들어보세요.
cumulativeSum( quantity for all )
───────────────────────────────────────
하나의 윈도우를 여러번 오픈~!...
이번팁은 하나의 윈도우를 여러개처럼 사용하기 위한 것입니다.
같은 스타일의 윈도우는 하나만 만들면 여러번 오픈할수 있습니다.
그만큼..어플리케이션의 부피도 줄일수 있는거죠.
w_comm_sheet란 윈도우를 하나 만들어 봅시다~. 윈도우의 스타일은 그냥 main윈도우로 하면 되구요.
//w_mdi의 m_mdi.new 메뉴의 script
w_comm_sheetlw_sheet //이부분이 하나의 윈도우를 여러개처럼
사용하기 위해 선언하는 부분이다.
OpenSheet (lw_sheet, w_main, 0, layered!)
이랬을때 new메뉴를 선택하면 같은 윈도우가 여러개 열리게 됩니다~
자 이렇게 된경우엔 너무 밋밋하죠.
윈도우의 title에 serial number를 준다면 조금 세련되어 보일라나?
//w_comm_sheet의 open event에 다음과 같이 써보세요~
Window lw_count_sheet
Integerli_count
lw_count_sheet = w_main.GetFirstSheet ()
If IsValid (lw_count_sheet) Then
Do
li_count += 1
lw_count_sheet = w_main.GetNextSheet (lw_count_sheet)
Loop While IsValid (lw_count_sheet)
End If
This.Title = "Sheet #" + String(li_count)
───────────────────────────────────────
여러 Display value를 갖는 DDDW...
DropDownDataWindow(DDDW)를 사용하면 코드의 입력시 편하긴 한데...
코드와 코드명을 동시에 보여줄려고 하면 dddw로는 불가능해 보이죠.
하지만 dddw로 가능합니다. 다음과 같이.....
dddw로 사용하려는 datawindow design시에 보여주려는 두개의 필드를
compute field로 만드는 겁니다. 정말 간단하죠.
───────────────────────────────────────
pb5.0에서 toolbar의 disable
파워빌더 5.0에서 사용중에
갑자기 툴바중에서 몇개가 disable 되는 현상이 있더군요.
요거때문에 파워빌더 죽이고 다시실행시키지 마시고 툴바에서 오른쪽마우스 클릭을 해서 나오는 메뉴
에서 customize을 선택하면 오픈되는 customize 윈도우에서 custom 선택하고 OK버튼을 클릭.
───────────────────────────────────────
DataWindow, refer to data
DataWindow에 product_id라는 column이 있다.
product_id값을 검사하여 product_id값이 변경되면 글자색도 변경하고자 한다.
이런 경우 현재 row의 product_id값과 다음 row의 product_id값을 비교할 수
있다면 간단해진다.
DataWindow에서 각각의 column은 마치 배열처럼 취급할 수 있다.
즉 row는 column의 첨자라고 볼 수 있다.
이러한 특징은 script 작성시에 많이 느낄 수 있을 것이다.
그러나 DataWindow field에선 좀 다르다.
현재 row는 0이고 그 다음 row는 -1, -2, -3으로 나간다.
그 전 row는 1,2,3,4.. 가 된다.
따라서 위의 문제가 아주 간단히 해결된다.
if ( product_id = product_id[-1], rgb(0, 0, 0), rgb(255, 0, 0) )
이러한 식을 product_id 의 color property에 기술하면 간단히 해결된다.
-
http://www.magicnet.net/purepower/puretips/index.html -
───────────────────────────────────────
DDDW Trick
Parent DataWindow가 retrieve되는 시점에 DropDown DataWindow(dddw)의 모든 내용이 retrieve된다.
어떤 사람이 parent datawindow의 clicked event에서 dddw.retrieve()를 하면된다는 그러한 솔루션을
제공한적이 있었다.그것이 틀리다고 볼수는 없다.
하지만 문제가 있다.
clicked event는 user가 dropdown 화살표를 click하던 일반 text column을 click하던간에 event가 발
생된다는 것이다. 만일 click하는 지점이 일반문자(dddw의 화살표 옆에 있는 문자말고...)라면
program은 dddw.retrieve()하지말아야 한다. 따라서 그 방법은 이들간의 click 지점이 어딘지를 구분
해야하는 script가 필요하다.
GetObjectAtPointer()와 GetClickedColumn()함수는 dddw가 click되어졌는지 아닌지를 알려주진 못한
다.
따라서 몇가지 내부적인 Hacking에 의해 올바른 event는 pbm_dwndropdown이라는 결론에 이르게 되었
다.
그래서 solution은 다음과 같다.
user event ue_dddw_dropdown을 만들자(pbm_dwndropdown을 mapping)
그리고 다음과 같은 script를 작성한다.
1) DataWindowChild dwc
dwGetChild(GetColumnName(), dwc)
dwc.SetTransObject(SQLCA)
dwc.retrieve()
위의 것은 기본적인 기능을 구현한 것이고 다음은 여러 개선사항을 첨가한 예다.
2) dwGetChild(GetColumnName(), dwc)
// 만약 dwc.rowcount()>1 이라면 그러면 dddw는 이미 한번
// drop down했다는 것이다. 따라서 귀찮게 다시 retrieve() 할필요없다.
// 또하나 주의할것이 있다. dddw는 하나의 단일 row 즉 blank인
// 단일 row를 기본적으로 갖는다.
if dwc.rowcount() > 1 then return
dwc.SetTransObject(SQLCA)
dwc.retrieve()
3) ... // same as that in (1).
dwc.retrieve(GetText())
dddw의 SQL문장:
select country_name from contry_lookup
where country_name like :retrieve_argument + "%"
위의 것은 만약 user가 'ca'를 입력해서 dropdown 화살표를 click하면 국가이름 의 목록이 아주 보기
좋게 drop down될것이다.
cambodia
cameroon
canada
하지만 france나 sussia같은 국가명은 retrieve되지 않는다. 왜냐하면 SQL문장의 like구문에 의해 ca
로 시작하는 것만 retrieve하기 때문이다.
이건 retrieve량을 줄이게되어 메모리 절약등의 효과도 있게된다.
그런데 만일 dddw가 employee_name이고 department field가 있다고 생각한다면...
회사의 사원은 상당히 많다 따라서 department에 'Information Service'라고 입력한다. 그리고
employee_name field로 이동하여 dddw의 화살표를 click한면 dddw는 오직 Information Service에서
일하는 사원만을 retrieve하게된다.
... // same as that in (1)
dwc.retrieve(GetItemString(GetRow(), "department"))
dddw의 SQL문장:
select employee_name from employee_table
where department = :retrieve_argument
여기까지의 설명에서 주 요점 사항으로 기억할것은 pbm_dwndropdown event를 이해하고 사용하는 것이
다. 이건 undocumented event다.
───────────────────────────────────────
DW에서 like사용시 null컬럼나오...
table test 의 컬럼이
empno,name,deptname,address,phone
(사번,이름,부서명,주소,전화번호)
라고 할때 datawindow를 디자인할때 주소로 검색할 경우
retrieve argument를 r_address 라고 할때
select empno,name,deptname,address,phone
from test
where address = :r_address;
로 정의한다.
그런데, 전체를 검색하기 위해 r_address 의 값을 '%'로 주었을 경우
address 컬럼이 null인 경우는 가져오지 못한다.
이럴 경우 다음과 같이 한다.
select empno,name,deptname,address,phone
from test
where ((address = :r_address) or (address is null and :r_address = '%'))
───────────────────────────────────────
DW list에서 현재 row의 표시
datawindow로 만든 리스트에서 현재 row를 표시하는 방법은 여러가지가 있지요.
가장 많이 쓰는 방법은 RowFocusChanged 이벤트에서
Selectrow(0,false)
SelectRow(GetRow(),true)
라고 적는 방법이 있죠.
위와 같이 하면 키보드의 위화살표,아래화살표로 리스트를 이동할 때 현재 row가 반전되서 나옵니다.
그런데 dw로 만든 list에서 중복선택할 경우는 현재 row를 표현한다는게 어렵지요.
선택한 row를 반전시켜야 하는데, 현재 row를 반전시킬 수도 없고....
이럴때
dw_employee.SetRowFocusIndicator() 를 사용하는 것입니다.
dw_employee.SetRowFocusIndicator(FocusRect!)라고 미리 정해 놓으면
현재 row는 점선사각형으로 표현되죠. 그리고 선택한 row는 반전시키면 되고.
───────────────────────────────────────
DropDownListBox의 Reset
DropDownListBox 가 처음 선택하지 않은 상태로 하려면?
Allow Editing 이 가능할 경우에는 ddlb_1.text = "" 로 하면 초기화가 되지만
Allow Editing 이 불가능할 경우에는 ddlb_1.text = "" 로 해도 초기화가 되지 않는다.
이럴 경우에는
SelectItem() 함수를 사용한다.
ddlb_1.SelectItem(0) 하면 초기에 선택되지 않은 상태로 초기화 할 수 있다.
───────────────────────────────────────
Powersoft사의 21세기 date
PowerBuilder 5.0과 InfoMaker 5.0이외에도 다음과 같이 Powersoft사가 이전에 발표한 버전의 제품에
서도 21세기를 대비한 Date가 지원된다.
PowerBuilder 3.0 이상의 PowerBuilder 제품 InfoMaker 4.0 이상의 InfoMaker 제품이 PowerBuilder과
동일하게 지원한다. PowerMaker 3.0과 PowerViewer 3.0에서는 PowerBuilder 3.0과 동일하게 Date를
지원한다.
Date형과 DateTime형의 데이터는 입력과 디스플레이 용도의 모든 형식으로 지원되며 Date의 진행도
적확히 파악되어 2000년 이상의 데이터까지도 표현 가능하다.
Date는 PowerBuilder내에서 Format과 Edit Mask 등 , 여러 형식으로 표현할 수 있다. 특히 Date는 MM
, DD , YY를 1에서 4까지의 자릿수를 이용하여 4자리 , 6자리 , 8자리 등의 어떠한 조합으로도 구성
할 수 있다.
이것은 개발자가 어플리케이션에 대해서 사전에 결정해야 할 사항이다.
* 2자리의 년도
년도가 2자리로 표시되면 PowerBuilder와 InfoMaker는 다음과 같이 세기를 선택한다
구간 PowerBuilder와 InfoMaker의 가정
00 과 49사이 첫 두자리를 20으로 가정
50 과 99사이 첫 두자리를 19으로 가정
만일 출생일 등의 1950년 이전의 데이터가 있다면 항상 그것을 4자리의 년도로 정의하여
PowerBuilder와 InfoMaker가 올바르게 인식할 수 있도록 유의해야 한다.
* Date의 유효 범위
PowerBuilder와 InfoMaker는 1000 ~ 3000년의 년도를 지원한다.
* 윤년의 처리
PowerBuilder와 InfoMaker가 윤년에 대해서 사용하는 알고리즘은 다음과 같다
년도는 4로 나뉘어져야 하며 만약 100으로도 나뉘어진다면 400으로도 나뉘어져야 한다.
그러므로 1900년은 윤년이 아니고 2000년은 윤년이다.
* S-Designor의 경우
S-Designor의 경우 Date의 처리는 상관없다.
Date의 형식은 개발자가 Domain과 Data Element를 모두 정의하므로 원하는 형태로 표시된다.
툴에 의해 취급되는 데이터는 없다.
───────────────────────────────────────
Dynamic DW Object and Compile...
DataWindow control 이나 DataStore의 Object을 실행시에 Dynamic하게 변경시킬 수 있다.
이런 프로그램은 실행파일을 만들때 PBR(PowerBuilder Resource) file에 DataWindow Object name을
기술해줘야 한다.
DW Object을 dynamic하게 변동시키면 PowerBuilder에서는 문제없이 잘 실행되지만 실행파일을 만들어
보면 에러가 발생한다.
다음과 같이 dynamic하게 하는 경우는 반드시 PBR file을 작성해야한다.
dw_1.dataobject = 'd_employee_maint'
PBR file이 필요한 이유는 DataWindow object 이름이 인용부호로 둘러싸여 있기 때문에 compiler가
실행파일 만들때 DataWindow object이라고 인식하지 못한다.
따라서 DataWindow Object으로 포함시키지 않게된다.
그렇기 때문에 반드시 PBR file에 다음과 같은 내용을 기술해주어야한다.
employee.pbl(d_employee_maint) PBR file은 매모장같은 곳에서 작성하여 .pbr 확장자로 저장시키면
된다.
그런다음에 실행파일을 만드는 Project painter에서 PBR file 이름을 포함시켜 주면 된다.
───────────────────────────────────────
Change the DW Error Message .....
Change the DW Error Message Title
DataWindow에서 값을 입력하다보면 'DataWinodw Error'라는 Title의 Message Box가 나타납니다.
영어로 쓰여져있고 일반 End User들에게는 생소한 영문이라서 눈에 거슬렸는데 이 title을 원하시는
내용으로 변경하는 방법이 있습니다. DataWindow attribute에는 Message.Title이란 것이 있습니다.
이부분을 수정하면 됩니다.
dw_1.Modify("DataWindow.Mesage.Title = '입력오류'")
───────────────────────────────────────
어플리케이션의 환경
IF env.machinecode THEN //어플리케이션의 실행코드
sle_1.Text = "Machine Code"
ELSE
sle_1.Text = "P-Code"
END IF
st_colors.text =string(env.numberofcolors) //화면의 색상
st_height.text = string(env.screenheight) //화면의 높이
st_width.text = string(env.screenwidth) //화면의 너비
───────────────────────────────────────
파워빌더의 버전은???
/* Declare */
Environment env
INTEGER resp
/* Variable Setting */
resp = GetEnvironment(env) //시스템 환경을 읽어오는 함수
CHOOSE CASE env.pbtype //현재 인스톨된 파워빌더의 type
CASE enterprise!
sle_1.Text = 'Enterprise'
CASE desktop!
sle_1.Text = 'Desktop'
END CHOOSE
IF env.win16 THEN
sle_2.Text = Trim(sle_1.Text) + "/16" + ' ' + STRING(env.pbmajorrevision) + '.' + &
STRING(env.pbminorrevision) + '.' + STRING(env.pbfixesrevision)
ELSE
sle_2.Text = Trim(sle_1.Text) + "/32" + ' ' + STRING(env.pbmajorrevision) + '.' + &
STRING(env.pbminorrevision) + '.' + STRING(env.pbfixesrevision)
END IF
───────────────────────────────────────
시스템환경을 체크해보자(os)
/* Declare */
Environment env
INTEGER resp
/* Variable Setting */
resp = GetEnvironment(env) //시스템 환경을 읽어오는 함수
CHOOSE CASE env.OSType //OS TYPE(파워빌더는 7개 인식)
CASE aix!
sle_1.Text = 'AIX'
CASE hpux!
sle_1.Text = 'HPUX'
CASE macintosh!
sle_1.Text = 'MacIntosh'
CASE osf1!
sle_1.Text = 'OSF1'
CASE sol2!
sle_1.Text = 'Solaris 2'
CASE Windows!
sle_1.Text = 'Windows'
CASE Windowsnt!
sle_1.Text = 'Windows NT'
END CHOOSE
sle_2.Text = Trim(Sle_1.Text) + ' ' + STRING(env.osmajorrevision) + &
'.' + STRING(env.osminorrevision) + &
'.' + STRING(env.osfixesrevision)
───────────────────────────────────────
시스템환경을 체크해보자(cpu)
/* Declare */
Environment env
INTEGER resp
/* Variable Setting */
resp = GetEnvironment(env) //시스템 환경을 읽어오는 함수
CHOOSE CASE env.cputype //CPU의 TYPE(파워빌더는 14개 인식)
CASE alpha!
sle_1.text = 'Alpha'
CASE hppa!
sle_1.text = 'HPPA'
CASE i286!
sle_1.text = '286'
CASE i386!
sle_1.text = '386'
CASE i486!
sle_1.text = '486'
CASE m68000!
sle_1.text = '68000'
CASE m68020!
sle_1.text = '68020'
CASE m68030!
sle_1.text = '68030'
CASE m68040!
sle_1.text = '68040'
CASE mips!
sle_1.text = 'MIPS'
CASE pentium!
sle_1.text = 'Pentium'
CASE powerpc!
sle_1.text = 'PowerPC'
CASE rs6000!
sle_1.text = 'RS6000'
CASE sparc!
sle_1.text = 'Sparc'
END CHOOSE
───────────────────────────────────────
Null 값 핸들링시에
이번 Tip은 DataWindow Painter에서 NULL값을 처리하는 작은 트릭입니다.
일반적으로 '급여:' + String(salary) 라는 computed field를 사용할 수 있겠는데 만약 salary
column의 값이 NULL인 경우 field또한 NULL값을 취하게 되어 아무런 값도 나타나지 않게 됩니다.
때문에 대개의 경우 IF문을 사용하지만 여기서는 String함수를 그대로 이용하는 방법을 소개합니다.
String함수의 mask는 number를 보기좋게 변환시켜주는 역할을 합니다.
mask는 number의 4가지 양태를 각각 지정할 수 있습니다.
즉 음수,양수,0,NULL 각각에 해당하는 mask를 semicolon으로 구분하여 별도로 지정합니다.
생략하는 경우 맨앞에 나타나는 mask로 처리됩니다.
'급여:' + string(salary, "#,##0; ; ; '(unknown)'")
위와같이 computed field를 사용하면 4번째 mask가 NULL인 경우에 적용됩니다.
즉 NULL인경우 '급여:(unknown)'이라고 나타나게 됩니다. 2번째,3번째는 생략되었기 때문에 1번째
mask 적용을 받게 됩니다.
───────────────────────────────────────
파워빌더에서 전화걸기
Declare 밑에
Local External Function에다가 다음과 같이 쓰시고
//////////////////////////////
Function int OpenComm (string lpComName, uint wInQueue, uint wOutQueue) Library "user.exe"
Function int CloseComm (int nCid) Library "user.exe"
Function int WriteComm (int nCid, string lpBuf, int nsize) Library "user.exe"
Function int FlushComm (int nCid, int nQueue) Library "user.exe"
//////////////////////////////
int li_comid
int li_rc
String ls_port // Poer No.
String ls_buffer /* 전화 번호(에를 들어 Pre Dial 이 있다면
9,01410 이런식으로*/
li_comid = OpenComm(ls_port,128,128)
If li_comid < 0 Then
If li_comid = -2 Then
Messagebox("Port: "+ ls_port + " In - Use","Error")
Else
Messagebox("Port: " + ls_port, "Error")
End If
Return -1
End If
Flushcomm(li_comid,0)
Flushcomm(li_comid,1)
li_rc = WriteComm(li_comid, ls_buffer, len(ls_buffer))
MessageBox("Dialing " + Left(ls_buffer,len(ls_buffer) - 2),"Hit Ok to hang up
modem")
CloseComm(li_comid)
return 1
───────────────────────────────────────
내방식대로의 마이크로 헬프 #2...
/* uf_set_clock */
/* scope : public, parameters :없음 return 값 : 없음 */
/************************************************************************/
st_login_time.text = string(gd_logindt, "mm/dd") + " " + string(gd_logindt, "h:mm:ss")
st_clock.text = string(today(), "mm/dd") + " " + string(now(), "h:mm:ss")
/************************************************************************/
/* uf_set_msg */
/* scope : public, parameters :as_msg(string) return 값 : 없음 */
/************************************************************************/
st_msg.text = '' + as_msg
/************************************************************************/
/* window의 Open Event */
/************************************************************************/
//Window의 Open Script
int x,y
uo_msg.uf_init(w_main.true) //user object를 초기화한다.
timer(60) //1분마다 timer를 발생시킨다.
x = this.workspacewidth()
y = this.workspaceheight() - 1
mdi_1.move(1,1) //work space를 초기 위치로 설정한다.
// window의 Timer Event
/*1분마다 user object에 시각을 나타낸다. */
uo_msg.uf_set_clock()
/* scope :public, parameters : s_color('R':Red(에러,데이타가 없음,경고메시지)
/* 'B':Blue(진행중임을 나타내는 메시지)
/* 'N':Black(성공,ok,information..)
/* return 값 : 1:ok, -1 :error or bad arg
/******************************************************************************/
IF len(s_msg) < 1 THEN RETURN -1
CHOOSE CASE s_color
CASE 'R'
w_main.uo_msg.st_msg.textcolor = 255
w_main.uo_msg.uf_set_msg(s_msg)
CASE 'B'
w_main.uo_msg.st_msg.textcolor = 167
w_main.uo_msg.uf_set_msg(s_msg)
CASE 'B'
w_main.uo_msg.st_msg.textcolor = RGB(0,0,0)
w_main.uo_msg.uf_set_msg(s_msg)
CASE ELSE
RETURN -1
END CHOOSE
RETURN 1
───────────────────────────────────────
내방식대로의 마이크로 헬프 #1...
1) userobject내의 instance 변수를 정의한다.
Window iw_parent_window
Integer ii_menu_ht = 0
Boolean ib_show_clock
Integer ii_resizeable_offset
2) userobject용 함수만들고 저장하기
uf_init(window,boolean): 오브젝트가 놓여 있는 윈도우의 open event에서
호출되어야 한다. 첫번째 변수는 윈도우를 등록하고
두번째는 object에 시각을 나타낼 것인지 아닌지를
결정한다.이 함수는 오직 한번만 호출된다.
uf_resize() : 모 윈도우의 Resize Event에서 호출된다. 이것은 모
윈도우의 크기나,바뀔때 나타낼 크기와 위치를 변경한다.
uf_set_clock() : 모 윈도우의 Timer Event 에서 호출된다.
이것은 현재의 시각을 표시한다.
uf_set_msg(string) : Object Bar의 왼쪽부터 해당 메시지를 표시한다.
Null string은 메시지를 지운다.
/************************************************************************/
/* uf_init */
/* scope : public, parameters : aw_win(window), ab_clock_on(boolean) */
/* return값 : 없음 */
/************************************************************************/
iw_parent_window = aw_win
//만약 메뉴가 있다면 메뉴의 크기만큼 보장하기 위해서
IF len(iw_parent_window.menuname) > 0 THEN
ii_menu_ht = 175
ELSE
ii_menu_ht = 98
END IF
//해당 윈도우가 Resize속성일때..
IF aw_win.resizable THEN
ii_resizeable_offset = 0
ELSE
ii_resizeable_offset = 16
END IF
//시각을 표시하기 원하면 초기 시간을 SET한다.
ib_show_clock = ab_clock_on
IF not ib_show_clock THEN
HIDE(st_clock)
ELSE
uf_set_clock()
END IF
//윈도우의 속성에 맞게 크기를 조정한다. 그리고 사용자를 표시한다.
uf_resized()
st_user.text = '' + gs_userid
/************************************************************************/
/* uf_resized */
/* scope : public, parameters :없음 return 값 : 없음 */
/************************************************************************/
//uf_init()함수가 실행되지 않았을때이다.
IF ii_memi_ht = 0 THEN RETURN
//user object 의 크기나 위치를 변경할 때 성능을 위하여 hide시킨다.
Hide(This)
//모 윈도우의 크기대로 user object의 크기를 변경한다.
This.Width = iw_parent_window.Width
//각 Component의 크기를 변경한다.
st_clock.x = iw_parent_window.Width - (st_clock.Width + 38)
st_login_time.x = st_clock.x - (st_login_time.Width + 12)
st_msg.Width = st_login_time.x - (st_msg.x + 12)
//모 윈도우의 맨 하단으로 오브젝트를 이동한다.
Move{(This, 1, iw_parent_window.height - (this.height + ii_menu_ht) + &
ii_resizeable_offset)}
Show(This)
───────────────────────────────────────
MDI frame 작업공간
하나 이상의 컨트롤을 갖는 mdi frame을 custom mdi라고 한다.
MDI frame에 컨트롤을 추가하면 작업 공간을 바꾸어야 한다.
프레임에서 모든 컨트롤들은 일관된 이미지를 주기 위하여 위치와 크기가 정해져야 한다.
따라서 custom MDI frame을만들때는 frame의 resize event에서 mdi_1의 추가된 컨트롤의 위치와 크기
를 바꾸어 준다.
처음 custom MDI frame에서 MDI_1의 높이와 넓이는 0이다. 만약 작업 공간이 없으면 Sheets는 열리지
만 보이지 않으며, 작업 공간보다 Sheet가 더 크면 잘라져 버린다.
다음의 예처럼 마이크로 헬프의 기능을 대체하기 위해 userobject를 만든다고 하면, mdi의 크기가 조
정이 되어야 할 것이다.
1) 먼저 작업공간(mdi_1)의 넓이와 높이를 얻는다.
int li_width, li_height
li_width = w_genapp_frame.WorkSpaceWidth()
li_height = w_genapp_frame.WorkSpaceHeight()
2) 새로 추가한 control을 제외한 새로운 작업 공간(mdi_1)의 크기를 계산한다.
li_height = li_height - (cb_print.y + cb_print.height)
li_height = li_height - MDI_1.MicroHelpHeight
li_height = li_height + WorkSpaceY()
3) 추가한 Control의 위치를 이동시킨다.
mdi_1.Move (WorkSpaceX(), cb_print.y + cb_print.height)
4) 작업 공간(mdi_1)을 변경한다.
mdi_1.Resixe(li_width, li_height)
───────────────────────────────────────
AVI 화일 보기
두개의 API함수를 사용해야 합니다.
micSendStringA()함수, 연주장치에 명령을 내보내는 역할을 합니다.
mdiGetErrorStringA()함수, 처리과정중에 관계된 에러를 보고합니다.
역시 API함수를 사용하기 위해서는 Declare Menu에서 Local External Function을 선언해야 합니다.
Function UINT LoadLibraryA(String as_library) Library "kernel32"
Subroutine FreeLibrary(UINT HInstance) Library "kernel32"
Function Boolean mciGetErrorStringA(long errorStr, ref string buffer,
int wlength) Library "WINMM.dll"
Function Long mciSendStringA(ref string command, ref string returnstring,
int wlength, UINT wcallback) Library "WINMM.dll"
Script는 좀 깁니다.
string ls_command, ls_buffer, ls_file, ls_path, ls_driver
long ll_error
UINT lu_lib
//멀티미디어 Library가 install되어 있는지를 loadlibraryA함수로 확인 합니다.
IF lu_lib <> 0 Then
MessageBox("오류",'멀티미디어 디바이스 라이브러리가 없습니다.', StopSign!)
Else
FreeLibrary(lu_lib)
//AVIVideo device에 대한 registry를 찾는다 RegistryGet()함수가 0을
//return하면 video playback device를 갖고 있는것이고 -1을 return
//하면 컴퓨터에 비디오 실행을 위한 장치가 구성되어 있지 않다는 의미.
ls_buffer = "HKEY_LOCAL_MACHINE₩System₩CurrentControlSet₩Control" + &
"₩MediaResources₩MCI₩AVIVideo"
ll_error = RegistryGet(ls_buffer,"Driver",ls_driver
IF ll_error = -1 Then
MessageBox('오류','설치된 비디오 드라이브가 없습니다.', StopSign!)
Else
//device를 open하기 위해 command string을 작성한다.
//open하기 위한 file과 AVIVedeo Type, alias를 지정한다.
//alias는 후에 멀티미디어 함수 호출때 사용하기 위한것이므로 아무
//렇게나 주어도 상관없다.
ls_buffer = Fill(char(0),255)
ls_command = "open music.avi type avivideo alias cartoon"
ll_error = mciSendStringA(ls_command, ls_buffer, 255, &
Handle(Parent))
//device를 open하는 동안 Error가 발생하였으면 ErrorMessage를 얻어서 보여준다.
//정상적으로 open되면 mcisendStringA()함수에게 alias를 사용해서 file을 실행하라고 알려준다.
If ll_error <> 0 Then
mciGetErrorStringA(ll_error, ls_buffer, 255)
MessageBox('오류', ls_buffer)
Else
//연주가 끝났는지를 확인하기 위해서 Yield()함수를 사용한다.
Yield()
ls_command = 'play cartoon notify'
mciSendStringA(ls_command, ls_buffer,255, &
Handle(Parent))
Yield()
End If
End If
End If
window에서 pbm_mmmcinotify event ID를 이용 User Event를 만듭니다.
비디오 파일의 연주가 끝나면 이 이벤트가 구동하게 됩니다.
stript는...
String ls_command, ls_buffer
//device를 닫는다.
ls_command = 'close cartoon'
mciSendStringA(ls_command, ls_buffer, 255, Handle(this))
Yield()
───────────────────────────────────────
WAV 화일 불러오기
waveform audio file을 연주하기 위해선 API함수를 사용해야 합니다.
이 함수는 SndPlaySoundA()함수입니다.
두개의 Parameter를 취하는데 하나는 .WAV file name 또하나는 연주하는 방법.
먼저 API함수를 사용하기 위해서는 Declare Menu에서 Local External Function을 선언해야 합니다.
Function Boolean sndPlaySoundA(String s_file, UINT u_flags) Library "WINMM.dll"
Function UINT LoadLibraryA(String as_library) Library "kernel32"
Subroutine FreeLibrary(UINT HInstance) Library "kernel32"
위에서 LoadLibraryA()함수는 멀티미디어 Library가 있는지 확인하는 함수. 그런다음
sndPlaySoundA()함수로 .WAV file을 전달한다. 연주가 끝난후에 메모리를 해제하기 위해서
FreeLibrary()함수가 호출된다.
다음의 예제 소스를 f_play라는 함수로 만들어 필요시에 사용하면 편리합니다.
return value는 None이고 Access 속성은 Public입니다.
UINT lu_instance
lu_instance = LoadLibraryA("WINMM.dll")
IF lu_instance = 0 THEN
sndPlaySoundA(as_wave, 0)
FreeLibrry(lu_instance)
END IF
return
위의 함수사용은 아주 간단합니다.
as_wave는 Argument로 받는겁니다.
즉 f_play("sound.wav")하면 됩니다.
sndPlaySoundA()함수에서 .WAV file을 지정할때 Path에 들어있는 file 이어야 합니다.
그렇지 않으면 Directory도 지정해주어야 합니다.
두번째 Argument는 여러가지가 있습니다.
SND_SYNC 0 sound를 연주하고 return되기 전에 종료한다.
SND_ASYNC 1 sound를 연주하고 연주하는중에 return한다.
SND_NODEFAULT 2 WAV fiel이 발견되지 않았다면 default sound가 연주되지 않는다.
SND_MEMORY 4 file name이 memory안에 있는 image를 가리키고 있다.
SND_LOOP 8 SndPlaySound가 file name에 대해 NULL값을 가지고 호출될 때까지 연주한다.
SND_NOSTOP 16 현재 sound가 연주중이면 FALSE를 return한다.
지정한 sound file이 발견되지 않으면 함수는 소리를 연주하지 않고 FALSE를 return합니다.
SND_NOSTOP Argument가 지정되어 있고 또 다른 Sound가 현재 연주중인 경우에도 FALSE가 return됩니
다.
───────────────────────────────────────
MultiLineEdit에서의 텝키사용
MultiLineEdit에서 입력중에 Tab key를 누르면 tab order 순서에 따라 focus가 이동된다.
이때 발생하는 event는 pbm_keydown 또는 pbm_dnwkey인데 이들 event를 직접 가로채서 MultiLineEdit
에 tab문자나 Spaces문자를 입력하게 하는것은 힘들다.
하지만 Tab key에의한 Focus이동을 허용하지 않고 Tab문자나 Spaces문자를 MultiLineEdit에 입력하게
하고자 한다면 MultiLineEdit의 LoseFocus Event에서 Tab Key가 눌려졌는지를 KeyPress()함수를 이용
해서 처리할 수 있다.
Tab Key가 눌려졌다면 원하는 문자를 입력하고 나서 User Event를 Post시켜서 focus를 SetFocus()함
수를 사용해서 다시 MultiLineEdit로 돌려놓는다.
LostFocus Event
IF KeyDown(KeyTab!) THEN
this.ReplaceText(" ")
this.PostEvent("ue_setfocus")
END IF
───────────────────────────────────────
OpenWithParm
OpenWithParm을 사용하면 Open되는 Window로 필요한 parameter를 넘겨줄 수 있다.
하지만 PB에선 하나의 변수만 넘길 수 있다.(여러개를 넘길때는 structure를 사용하세용~~)
예를 들어서 OpenWithParm(w_abc,"hello"))은 PB의 global 변수인
Message.StringParm에 저장되어 전달된다.
OpenWithParm(w_abc, 34)하면 Global 변수인 Message.DoubleParm에 저장되어 전달된다.
그밖에 다른 Type의 값들인 경우는 MessagePowerObjectParm에 저장되어 전달된다.
그럼 하나이상의 값을 어떻게 전달할 수 있는가... 그건 structure object을 사용하면 된다.
structure object에 unbounded array를 정의하면 필요한 갯수만큼을 전달할 수 있다
unbounded array는 실행시에 배열의 크기를 결정하는 형식이므로 얼마든지 많은 값을 전달할 수 있
다.
structure painter에서 'Variable Name'을 s[]로하고 'Type'을 string으로 한다.
그럼 배열 s[]는 string을 저장할 수 있다.
계속해서 필요한 여러 Type의 배열을 정의해준다.
예를 들어서 double type의 d[], boolean type의 b[], datetime type의 dt[] 등으로 같은 structure
안에 각각을 정의한다.
structure 이름을 str_parms로 한다면 아래와 같이 활용할 수 있다.
//Script에서 정의한 str_parms type의 structure 변수를 선언한다.
str_parms l_str_parms
//전달하고자 하는 값을 Type에 맞는 structure의 member에 치환시킨다.
l_str_parms.s[1] = "Smith"
l_str_parms.s[2] = s_company_name
OpenWithParm(w_abc, l_str_parms)
//w_abc의 Open event
//앞전의 script에서 보낸 structure가 전역변수인
//Message.PowerObjectParm에 저장되어 있다.
str_parms l_str_parms
l_str_parms = Message.PowerObjectParm
//여기서 전달받은 structure의 각각의 값을 사용한다.
//(ex. l_str_parms.s[1], etc.)
위와같은 방식은 CloseWithReturn 함수에서도 똑같이 적용된다.
───────────────────────────────────────
Window를 항상 Top위치에
Window가 다른 Window들 보다 항상 Top에 있어야할 필요가 있는 경우가 있다.
Win3.1에서의 시계가 Option으로 항상 위에 있게 할 수 있게 되어있었다.
PowerBuilder에서는 이것을 아주 간단히 구현할 수 있도록 SetPosition() method로써 제공하고있다.
SetPosition()에 사용되는 argument로서 TopMost!와 NoTopMost!가 있다.
이밖에 Behind!, ToTop!, ToBottom!이 있다.
winobj.SetPosition(TopMost!)로 하면 Window는 항상 Top위치에 있게 된다.
이를 해제할려면 winobj.SetPosition(NoTopMost!)로 하면 된다.
이밖에 ToTop!은 Window들 중에서 가장 높은 Top위치로 올리는 것이고,
ToBottom!은 Window들 중에서 가장 낮은 Bottom위치로 내리는 것이다.
Behind!를 사용할때는 Argument가 더 추가되어서 여러 Window들 중에서 몇번째 위치로 올려놓을 것인
지를 지정해주어야 된다.
SetPosition() Method는 Window Object뿐만 아니라 Window에 있는 다양한 Object에도 적용가능하다.
예를 들어서 겹쳐있는 DataWindow의 위치를 변화시켜줄 필요가 있는 경우에도 사용할 수 있다.
───────────────────────────────────────
시리얼 통신
Serila comunications: a 32bit modem dialer
Win3.x에서 16bit PowerBuilder로 communication ports를 사용하는 것은 간단하다.
하지만 Win95/NT에서는 좀 어려운 작업이 필요하다.
왜냐하면 이들 OS는 communication port를 다른 방식으로 사용하기 때문이다.
즉 port를 file처럼 사용한다.
게다가 Win32 API 함수를 사용해야 한다.
또한 communication port file은 특별한 file이기 때문에 매우 특별한 방법으로
사용해야 한다.
먼저 Win32 API 함수가 필요하다.
/* To create a file */
function long CreateFileA(
ref string lpszName,
long fdwAccess,
long fdwShareMode,
long lpsa,
long fdwCreate,
long fdwAttrsAndFlags,
long hTemplateFile ) library "kernel32.dll"
/* to write to the file */
function boolean WriteFile(
long hFile,
ref string lpBuffer,
long nNumberOfBytesToWrite,
ref long lpNumberOfBytesWritten,
st_overlapped lpOverlapped ) library "kernel32.dll"
/* To close the file */
function long GetLastError() library "kernel32.dll"
/* To get error information */
function boolean CloseHandle(long hObject ) library "kernel32.dll"
위 4개의 API함수 ProtoType을 PowerBuilder에서 Global External Fucntions
정의하는 곳에 기술하면 된다. 그럼 Script에서 참조할 수 있다.
st_overlapped는 communication port로 쓰기를 시도할때 사용하는 structure다.
이것을 PB에서 정의한다.
$PBExportHeader$st_overlapped.srs
global type st_overlapped from structure
long Internal
long Internalhigh
long offset
long offsethigh
long hevent
end type
다음 예는 모뎀이 어떻게 전화를 거는지를 보여준다.
그외의 자세한 사항은 Win32 API HelpFile에 설명되어있다.
PowerBuilder에 있는 Watcom C++ compile에 보면 Helpfile이 있다.
/* declare the needed variables */
long ll_comid
long lnull
st_overlapped lst_overlapped
long ll_written
string ls_Port
string ls_Number
string ls_CRLF = "~r~n"
/* some constant, see helpfile for more information */
long GENERAL_WRITE = 1073741824
long SHARE_MODE = 0
long OPEN_EXISTING = 3
long FILE_FLAG_OVERLAPPED = 1073741824
/* port and number to dial */
ls_Port = "COM2"
ls_Number = "ATDT 0306090146" + ls_CRLF
setnull(lnull)
/* open the port by creating the 'file' */
ll_Comid = CreateFileA(ls_port,GENERAL_WRITE,SHARE_MODE,lnull, &
OPEN_EXISTING,FILE_FLAG_OVERLAPPED,lnull)
IF ll_ComId >= 0 THEN
/* write the number to the port */
writefile(ll_ComId, ls_Number,len( ls_Number),ll_written, lst_overlapped)
messagebox("Yo!","Press Enter To Disconnect")
writefile(ll_ComId, ls_CRLF,len(ls_CRLF),ll_written, lst_overlapped)
ELSE
/* display error */
messagebox(string(ll_Comid),getlasterror())
END IF
/* close always */
closehandle(ll_ComId)
───────────────────────────────────────
칼럼이름 저장하기
Looping through DataWindow Objects
DataWindow에 있는 Column의 이름을 저장하는 방법입니다.
String ls_string[]
int i
any ll_count
ll_count = dw_1.Object.Datawindow.Column.count
For i = 1 to integer(ll_count)
ls_string[i] = dw_1.Describe("#"+String(i)+".Name")
Next
이렇게하면 ls_string에 Column 이름이 들어가게 되죠.
"#"+String(i)+".Name"을 잘 보세요.
Modify함수나 Describe함수에서 #1,#2 등은 Column을 의미합니다.
즉 DataWindow에서 Table의 Column을 선택할때 상단의 selection list 에 나오는 column의 순서를 #1
등으로 표시하는 겁니다.
그럼 #1은 dwobject과 같은 type을 갖게 됩니다.
따라서 dwobject.name처럼 사용하면되죠.
DataWindow의 Itemchanged event에서 사용하는 dwobject처럼 사용하는 겁니다.
───────────────────────────────────────
Selected Rows
DataWindow에서 SelectRow함수를 이용하면 DropDownListBox처럼 Row를 마우스로 선택하는 효과를 얻
을 수 있다. 또한 동시에 여러개의 Row를 선택할 수 있다.
이건 DataWindow를 DropDownListBox처럼 사용할 수 있다.
이런 SelectRow함수는 DataWindow의 RowFocusChanged 또는 Clicked Event에 주로 기술한다.
또한 SelectRow함수로 선택된 Row의 색깔은 파란색으로 반전 된다. 이렇게 사용자가 선택한 Row가 몇
개나 되는지 또한 선택된 Row의 특정 Column의 값을 얻고자 한다면 흔히들 GetSelectedRow함수를 사
용해서 loop
를 돌리게 된다.
하지만 loop대신 한줄로 해결할 수 있다.
long dept_id[]
dept_id = dw_1.object.dept_id.selected
하면 column dept_id의 값이 dept_id라는 배열에 모두 저장된다.
배열의 DataType은 column의 DataType과 일치하게 선언하면 된다.
즉 선택된 Row의 dept_id라는 column의 값이 dept_id라는 배열에 치환된다.
───────────────────────────────────────
ScrollToFirstRowOnPage
User가 DataWindow의 scrollbar를 눌러 scroll시킬때 row에 대한 highlight도 이동되도록 하는 Tip이
다.
highlight되는 row는 현재 보여지는 page의 첫번째 row가 되도록 한다.
vertical scrollbar를 누르면 ScrollVertical event가 발생한다.
따라서 ScrollVertical Event에서 Describe()함수로 현재 보여지는 page의 첫번째 row의 값을 구해서
highlight를 설정하면 된다.
integer li_RC
long ll_row
string ls_result
// 현재 Page의 첫번째 row를 구한다.
ls_result = this.describe ( "DataWindow.FirstRowOnPage" )
if ls_result = "!" then
MessageBox ( "Error", "Describe failed (1)" )
halt close
end if
ll_row = long ( ls_result )
if ll_row < 1 then
MessageBox ( "Error", "Describe failed (1)" )
halt close
end if
// ScrollToRow함수로 상단의 row를 current row로 설정한다.
li_RC = this.ScrollToRow ( ll_row )
if li_RC <> 1 then
MessageBox ( "Error", "ScrollToRow failed" )
halt close
end if
───────────────────────────────────────
문자를 반짝이게(blink 기능)
DataWindow에서 display되는 문자를 반짝이게 하는 Tip이다.
문자를 반짝이게 하기 위해서는 DataWindow의 property중에서 timer inteval을 적당한 값으로
setting시켜야 한다.
10정도로 잡으면 된다. 그런다음 column이나 기타 control을 만들고 control의 property에서
Expressions의 visible에 다음 코드를 작성한다.
if( mod( Integer( Mid( String( Now() ), 7, 2) ),2 ) = 1, 0, 1)
색변화를 주기위해서는 color에 다음 코드를 작성한다.
if( mod ( Integer( Mid( String( Now() ), 7, 2) ),2 ) = 1, 0, 255)
이렇게 하면1초를 주기로 반짝이게 된다.
Now()는 현재 시간이고 Mid()로 초를 잘라내어 2로 나눈 나머지 값의 변화로 반짝이게 하는 것이다.
timer interval이 0으로 되어있으면 Now()는 처음 retrieve된 시간을 유지하게 되므로 반드시 timer
interval을 줘야 한다.
───────────────────────────────────────
자동 Row 삽입
DataWindow에서 사용자가 맨 마지막 row 마지막 column에서 Tab Key를 누르면 자동으로 새로운 row를
insert해주는 Tip을 소개하겠다.
먼저 DataWindow에서 User Event를 선언하자.
선언할 User Event는 ue_tabout이라고 하고 사용할 User Event는 pbm_dwntabout이다. 이 Event는 사
용자가 tab key를 입력해서 DataWindow를 벗어날때 발생하는 event다.
따라서 focus는 DataWindow가 아닌 다른곳으로 이동하게 되는 순간에 발생한다. ue_tabout script는
다음처럼 기술하면 된다.
long l_row
l_row = this.InsertRow(0)
this.SetRow(l_row)
//DataWindow가 horizontal scroll bar를 갖도록 property를 설정한 경우.
this.Modify("datawindow.HorizontalScrollPosition = 1")
this.SetColumn("first_column")
this.SetFocus()
───────────────────────────────────────
SetMicroHelp
마우스를 어떤 control위로 이동시키면 microhelp message가 뿌려지는 프로그램을 본 일이 있을것이
다.
이걸 파빌에서 구현하기 위한 Tip이다. 표준 Window event로써 pbm_nchittest가 있다.
이건 마우스가 어떤 control로 이동하면 자동으로 발생하는 event다.
예를 들어서 command button을 만들었다면 사용자가 마우스를 command button위로 이동시켰을 때
pbm_nchittest event가 발생한다.
그럼 우리는 pbm_nchittest event를 commnand button의 user event로 만들고 microhelp message를 뿌
리는 script를 작성하면 끝이다.
message는 control의 properties... 의 Tag에 기술하고 pbm_nchittest event의 script로서 다음과 같
이 기술하면 된다.
g_w_frame.SetMicroHelp(this.tag)
g_w_frame은 MDI frame window를 가리키고 있다.
Tag가 싫다면 직접 string을 줘도 된다.
script는 여러분의 application에 맞게 기술하면된다.
user event만드는 방법은 해당 control의 script에 들어가서 메뉴의 Declare항목 User Events...를
선택하면 된다.
그럼 Events - controlname 이라는 window가 뜨고 거기에서 Event Name은 적당한 이름을 준다.
가능하면 we_nchittest라고 주는 것을 권장한다.
그리고 Event ID는 pbm_nchittest라고 주면 된다.
또는 Paste Event ID:라는 ListBox에서 찾아보면 있다.
더블클릭하면 해당 event id가 Event ID에 등록된다.
───────────────────────────────────────
pbm_syscommand Event
파빌 프로그래밍을 하다보면 response window를 많이 사용하게 된다.
response window에서는 'OK'라던가 'Cancel' button을 사용하게 되는데 사용자가 이 버튼만 사용한다
고 말할 순 없다.
윈도우의 오른쪽 상단 구석에 보면 'x'표시가 있다. 마우스로 이걸 누르게 되면 윈도우는 close된다.
우리가 response window를 사용할때 main window와의 message 전달 수단으로 CloseWithReturn 함수를
사용한다.
따라서 main window는 responce window가 close되면 message객체에서 message를 참조하려 할것이고
사용자는 'OK'또는 'Cancel' 버튼으로 종료하지 않았으므로 message는 null값이 전달된다.
즉 실행중에 'null object referenced' error message가 발생한다.
이를 해결하기 위한 script로서는 pbm_syscommand로 User Event를 선언한다.
그런다음에 user event에 다음의 script를 기술한다.
ue_cancel은 cancel 버튼을 눌렀을 때 수행하는 내용과 같다.
그럼 ue_cancel에서 적당한 message를 전달하게 script를 작성하면 끝난다.
IF message.wordparm = 61536 THEN
this.TriggerEvent("ue_cancel")
END IF
───────────────────────────────────────
Path 지정
파빌로 만든 프로그램을 설치할때 필요한 run-time DLLs를 위해 Path를 설정해야한다.
autoexec.bat file에서 Path문장으로 설정할 수도 있지만 Win95의 registry를 이용할 수도 있다.
'시작'의 '실행'에서 regedit를 하면 registry를 편집할 수 있다.
'HKEY_LOCAL_MACHINE₩SOFTWARE₩Microsoft₩Windows₩CurrentVersion₩App Apths'
항목을 선택하면 모든 실행화일 이름들이 나열되어있다.
거기에는 'Path'라는 값이 있고 거기에 실행파일에 필요한 path가 기재되어있다.
마우스 오른쪽버튼으로 path를 수정할 수 있다.
예를 들어 'myprog.exe'라는 프로그램을 설치한다면 App Paths밑에 'myprog.exe' 라는 key를 만들고
Path값으로 'c₩Program Files₩Common Files₩Powersoft Shared;c:₩SQLANY50₩win32'하면 Windows는 실
행파일에 필요한 내용을 찾을 수 있게된다.
Window의 Timer Event를 이용하지 않은 시계만들기
1. Datawindow Object의 Attribute 중 Timer interval은 내부적으로 Timer를 발생시킨다. 이 Option
을 1000으로하여 1초마다 변화를 발생하게한뒤 Now() 함수를 쓴 Computed field를 만들어 Header에
위치시키면 시계가 된다. 물론 이 Datawindow는 Dummy column 한개의 External Datawindow로 만들면
될것이다.
2. 이러한 Timer interval을 응용하여 어떤 특정한 칼럼이나 Row를 깜박이게 하거나 색을 반복적으로
변화시킬 수 있다.
원하는 Column의 Attribute 중 Visible에 다음과 같이 쓰거나
if(mod(integer(Mid(string(Now()),7,2)),2) = 1, 0 , 1)
Attribute 중 color에 아래와 같이 쓰면 된다 if(mod(integer(Mid(string(Now()),7, 2)),2) = 1 , 0
, 255)
Update된 특정 Row의 칼럼만 다시 Retrieve 하기
간단하다. Reselectrow() 함수를 사용하면 됨.
보통 모든 칼럼대비 10% 미만의 변화가 있을때 사용함이 유용하며 그 이상일때는 완
전한 Retrieve() 함수의 사용이 보다 빠르다.
Datawindow에서 Multi Table Update를 가능하게
몇개의 Table을 Join하여 Datawindow를 만들었을때 Update characteristic에서 하나의 Table에 대해
서만 선택이 가능하며 다른 Table의 칼럼을 선택하여 OK를 누르면 선택된 Table의 칼럼이 아니라는
Error가 뜬다. 그러나 Update를 원하는 모든칼럼을 선택하고 여러 Table의 Key column을 선택할 수
있는 방법이 있다.
위와같이 필요한 모든 칼럼과 원하는 Option을 선택한뒤 Table to Update 필드에 임의 테이블명을 쓰
고 (예:TEMP) OK 버튼을 누르면 Table 'TEMP' not found. Use table name anyway ? 라는 선택 메세지
박스가 나타나는데 확인을 누르면 모든게 OK
이 방법은 물론 Update() 함수를 날릴때 Sqlsyntex를 확인해보면 알겠지만 Update나 Insert SQL문이
TEMP라는 Table로 날아감으로 Not Found Table이라는 Error가 발생할것이므로 미리
Modify("Datawindow.Table.UpdateTable = {table명}") 과
Modify("column명..Update = {yes/no}")을 해야하는 수고가 필요한 것은 같으나 선택한 column에 대
한 Itemstatus를 내부적으로 Checking 하고있는것이 다르다.
보통 이렇게 쓰지않고 직접 SQL문장을 몇번씩 날리거나 보다편하게 Stored Procedure를 사용하는게
속편하고 행복한(?) 방법이다.