| Home | Previous Lesson: Course 3:: Session 21 :: Page 220 Next Lesson: Course 3:: Session 21 :: Page 240 |
Programming Tab controlTab control is another Window's 95 control introduced with version 5.0. Using this control you can create diary like user interface. Tab control user interface in PowerBuilder was never heard of prior to version 3.0. From version 3.0 developers started developing tab controls using user objects and found that developing tab control was not that easy and also would not be that flexible. Tab control is a container for the tab pages. Initially, when you place the tab control on the window/user object, you will see one tab page available by default. Logically, you can divide the tab control in two areas, one, tab area (the top area of the tab control where tabs are displayed at design time) and the other is the tab page area. Clicking on the tab page area will prompt you for the tab page properties. Clicking on the tab area irrespective of the displayed tab page, PowerBuilder displays the tab control's property dialog box.
To learn about tab control & tab page properties, please refer to 'Tab Control' in the 'Window Painter' section. Using a common control for all tab pagesYou may come across a situation where you want to use a single control say a DataWindow control for all tab pages and want to filter that DataWindow depending on the tab page selection. For example, say you want to create a tab control with three tab pages, "Receipts", "Issues", "Returns". You may want to use a single DataWindow control and retrieve all the transactions and filter transactions depending on the selected tab page. What you could do in this situation is you can place a tab control on the window. Place a DataWindow control somewhere on the window (make sure you don't place the DataWindow control directly over the tab control). Then either you can pull the DataWindow control onto the tab control and set 'BringToTop' option from it's popup menu or you can do that at run-time through programming. The above sequence is very important. If you want to refer to the DataWindow "dw_1" from window's open event, you can refer as: dw_1.SetTransObject( SQLCA ) The thumb rule you need to understand here is that, if you place a control on a tab page directly that control becomes part of that tab page. For ex, if you place dw_1 on the "Receipts" tab page directly and want to set it's transaction object in the window's open event, you need to code like: tab_1.tabpage_receipts.dw_1.SetTransObject( SQLCA ) The above reference to the DataWindow is similar to referring to an object in a custom user object. You can refer to the DataWindow directly from tabpage_receipts tab page, but not from other tab pages. For example, dw_1.SetTransObject( SQLCA ) The above statement is valid if you are writing in any event for the tab page 'tabpage_receipts'. However, if you write the same code in 'tabpage_issues', you get compilation error. Insert two tab pages in the tab control as shown in the above picture (One tab page is created by default for you). Specify 'R', 'I' and 'T' as tag values respectively. We will be using these tag values as the retrieval arguments for the DataWindow to retrieve transactions from the database. Write the following code in the 'SelectionChanged' event of the tab control 'tab_1'. String ls_TranType The index of the selected tab page is passed as an argument to the 'SelectionChanged' event. Using that index number, we are reading the tag value of the selected tab page and passing that to the event 'ue_filter_trans' as an argument. When you compile the code you get error since 'ue_filter_trans' event is not yet defined. Just comment the last line and define 'ue_filter_trans' event at the window level. Define a string argument 'as_tran_type'. Now, uncomment the last line and compile it. As you may recall, window has an array property named 'Control'. All the controls that you place on the window is listed in this property. Similarly, tab control also has 'control' property. It contains all the tab pages that you paint at design time. You can observe in the second line we are referring to 'control' property. dw_trans.SetFilter( "tran_type = " + '"' + upper( & as_tran_type ) + '"' ) dw_trans.Filter() Return (dw_trans.RowCount() - dw_trans.FilteredCount()) Write the above code to the 'ue_filter_trans' event. I am sure you know what the above code does. In the window opening event, make sure to set the transaction object to the DataWindow control dw_1 and retrieve the data into it.
Using independent controls for each tab pageIn the previous example we used same DataWindow control for all tab pages. In this section let's learn how to use independent DataWindow controls on each tab page. Create another window 'w_tab_demo2' and add a new tab control to the window. Change the text of the first tab from 'none' to 'Receipts. Name this tab page as 'tabpage_receipts'. Place a DataWindow control on the 'tabpage_receipts'. Name the DataWindow control as 'dw_receitps'. Now, insert a new tab page and name it as 'tabpage_issues'. Change its text to 'Issues'. Place a DataWindow control on the 'tabpage_issues' and name the DataWindow control as 'dw_issues'. Similarly insert one more tab page for 'Returns'. We are using three DataWindow controls but, we haven't created/assigned DataWindow objects to these DataWindow controls. Paint a DataWindow to retrieve all the receipt transactions and assign that DataWindow object to the 'dw_receipts' DataWindow. That means, the data source for the DataWindow would be: SELECT * FROM trans WHERE tran_type = 'R' Similarly paint DataWindow objects for other DataWindow controls and assign them to the appropriate DataWindow controls. Then write the following code to the 'SelectionChanged' event of the 'tab_1' tab control. DataWindow ldw1 Transaction ltr1 ldw1 = Tab_1.Control[ NewIndex ].Control[1] If ( ldw1.GetTrans( ltr1 ) ) <> 1 & Then ldw1.SetTransObject( SQLCA ) return ldw1.Retrieve() You can observe that, in the third line we are using 'Control[]' twice. The first 'control[]' has the list of all the tab pages in the tab control. The second 'Control[]' has the list of all controls that you placed on that tab page. As you know, we placed only one control i.e, DataWindow control on the tab page, That's why, I hard coded 'Control[1]'. In real-world projects, you need to check whether that control is a DataWindow control or not and retrieve it only if it is a DataWindow control. The following revised code does that. UserObject luo1 DataWindow ldw1 Transaction ltr1 Integer li_Counter, li_TotControls
For li_Counter = 1 to li_TotControls
luo1 = Tab_1.Control[ NewIndex ].Control[ li_Counter]
If luo1.TypeOf() = DataWindow! Then ldw1 = luo1
If ( ldw1.GetTrans( ltr1 ) ) <> 1 Then &
ldw1.SetTransObject( SQLCA )
return ldw1.Retrieve()
End If
Next
Return 0
You can observe one more technique from the code fragments above. That is, we are checking whether the DataWindow has a transaction object set to it or not. If not, then only we are calling the SetTransObject() function. Now, to really understand the difference between 'Using a single control on all tab pages' and this method, you need to print the codes and compare them. In the former, we are referring to the DataWindow control directly like any other control on the window. However, we need to go an extra mile to find the correct DataWindow control in the tab page. Creating Tab Pages Using Custom User ObjectsIn the previous method we placed standard window controls on each tab page. In this method, you will learn how to use custom visual user objects on the tab pages. Coding in this method is more complex compared to previous methods. The first one is easy and straight forward. The second method needs more coding. To use this method, you need to have better understanding of PowerBuilder objects, otherwise, the program will end up have heavy run-time errors, such as NULL object references and assigning objects to the wrong variable types and so on. In this method, what I would like you to do is, first create a custom visual user object and then the window. Invoke the user object painter and select 'New' and select "Custom" from the "Visual" category. Place a DataWindow control on the work area and save it as 'uo_trans_for_tab_page_demo'. I know we haven't assigned any DataWindow object for that. Don't worry, we will be doing that at run-time. Create a window 'w_tab_demo3' and place a tab control 'tab-1' on the window. Turn off Visual & Enabled properties for the first tab page 'tabpage_1'. Now, click on the tab text display area and invoke the popup menu. Select 'Insert UserObject' and select 'uo_trans_for_tab_page_demo'. Name this tab page as 'tabpage_issues'. Similarly create one more for 'returns'. I would like to do something different for the 'receipts'. This will give you clear understanding of tab control programming. This time instead of selecting 'Insert User Object', select 'Insert TabPage'. Name it as 'tabpage_receipts'. Now, Select 'Controls/User Object' from the menu and 'uo_trans_for_tab_page_demo' and place it on the 'tabpage_receipts' tab page. In summary, we are not using the default tab page. For 'Issues' and 'Returns' we are creating tab pages using user objects. For 'Receipts' we are adding a regular tab page and placing the user object on the tab page. Now, let's see how to program tab control to retrieve data into these DataWindows. Write the following code to the 'SelectionChanged' event of the tab control 'tab_1'. DataWindow ldw1 UserObject luo1 Integer li_ObjectCount, li_Counter Integer li_Counter1, li_ObjectCount1 li_ObjectCount = UpperBound( & This.Control[ NewIndex ].Control[] ) For li_Counter = 1 to li_ObjectCount If This.Control[ NewIndex ].& Control[li_Counter].TypeOf() = UserObject! Then luo1 = This.Control[ NewIndex ].Control[li_Counter] li_ObjectCount1 = UpperBound( luo1.Control[]) For li_Counter1 = 1 to li_ObjectCount1 If luo1.Control[ li_Counter1 ].& TypeOf() = DataWindow! Then ldw1 = luo1.Control[li_Counter] Exit End If Next If NOT IsValid( ldw1 ) Then Return -1 ElseIf This.Control[ NewIndex ].& Control[li_Counter].TypeOf() = DataWindow! Then ldw1 = This.Control[ NewIndex ].Control[li_Counter] Exit End If Next If Not IsValid( ldw1 ) Then Return -1 If This.Control[ NewIndex ].ClassName() = & "tabpage_receipts" Then ldw1.DataObject = "d_receipts_for_tabpage_demo" ElseIf This.Control[ NewIndex ].ClassName() = & "tabpage_issues" Then ldw1.DataObject = "d_issues_for_tabpage_demo" ElseIf This.Control[ NewIndex ].ClassName() = & "tabpage_returns" Then ldw1.DataObject = "d_returns_for_tabpage_demo" End If ldw1.SetTransObject( SQLCA ) ldw1.Retrieve() After declaring the variables, we are finding the number of objects that are residing on the selected tab page. Then in a loop we are checking object type for each object on the tab page. Remember that we added the tab page for 'Issues' and 'Returns' by inserting the user object. In this case, 'This.Control[ NewIndex ]' refers to the tab page say, tabpage_issues. Since the tab page itself is a user object, it is referring to 'uo_trans_for_tab_page_demo'. 'This.Control[ NewIndex ].Control[li_Counter]' refers to the DataWindow control inside 'uo_trans_for_tab_page_demo'. However, the above code is not correct for the 'receipts' tab page. In this case, 'This.Control[ NewIndex ]' refers to the tab page 'tabpage_receipts' itself. 'This.Control[ NewIndex ].Control[li_Counter]' refers to the user object 'uo_1' (of type 'uo_trans_for_tab_page_demo') that we placed on the tab page. To refer to the DataWindow control inside the 'uo_trans_for_tab_page_demo' you need to refer to another set of 'Control[]' property. This is being done in the inner FOR loop. For practice read this topic couple of times more. See the code in action. Then do the same without looking at the code. If you can do it (without cut/paste method), you are ready to program tab controls in real world apps. Creating Tab Pages DynamicallySome times, you may not know the number of tab pages that you need to display at run-time. You need to dynamically create tab pages on the fly at run-time and display it to the user. Another reason you may prefer dynamic tab pages is if there are too many tab pages on the tab control, for it takes more time to load the window. Not every user will be using all the tab pages. So, it is a good idea to create only those pages that are needed at run-time, by this, window will be opened faster and also we don't open tab pages unnecessarily. As you may recall, all the tab pages are listed in the Control[] property of the tab control. We are referring to this property all the time. However, in version 5.0, PowerBuilder DOES NOT populate the dynamic tab page information in the control[] property. Because of this, we need to program tab control in a different way. That is, we need to declare an array of user object variable and refer to those objects in various events & functions.
To create tab pages dynamically, you need to understand OpenTab() and OpenTabWithParm() functions. I will explain these functions as we go. First, create a window 'w_tab_demo4' and place a tab control. As we are going to create tab pages dynamically, we do not need the default tab page. Deleting this tab page is a little tricky. If you select the tab page and hit the delete button, whole tab control is removed from the window. What you could do is, select the default tab page, invoke the popup menu for the tab page (not for the tab control) and select 'Cut' option. Declare the following instance variables in the window. uo_trans_for_tab_page_demo
iuo[3] The reason we are declaring three subscripts is, we need to create three tab pages, receipts, issues and returns. Write the following code to the window open event. iuo[1] = Create
uo_trans_for_tab_page_demo is_DwObjects[1] =
"d_receipts_for_tabpage_demo" tab_1.OpenTab(
iuo[1], 0 ) tab_1.OpenTab(
iuo[2], 0 ) tab_1.OpenTab(
iuo[3], 0 ) In the above code, first we are creating instances of user objects and storing them in the array. In this example, we are using the same user object in all tab pages, however, you can use different user objects. Similarly, we are storing the DataWindow objects that needs to be assigned. Later we are calling OpenTab() function. This function opens the user object as the tab page and inserts before the specified index. We are specifying zero, which means, append this tab page to the existing tab page. That means, the first tab page we open will become the first one. After opening each tab page, we are setting the tab page text by referring to the user object. If PowerBuilder populates tab control's control[] property with dynamic tab page information, we can set the text in the following way. Tab_1.Control[1].Text = "Receipts" If you write the above code instead of writing iuo[1].Text = "Receipts", you will be able to compile the code successfully. However, you will end up with run-time error. See, we haven't painted any thing on the tab control at design time. Now, write the following code to the tab control's SelectionChanged event. DataWindow ldw1 UserObject luo1 Integer li_ObjectCount, li_Counter Transaction lTr1 luo1 = iuo[ NewIndex ] li_ObjectCount = UpperBound( luo1.Control[] ) For li_Counter = 1 to li_ObjectCount If luo1.Control[ li_Counter ].TypeOf() = & DataWindow! Then ldw1 = luo1.Control[li_Counter] Exit End If Next If Not IsValid( ldw1 ) Then Return -1 //Set transaction object only if it is not already set. If ldw1.GetTrans( lTr1 ) <> 1 Then ldw1.DataObject = is_DWObjects[ NewIndex ] ldw1.SetTransObject( SQLCA ) End If ldw1.Retrieve() We are storing the reference to the selected tab page's DataWindow control. Then we are setting the DataWindow object and transaction object if it is not already assigned. Then we are retrieving data into the DataWindow control. Before we end this topic, one quick point. If you see the tab control's property dialog box, you will find a property 'Selected Tab'. As soon as PowerBuilder creates the tab control, PowerBuilder selects the tab that you specify for this property. That means, SelectionChanged event is fired and this index number is passed to the event.
Tab Control User ObjectsIf you understood previous topics pretty well this topic is easy. Let us create a standard visual user object of type tab. Invoke user object painter, select 'New', and select 'Standard' from the 'Visual' category. Now, select 'Tab' from the popup window. Save this as 'uo_tab'. Create a new window 'w_tab_demo5' and place the user object you created in the previous step on the window. Copy and paste the code you wrote for the window open event from the previous topic. Similarly do for the tab control's SelectionChanged event also. What we are doing here is exactly same as what we did in the previous topic i.e. opening tab pages dynamically. One thing you need to observe is, when you are using tab control user object, you can insert neither regular tab pages nor the user object tab pages at design time. You have to do it at run-time only. |
| Home | Previous Lesson: Course 3:: Session 21 :: Page 220 Next Lesson: Course 3:: Session 21 :: Page 240 |