ANSI Standard PL/B Language and Visual PL/B
Event Handling
TOPICS:
DATAGRID or
LISTVIEW
Windows 95 users became accoustomed to a structure that looks much like a DATALIST but is, in fact, something else.
This is the DATAGRID or LISTVIEW.
The LISTVIEW is typified by several features:
There's a header bar across the top with
dividers between columns.
The data lines up under the columns regardless of the
use of a proportional font.
The user can change the width of the columns with the
mouse by grabbing a divider and sliding it left and right.
The user can usually
sort the entire grid on any column by clicking
in the column heading.
LISTVIEW
The discussion of LISTVIEW is just being written.
MORE TO COME!
GENERAL NOTES
This FIRST section is a discussion of a few of the notes in the Sunbelt on-line reference.
Interesting points (by number) are shown in this color then disussed as appropriate.
- The LISTVIEW displays a collection of items.
By item the mean ROW.
- Each item in the list has a label.
That is ROW. Don't know what this label is or how to use it.
- Each item in the list can optionally have both a large
and small icon or bitmap associated with it.
Not sure where these go or how to use them.
There's some kind of list of images but we've not found
an example or discussion of this.
- Each item can also have a four byte user defined value associated with it.
Our understanding at this time is this:
This value is not visible. It is not required. It can contain anything
that the user wants to store there which will fit in 4 bytes. The value is
stored and retreived with the GetItemParam and SetItemParam methods.
- Asociated with each item can be any number of sub-items or columns, but all items must have the same number of sub-items.
Remember that an ITEM is the same as a ROW.
sub-items are COLUMNS.
When it says that all items (rows) must have the same number of sub-items (columns)
it just means that the number of columns are fixed for the entire table.
When building the LISTVIEW you insert those sub-items or columns with the "InsertColumn" method.
For example:
LV_002.InsertColumn USING "Name", 140, 0
LV_002.InsertColumn USING "Sex", 40, 1
LV_002.InsertColumn USING "Age", 40, 2
- A sub-item contains a text string that appears next to the item in report view.
That's just the text in the columns.
- LISTVIEW object can be displayed in one of four different views as specified by the VIEWSTYLE property:
using the item's large (or standard) icons
using the small icons
as a list
as a report.
SETTING UP THE LISTVIEW
In our mainline programs we normally start with two routines:
CALL SETUP_LV_001
CALL LOAD_LV_001
The SETUP_LV_001 routine will define the listview columns, set the initial width of each column, the justification. It also sets the overall look of the listview if grid lines are used.
Note that listviews are ZERO BASED. That is, the first column and the first rows are ZERO rather than 1.
setup_lv_001
DISPLAY "SETUP Listview"
F1_LV_001.InsertColumn USING "Agent", 50, 0
F1_LV_001.InsertColumn USING "Freddy ID", 70, 1
F1_LV_001.InsertColumn USING "Area", 70, 2
F1_LV_001.InsertColumn USING "record key", 210, 3
.
MOVE "4",LV_TOT_COLS
.
F1_LV_001.SetColumnFormat USING 1, *FORMAT=LVCFMT_LEFT
F1_LV_001.SetColumnFormat USING 2, *FORMAT=LVCFMT_RIGHT
F1_LV_001.SetColumnFormat USING 3, *FORMAT=LVCFMT_CENTER
F1_LV_001.SetColumnFormat USING 4, *FORMAT=LVCFMT_LEFT
.
F1_LV_001.SetExtendedStyle:
USING LVS_EX_GRIDLINES:
LVS_EX_GRIDLINES
.
SETPROP F1_LV_001,HIDESEL=0 ; Insure sel if no focus
RETURN
The load of the listview is usually run from a loop. As we get each record we insert that record into the listview as follows:
load_lv_001
F1_LV_001.DeleteAllItems ;Empty the listview
SETPROP F1_LV_001,AUTOREDRAW=0 ;Turn OFF for loading
MOVE ZERO, LV2IX ;Top line is ZERO & headings
MOVE SPACE,LLRECKEY ;Start with first record
CALL LLIOREAD ;Prime the isi file
LOOP
CALL LLIORKS ;Read next record
MATCH NO,LLNIF ;see if end of file
BREAK IF NOT EQUAL ;break when EOF
CALL INSERT_LV_002_ITEM ;put into list view
MOVE LV2IX, LV_HIGHIX ;How many in list?
ADD ONE, LV2IX ;Increment after
REPEAT
SETPROP F1_LV_001,AUTOREDRAW=1 ; Turn it back on
.
.........................................
. If we KNOW the line that we want to highlight we can
. do it as follows. In this example, IX2 would be the
. line we want to point to:
.
IF (IX2 != 0) ;when we found our line.
MOVE IX2,LV2IX ;Line to position to
F5010_LV_002.EnsureVisible USING LV2IX,1
F5010_LV_002.SetItemState USING LV2IX,LVB,LVB
MOVE LV2IX, EventResult ;for clicker
DISPLAY "Call CLICKER"
CALL LV_DETAILS_CLICK ;Get the fields Loaded
ENDIF
RETURN
The following INSERT routine will insert a new line into the listview. The line number LV2IX. Remember that the first line is ZERO.
The first task is to insert the line into the listview using the .InsertItem method. Note that we do this only once for each line and that it also sets the value of the first column.
After the line (item) is inserted, we follow up with .SetItemText methods to put the data in each column of the line.
Our practice is to have TWO routines. The first is the INSERT and the second is the SETITEM. For brand new lines we call the first routine which falls into the second one. Later we can update the contents of the line by calling the second routine.
Data put into the columns must be TEXT, not numbers. If any editing is required, it would be done prior to inserting the text into the column.
insert_lv_002_item
F1_LV_001.InsertItem USING LLSALEMN, LV2IX
. fall into the set which can be used from elsewhere
.
set_lv_002_item
F1_LV_001.SetItemText USING LV2IX, LLID, 1
F1_LV_001.SetItemText USING LV2IX, LLAREACD, 2
F1_LV_001.SetItemText USING LV2IX, LLRECKEY, 3
RETURN
PLBWIN 8.7F introduced a new method for setting a group of items all at the same time. This is much more efficient and was desiged for systems running on PLBSERVE to minimize network traffic:
F1_LV_001.insertItemEx giving RETURN_CODE using *text=LLSALEMN:
*index=Row:
*subitem1=LLID:
*subitem2=LLAREACD:
*subitem3=LLRECKEY
LISTVIEW ROW COLORS
Sunbelt added the ability to set the foreground and background colors for an entire listview row.
It is anticipated that around release 9.0A they'll add the ability to set the color of individual cells.
Color columns are done internally by using a hidden column (width = zero) to store color information for the entire row.
The hidden column can be in any position that the user wishes.
The color column should be defined ONCE when the listview is established.
The background color column is inserted via the InsertColumnBgClr.
The Foreground color column is inserted via the InsertColumnBgClr.
The format of these instructions is:
F1_LV_001.InsertColumnBgClr GIVING {return}:
USING *INDEX={index}
The INDEX value is the (zero based) column where you want the color value stored. The RETURN is the same value coming back. These two are basically redundant . . . except that RETURN can be an error condition if the InsertColumnXXClr fails.
You can only set a single foreground color column and a single background color column. If you use BOTH values, the two columns are in different positions. For example, put the background in column 4 and the foreground in column 5.
Our practice is to do something like:
setup_lv_001
DISPLAY "SETUP Listview"
F1_LV_001.InsertColumn USING "Agent", 50, 0
F1_LV_001.InsertColumn USING "Freddy ID", 70, 1
F1_LV_001.InsertColumn USING "Area", 70, 2
F1_LV_001.InsertColumn USING "record key", 210, 3
.
MOVE "4",LV_BGCOLOR_COLUMN
F1_LV_001.InsertColumnBgClr GIVING LV_BGCOLOR_COLUMN:
USING LV_BGCOLOR_COLUMN
SETTING A ROW's COLOR is done by inserting a color code into the hidden cell just as you'd insert any other value into a cell. For example:
F1_LV_001.DeleteAllItems //Empty the entire listview
MOVE ZERO,LV2IX //initialize zero based row counter
LOOP
CALL LLIOREAD //Loop through the LL file
MATCH NO,LLNIF //Look for the EOF condition
BREAK IF NOT EQUAL
CALL INSERT_LV_002_ITEM //Insert this line into the listview
ADD ONE,LV2IX //Prepare for next line (zero based ix)
REPEAT
...... other stuff in program ......
insert_lv_002_item
F1_LV_001.InsertItem USING LLSALEMN, LV2IX
.
F1_LV_001.SetItemText USING LV2IX, LLID, 1
F1_LV_001.SetItemText USING LV2IX, LLAREACD, 2
F1_LV_001.SetItemText USING LV2IX, LLRECKEY, 3
IF (LLAREACD = "20")
F1_LV_001.SetItemText USING LV2IX, "0xCFA5F1", LV_BGCOLOR_COLUMN
ENDIF
RETURN
The above example inserts a line for each record in the LL file. If the AREA CODE is "20" the background color is set to a putrid purple.
Defining the color is done with a simple string formatted like a standard hex value. The Sunbelt manual says:
1. The data string stored into each subitem of the new column specifies a hexadecimal string that represents the RGB background color value of the line item. The expected string format of a line item color is '0xRRGGBB'.
That means almost what it says. The value can be be a STRING variable or a LITERAL. We used a literal in the example above.
You DO NOT define a hex literal value. The RIGHT and WRONG ways are:
PUTRID_PURPLE_GOOD INIT "0xCFA5F1" // this is CORRECT
PUTRID_PURPLE_BAD INIT 0xCF,0xA5,0xF1 // this will NOT work
The easiest way to find the color values is to use the visual color box in the Forms Designer. You get a nice visual representation and can select any color that you want. You can also program this box yourself by creating a color object and leaving the color blank.
(See COLORS Article.)
If you use the Forms Designer to set the color, you'll see the HEX representation in the Properties box.
What you see is NOT what you want!
For whatever reason, the values that show in the Designer are backwards from what the ListView colors should be. The
BLUE and
RED components are reversed in the hex string. I don't know which is wrong, but they WORK differently. These following two examples are equivalent and product the same putrid purple color:
DESIGNER property shows: 00F1A5CF
ListView should be: CFA5F1
PL/B Version 9 correction 8/20/2009
The color order in the LV has been corrected.
If you use the reversed color order as shown in the example above you get the wrong color.
The correct order is BLUE, GREEN, RED.
Old code needs to be changed to reverse these the first and last bytes to get what
you had on version 8.
USING ANOTHER OBJECT'S COLOR
Another useful way to define a ListView color using the designer is to give an object a color then grab that color at run time and use it in the ListView.
Unfortunately you can't use the color directly. It would be nice to just
GETPROP BGCOLOR and use that color.
Sorry... Won't work.
The work around is to
Get the color components for the object in question as decimal numbers.
Convert each decimal number to a hex.
Build a STRING that the ListView will accept.
Here's our routine, which uses MMCC's standard
DECIMAL_TO_HEX utility routine:
WORK_COLOR COLOR ;color object
WORK_RED FORM 3
WORK_GREEN FORM 3
WORK_BLUE FORM 3
HEX_COLOR DIM 8
HEX_RETURN DIM 2
GETPROP ET_Sample, BGCOLOR=WORK_COLOR ;Get color from form.
GETPROP WORK_COLOR, 1, WORK_RED ;Split out the components
GETPROP WORK_COLOR, 2, WORK_GREEN
GETPROP WORK_COLOR, 3, COLOR_BLUE
PACK HEX_COLOR, "0x" ;Initialize color string
CALL DECIMAL_TO_HEX, WORK_RED, HEX_RETURN
PACK HEX_COLOR, HEX_COLOR, HEX_RETURN
CALL DECIMAL_TO_HEX, WORK_GREEN, HEX_RETURN
PACK HEX_COLOR, HEX_COLOR, HEX_RETURN
CALL DECIMAL_TO_HEX, WORK_BLUE, HEX_RETURN
PACK HEX_COLOR, HEX_COLOR, HEX_RETURN
LISTVIEW COLOR BUG and FIX
There's a case where a ListView with colored lines might not load properly.
Typically the lines are partially colored in a random fashion.
(See image at right.)
Manually changing any of the screen sizes (top, left, width, height), even slightly,
will cause the colors to pop back to what they should be.
There are several ways to fix this in the program:
REFRESH: Refreshing the screen resets the lines.
The instruction format is simply:
{window name}.Refresh
or
F1_Window.Refresh
That might cause some flickering so it's best done when the user clicks something
rather than just doing it all the time.
In the MMCC programs it's typically done right after the initial load and
before the main WAITEEVENT loop.
Put ListView on a PANEL:
Simply doing this in the designer seems to cure the problem.
PROCESSING LISTVIEW EVENTS
There is an important thing to know about listviews built in the designer.
Events generate a series of values which are stored by the form in local variables.
To get these back into a mainline program you have to move them from the local variable to a general variable which the mainline can see.
For example Clicking anywhere on the ROW generates a CLICK event. If you want to know the details of this click you must capture the data within the code in the form itself.
We use the following code on each event that we want to process. This example is for a CLICK event:
MOVE "CLICK", ACTION
MOVE #EventType, EventType
MOVE #EventResult, EventResult (the row number)
MOVE #EventObjId, EventObjId
MOVE #EventChar, EventChar
MOVE #EventMod, EventMod
CLOSEING AND CLEANING UP A LISTVIEW
We learned that listviews which contain a lot of lines can cause a bad behavior in Windows.
When the program performs a STOP, the screen goes away leaving the desktop visible, but the menu program does not come back.
It's been speculated that Windows is in the process of cleaning up memory. Sometimes this can take forever. Users may want to try to start the application again since it looks like it's closed. Eventually Windows finishes whatever it's doing and your next program loads.
To provide feedback we learned to put up a "wait" form to tell the user what's happening. Then we do a DeleteAllItems to have the table cleared. I then do a GetItemCount to keep my program alive until Windows is done. At that point I can drop the "wait" form and chain back to the menu.
This sequence holds the window up and provides the feed back to the user.
Typical code (using MMCC's standard wait form) is:
FORMLOAD WAIT_FORM
SETITEM FWAIT_StatText001,0,"CLOSE PROGRAM"
SETITEM FWAIT_StatText002,0,"WAIT FOR WINDOWS TO CLEAR MEMORY"
SETITEM FWAIT_StatText003,0,"THIS COULD TAKE A WHILE!"
SETPROP FWAIT_StatText004, VISIBLE=0
SETPROP FWAIT_ProgressBar, VISIBLE=0
SETPROP FWAIT_Button001, VISIBLE=0
F8010_LV_001.DeleteAllItems
F8010_LV_001.GetItemCount GIVING IX1
DESTROY WAIT_FORM
STOP
FINDING DATA IN THE LISTVIEW
Sometimes you need to search the listview for selected data. For example, after a sort you may need to find the line that was originally being viewed and reposition to that line.
Here's the routine we use to find a record key in a listview. The record key is usally how we know which line we're pointing to.
lv_find_curr_key
F1_LV0001.GetItemCount GIVING LV_LIST_LAST
MOVE ZERO, LVIX2
LOOP
F1_LV0001.GetItemText GIVING XCRECKEY USING LV2IX2,LV_KEYCOL
IF (XCRECKEY = CURR_XCKEY)
MOVE LV2IX2, EventResult
MOVE LV2IX2,LVCURIX
F1_LV0001.EnsureVisible USING LVCURIX,1
F1_LV0001.SetItemState USING LVCURIX,LVB,LVB
CALL LV_CLICK ;As though someone clicked
BREAK
ENDIF
IF (LVIX2 = LV_LIST_LAST)
BREAK
ENDIF
ADD ONE< LVIX2
REPEAT
RETURN
FINDING THE SELECTED ROW/LINE
There are a couple of ways to identify the selected line in a ListView. The first is on the CLICK event to save the #EventResult in the PLFORM. That will be the line just clicked.
If you want to find the selected line at anytime, you can use the GetNextItem method at any time. You tell the method to find the next selected line after a given line. If you have a single select listview and just want to find the first selected line, use "-1" as the starting line and do a get next.
If there are NO lines selected, the method will return a "-1".
In this example below we first find the next selected line starting at line "-1" (which is BEFORE the first line). We use LVNI_SELECTED which is defined in PLBMETH.INC. Then we use that line (pointed to by LVIX) and reset the state bits to turn off the selection and the focus:
LVIX FORM 5
SEQ FORM "-1"
MyListView.GetNextItem GIVING LVIX, USING *Flags=LVNI_SELECTED,*Start=SEQ
FINDING THE SELECTED ROW/LINE and COLUMN
The row in the Listview is called the ITEM. The column is call the SubItem. You can use the SubItemHitTest method to find both of these items based on the #EventResult.
You can get the EventResult matching the mouse location with either the MOUSE DOWN or the MOUSE MOVE events. The MOUSE DOWN is preferred since the Mouse Move occurs continuously as the mouse tracks over the form.
The MOUSE DOWN event fires BEFORE the CLICK event. Therefore, you would trap on the MOUSE DOWN, record the EventResult value, then use that value when the CLICK occurs, or when some other event occurs where you need the column number.
The EVENT RESULT returned from the MOUSE DOWN is an eight digit number which represents the HORIZONTAL and VERTICAL position of the mouse within the form. You have to split that number yourself to get the two parts.
The SubItemHitTest method will use the horizontal and vertical positions and will return a single value which contains the ROW and COLUMN as one number. The column is the low two digits which meand that the method will only get the position for a max of 99 columns.
The coding we use is this.
- In the PLFORM, trap on the MOUSE DOWN event:
MOVE "MOUSE-DOWN", ACTION
MOVE #EventType, EventType
MOVE #EventResult, EventResult (horiz/vert mouse position)
MOVE #EventObjId, EventObjId
MOVE #EventChar, EventChar
MOVE #EventMod, EventMod
- In the main line we look for the "MOUSE-DOWN" action after the Waitevent then call a routine to do this:
NWK08 FORM 8
LV_HORZ FORM 4 ;Split horizontal from EventResult
LV_VERT FORM 4 ;Split vertical from EventResult
LV_ROWCOL FORM 8 ;Combined row (item) and column (sub item)
LV_ROW FORM 6 ;Row (item)
LV_COL FORM 2 ;Column (sub-item)
lv_mouse_down
MOVE EventResult, NWK08 ;fix size at 8 bytes
UNPACK NWK08, LV_HORZ, LV_VERT ;split into two parts
F1_LV001.SubItemHitTest GIVING LV_ROWCOL:
USING *Horz=LV_HORZ:
*Vert=LV_VERT
MOVE LV_ROWCOL, NWK08 ;fix size at 8 bytes
UNPACK NWK08, LV_ROW, LV_COL ;split into two parts
RETURN
At this point we have stored the ROW and the COLUMN of the last mouse down. The click event fires when the mouse button is released.
USING THE "RECTANGLE"
There's no method for direct user input inside a cell. To get around this, some people detect a mouse click on the cell then create an EditText exactly on top of the cell. The user enters their data there.
To create the EditText, you need the screen coordinates of the cell you want to cover. This data can be retrieved using the GetItemRect method.
LV_001.GetItemRect GIVING LV_RECT_STRING:
USING *Index=LV_ROW:
*Subitem=LV_COL
As the example shows, this method requires the row (item) and the column (sub item) that you want top get the rectangle for.
You get the row and column using the code above to find the selected row/column.
The
GetItemRect method returns a
string formatted as follows:
ttttbbbbllllrrrr where:
tttt = top
bbbb = bottom
llll = left
rrrr = right
We won't go into more detail at this time. Suffice to say you need to pay attention to creating and destroying the EditText at the appropriate times. You may also have problems if the user moves the ListView using the scroll bars. There is some discussion of these considerations on the Sunbelt Web Board.
Getting Data Out of the Listview
To get information out of the listview you have to know the line that was clicked on. Normally you'd do that on the CLICK or DBLCLICK events. The example above shows how. On that event we'd so something like the following code:
.........................................
. Load the info from the Clicked list view
.
lv_details_click
DISPLAY "LV Click:":
*HON,EventResult, *HOFF, "=line ";
MOVE EventResult,LIST_CURRIX
.
F1_LV_001.GetItemText GIVING LLRECKEY:
USING EventResult,19
CALL LLIOREAD
DISPLAY "LL:",*HON,*LL,LLRECKEY,*PL,*HOFF," ",LLNIF
RETURN
.
MULTI-SELECTING LINES
(see SHOP5120, GHCW1082)
In the designer, be sure that "Multi-Select" is set TRUE
MOVE SEQ, LISTVIEW_CURR_ITEM ;insure first one is first
F1_LV_001.GetItemCount GIVING LISTVIEW_TOTAL_ITEMS
LOOP
F1_LV_001.GetNextItem GIVING LISTVIEW_NEXT_ITEM:
USING *FLAGS=LVNI_SELECTED:
*START=LISTVIEW_CURR_ITEM
IF (LISTVIEW_NEXT_ITEM < 0)
BREAK
ENDIF
.
MOVE LISTVIEW_NEXT_ITEM, LISTVIEW_CURR_ITEM
.
F1_LV_001.GetItemText GIVING any-data:
USING LISTVIEW_CURR_ITEM, column
REPEAT
COUNT SELECTED LINES
(see LV-STUB)
In the designer, be sure that "Multi-Select" is set TRUE
lv_count_selected_lines
MOVE ZERO,LV_TOT_SEL
MOVE SEQ, LV_AFTERIX
LOOP
F1_LV001.GetNextItem GIVING LV_NEXTIX:
USING *Flags=LVNI_SELECTED:
*Start=LV_AFTERIX
.
IF (LV_NEXTIX < 0)
BREAK
ENDIF
.
ADD ONE,LV_TOT_SEL
MOVE LV_NEXTIX,LV_AFTERIX
REPEAT
DISPLAY *HON,LV_TOT_SEL,*HOFF," items selected"
RETURN
FULL ROW CLICK
Note that you must make the "FULL ROW" property TRUE in the designer or via a SETPROP if you want the user to be able to click anywhere on the row. If you don't do this, you'll get the CLICK event, but the event handling code will not get a valid row number in EventResult... you just get line ZERO.
|
ITEM STATES - Select and Deselect a line
Each line of the ListView is controlled by a "state mask". As far as we know, this is an two byte bit mask. The definition is (we think):
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 HEX
| | | | | | | | | | | | | | | FOCUSED 1 0001
| | | | | | | | | | | | | | SELECTED 2 0002
| | | | | | | | | | | | | CUT 4 0004
| | | | | | | | | | | | DROP HIGHLIGHTED 8 0008
| | | | | | | | | | | | 16 0010
| | | | | | | | | | | 32 0020
| | | | | | | | | | 64 0040
| | | | | | | | | 128 0080
| | | | | | | | 256 0100
| | | | | | |
| | | | | |
| | | | |
| | | |
| | |
| |
|
0 0 0 0 1 1 1 1 OVERLAYMASK 0F00
1 1 1 1 STATEIMAGEMASK F000
The SetItemState method uses "STATE" and "STATEMASK" to set these bits. The instruction format is:
{object}.SetItemState [GIVING {return}]
USING [*Index=]{index}:
[*State=]{state}:
[*Statemask=]{statemask}
You can leave out the GIVING unless you want the return. The USING items are required. If you code them in the order given you can leave out the * descriptors. That is, to set line 5 as the current line, you want it FOCUSED AND SELECTED. You'd key:
MyListView.SetItemState USING 5,LVIS_FOCUSED, LVIS_FOCUSED
MyListView.SetItemState USING 5,LVIS_SELECTED,LVIS_SELECTED
The STATE and the STATEMASK are defined like this:
- STATE is the bit values you want the bitmask to take.
- STATEMASK is the bits you want to control.
That is, if you want to turn on the FOCUSED and SELECTED bits (the low order two), you could use
- STATE = 0xFFFF (binary 11111111 11111111) which says to set every masked bit to 1.
- STATEMASK = 0x0003, (binary 00000000 00000011) which says to set the low order two bits to whatever is in the STATE mask. In this case, ONE's.
To DE-SELECT a line that has been clicked you want to turn off the SELECTED bit which is in bit position 2. You might also want to turn off bit 1. Adding bits 1 and 2 you get a value of hex 3. Therefore your STATE mask will be all ZEROs, which is what you want to set. The STATEMASK is 0x0003 which is binary 00000000 00000011, saying set bits 1 and 2.
In this example below we first find the next selected line starting at line "-1" (which is BEFORE the first line). We use LVNI_SELECTED which is defined in PLBMETH.INC. Then we use that line (pointed to by LVIX) and reset the state bits to turn off the selection and the focus:
LVIX FORM 5
SEQ FORM "-1"
HEX_0000 INTEGER 2,0x0000
HEX_0003 INTEGER 2,0x0003
MyListView.GetNextItem GIVING LVIX, USING *Flags=LVNI_SELECTED,*Start=SEQ
MyListView.SetItemState USING LVIX,HEX_0000,HEX0003
You can also use some of the predefined items found in PLBMETH.INC:
MyListView.SetItemState USING LVIX,LVIS_DROPHILITED,LVB
The following notes come from the Sunbelt on-line manual:
[label] {object}.SetItemState [GIVING {return}] USING [*Index=]{index}:
[*State=]{state},[*Statemask=]{statemask}
2. The {state} value is a constant as defined in PLBMETH.INC or a numeric variable indicating one of the following label justifications:
Value |
Constant |
The item ... |
0x0001 |
LVIS_FOCUSED |
has the focus and is surrounded by a standard focus rectangle.
Although more than item may be selected, only one item can have focus. |
0x0002 |
LVIS_SELECTED |
is selected. The appearance of a selected item depends on
whether it has the focus and the system colors used to indicate selection. |
0x0004 |
LVIS_CUT |
is marked. |
0x0008 |
LVIS_DROPHILITED |
is highlighted as a drag-and-drop target. |
0x0F00 |
LVIS_OVERLAYMASK |
has its overlay image index set. |
0xF000 |
LVIS_STATEIMAGEMASK |
has its state image mask set. |
3. The required {statemask} is a value from the table above that indicates the valid initial state values. An item's state value includes a set of bit flags. The state value can also include image list indexes that indicate the item's state image and overlay image.
4. The {statemask} parameter specifies the state bits modified, and the {state} parameter specifies the new value for those bits. To set a bit in the item's internal state, set it in both the {statemask} and {state} parameters. To clear a bit in the item's internal state, set it in the {statemask} parameter and clear it in the {state] parameter. To leave a bit unchanged in the item's internal state, clear it in the {statemask} parameter.
|
.
. Method Constants defined in PLBMETH.inc for listview (version 9.4d):
.
LVA_DEFAULT Integer 4,"0x0000" ;Arrange Code
LVA_ALIGNLEFT Integer 4,"0x0001"
LVA_ALIGNTOP Integer 4,"0x0002"
LVA_SNAPTOGRID Integer 4,"0x0005"
LVFI_PARTIAL Integer 4,"0x0008" ;FindItem Flags
LVNI_ALL Integer 4,"0x0000" ;GetNextItem Flags
LVNI_FOCUSED Integer 4,"0x0001"
LVNI_SELECTED Integer 4,"0x0002"
LVNI_CUT Integer 4,"0x0004"
LVNI_DROPHILITED Integer 4,"0x0008"
LVNI_ABOVE Integer 4,"0x0100"
LVNI_BELOW Integer 4,"0x0200"
LVNI_TOLEFT Integer 4,"0x0400"
LVNI_TORIGHT Integer 4,"0x0800"
LVCFMT_LEFT Integer 4,"0x00000000" ;InsertColumn Format
LVCFMT_RIGHT Integer 4,"0x00000001"
LVCFMT_CENTER Integer 4,"0x00000002"
LVCFMT_FIXED_WIDTH Integer 4,"0x00000100" ;Windows Vista 9.4B
LVCFMT_IMAGE Integer 4,"0x00000800" ;Column has image 8.6B
LVCFMT_BITMAP_ON_RIGHT Integer 4,"0x00001000" ;Image displayed on right 8.6B
LVCFMT_COL_HAS_IMAGES Integer 4,"0x00008000" 8.6B
LVCFMT_NO_DPI_SCALE Integer 4,"0x00040000" ;Windows Vista 9.4B
LVCFMT_FIXED_RATIO Integer 4,"0x00080000" ;Windows Vista 9.4B
LVCFMT_SPLITBUTTON Integer 4,"0x01000000" ;Windows Vista 9.4B
.
;InsertItem State/StateMask
;GetItemState StateMask
LVIS_FOCUSED Integer 4,"0x0001" ;SetItemState State/StateMask
LVIS_SELECTED Integer 4,"0x0002"
LVIS_CUT Integer 4,"0x0004"
LVIS_DROPHILITED Integer 4,"0x0008"
LVIS_OVERLAYMASK Integer 4,"0x0F00"
LVIS_STATEIMAGEMASK Integer 4,"0xF000"
LVS_EX_GRIDLINES Integer 4,"0x00000001" ;SetExtendedStyle MASK/STYLE
LVS_EX_CHECKBOXES Integer 4,"0x00000004" ;& GetExtendedStyle return value
LVS_EX_TRACKSELECT Integer 4,"0x00000008" ;
LVS_EX_HEADERDRAGDROP Integer 4,"0x00000010"
LVS_EX_FULLROWSELECT Integer 4,"0x00000020" ;applies to report mode only
LVS_EX_ONECLICKACTIVATE Integer 4,"0x00000040"
LVS_EX_UNDERLINEHOT Integer 4,"0x00000800"
LVS_EX_UNDERLINECOLD Integer 4,"0x00001000"
$LV_XMLRD_IGNORE_ATTR Integer 4,"0x001" ;LoadXmlFile Options 9.0B
$LV_XMLRD_NO_CONV Integer 4,"0x002"
$LV_XMLRD_USECOLUMNS Integer 4,"0x004"
$LV_XMLWR_NOATTR Integer 4,"0x001" ;SaveXmlFile Options 9.0B
$LV_XMLWR_NOCOLS Integer 4,"0x002"
$LV_XMLWR_NOATTRCOLS Integer 4,"0x004"
$LV_XMLWR_OUTPUTASATTR Integer 4,"0x008"
$LV_XMLWR_OUTPUTSEL Integer 4,"0x010"
$LV_XMLWR_OUTPUTCHK Integer 4,"0x020"
$LV_XMLWR_OUTPUTALLATTR Integer 4,"0x040"
$LV_XMLWR_NOZEROWIDTH Integer 4,"0x080"
$LV_XMLWR_ISO8859 Integer 4,"0x100" 9.2B
$LV_XMLWR_LOWERCASE Integer 4,"0x200" 9.3A
$LV_XMLWR_UPPERCASE Integer 4,"0x400" 9.3A
$LV_CSVRD_QUOTED Integer 4,"0x001" ;LoadCsvFile Options 9.0B
$LV_CSVRD_INPUTHEADER Integer 4,"0x008" 9.1A
$LV_CSVRD_NOZEROWIDTH Integer 4,"0x010" 9.1A
$LV_CSVWR_QUOTED Integer 4,"0x001" ;SaveCsvFile Options 9.0B
$LV_CSVWR_OUTPUTSEL Integer 4,"0x002"
$LV_CSVWR_OUTPUTCHK Integer 4,"0x004"
$LV_CSVWR_OUTPUTHEADER Integer 4,"0x008" 9.1A
$LV_CSVWR_NOZEROWIDTH Integer 4,"0x010" 9.1A
$LV_CSVWR_LOWERCASE Integer 4,"0x020" 9.3A
$LV_CSVWR_UPPERCASE Integer 4,"0x040" 9.3A
$LV_GETITEM_QUOTED Integer 4,"0x001" ;GetItemTextAll Options 9.1A
$LV_GETITEM_NOZEROWIDTH Integer 4,"0x010" 9.1A
$LV_SETITEM_QUOTED Integer 4,"0x001" ;SetItemTextAll Options 9.1A
$LV_SETITEM_NOZEROWIDTH Integer 4,"0x010" 9.1A
$LV_SETITEM_REPLACEROW Integer 4,"0x020" 9.1C
$LV_SETITEM_SKIPNULL Integer 4,"0x040" 9.1C
.
$LV_EXTENDED_FORMAT1 Integer 4,"0x001" ;SubitemHitTest Flags 9.4A
.
LISTIEW DRAG DROP
This information came from TREEVIEW drag/drop discussions. It may and may not apply to the LISTVIEW.
There is no specific drag drop. But you can simulate the action by using the MOUSEDOWN and MOUSEUP events. On each of these you grab EventResult and split it up to get the H:V positions of the mouse. Then you use the ItemHitTest method to translate that H:V coordinate into the handle of the line clicked on. Using that data you can get data from the lines.
tv_mousedown DISPLAY "TV MOUSE DOWN "
CALL CLEAR_PENDING_INFO ;Start over on any mouse DOWN
CALL GET_EVENT_STUFF ;get the associated data
MOVE TV_HANDLE,FROM_HANDLE ;the new position.
RETURN
.
tv_mouseup DISPLAY "TV MOUSE UP "; ;this is same as a DROP
CALL GET_EVENT_STUFF ;identify line they DROPPED on
MOVE TV_HANDLE,TO_HANDLE ;this is line handle
CALL DROP_ON_PRESS_LINE
RETURN
.
get_event_stuff
MOVE EventResult,WORK11
UNPACK WORK11,WORK03,#H,#V
F_TV1.ItemHitTest GIVING TV_HANDLE:
USING #H, #V
F_TV1.GetItemText GIVING TV_DATA USING TV_HANDLE
RETURN
Listview GRID LINES
The Listview normally does NOT have grid lines. If you want them you have to use the
SetExtendedStyle
method as follows:
ListView001.SetExtendedStyle:
USING LVS_EX_GRIDLINES:
LVS_EX_GRIDLINES
That variable "LVS_EX_GRIDLINES" is defined in the PLBMETH.INC include unit provided by Sunbelt. You probably can't get away with the continuation after the USING, but you can put a continuation after the first "LVS_EX_GRIDLINES".
Sorting Listviews
LISTVIEWs are typically implemented in such a way that the user can click on a column header and sort the listview on that column. If the same column is clicked again, the list is sorted in the opposite sequence (ascending then descending then ascending, etc.) Some software indicates the sort direction with a small up/down arrow in the right corner of the heading.
Sunbelt has provided a SortColumn method which implements this feature. You must be on release 8.6F or higher to use this.
Example code:
In the PLFORM we put the following code on the COLUMN CLICK event:
MOVE "LV1-COLCLICK",ACTION
MOVE #EventType, EventType
MOVE #EventResult, EventResult
MOVE #EventObjId, EventObjId
MOVE #EventChar, EventChar
MOVE #EventMod, EventMod
NOTE: If you are going to allow clicking on the column header you must set the property SortHeader to true. If you don't then you won't get the COLUMN CLICK event when the column header is clicked.
In the mainline program we wait for an event then check for the "LV1-COLCLICK" action. When we see that we call the following routine:
The first time any column is sorted, we want to force it to ASCENDING. What's more, if the user sorts on one column, then shifts to a DIFFERENT column, we want that new column to always sort ASCENDING first. If they want it descending, they can click it a second time.
By initializing the LAST_COL_SORT to "999" (or any number greated than your total number of columns), we insure that the first time a column is clicked the program will think that it's a different column from the last time and it will force ascending sequence.
If the column has a special format, like a date, you can take that into consideration too.
In the example below, assume that column 1 is a date column. We look for that and if found we change the sort code to either 5 or 6 for date format and we add a mask to match the column.
SORT_DIRECTION FORM 2 ;0,3=None: 1=Ascending: 2=Descending
LAST_SORT_COL FORM "999"
LV_DIRECTION FORM 2
LV_001_COLS FORM 3
LV_TOT_COLS FORM 3
f1_lv_001_colclick
MOVE EventResult,NWK03
DISPLAY "LV1 COLCLICK on col ",NWK03 ;column is ZERO BASED
.
IF (NWK03 = LAST_SORT_COL) ;when SAME column, flip direction
IF (LV_DIRECTION = 2) ;FIRST TIME direction is ZERO
MOVE "1",SORT_DIRECTION ;alpha ASCENDING
ELSE
MOVE "2",SORT_DIRECTION ;alpha DESCENDING
ENDIF
IF (LV_DIRECTION = 6)
MOVE "5",SORT_DIRECTION ;date ASCENDING
ELSE
MOVE "6",SORT_DIRECTION ;date DESCENDING
ENDIF
ELSE ;when different col
IF (NWK03 = 1) ;force code to ascending
MOVE "5", SORT_DIRECTION ;date ASCENDING
ELSE
MOVE "1", SORT_DIRECTION ;alpha ASCENDING
ENDIF
.
IF (NWK03 = 1) ;If this is a DATE column
F9120_LV_Files.SortColumn GIVING NWK01:
USING *Column=NWK03:
*Type=SORT_DIRECTION:
*Mask="mm-dd-yyyy"
ELSE
F9120_LV_Files.SortColumn GIVING NWK01:
USING *Column=NWK03:
*Type=SORT_DIRECTION
ENDIF
MOVE SORT_DIRECTION,LV_DIRECTION ;remember direction
DISPLAY "After column sort:",*HON,NWK01,*HOFF
RETURN
IMAGES IN THE COLUMN HEADINGS
Listviews typically use those little arrow up/down to indicate the sort direction. This can be done via PL/B too.
These are
preliminary notes based on conversation with Matthew Lake 10/9/2002:
- You need an image list in your PL/B program. That's a windows object.
- Create the Image list in the program.
- Use AddBmp to add an image (or a resource) to the list.
If you use an image, it must be a separate file.
If you use a RESOURCE, the image is imbedded into the PLC.
(More on resources later)
- Use the AddImageSmall method for the listview to add the images to the listview. This just associates the image with the Listview, it does not activate any images.
- When inserting columns into the listview, use InsertColumnEx rather than the standard InsertColumn method. This "ex" version allows you to define an image for the column.
- Later use SetColumnImage method to actual change the images to what you want.
NOTE: The listview will normally take the first image in the list. You can get NO image by using a high image number, outside the range of the imagelist, to get nothing.
Now, go look at the RESOURCES article for information on imbedding resources into the PLC program.
CHECK BOXES IN LISTVIEWS
The following is a discussion with Matthew Lake from Sunbelt in Feb 2001:
- When enabled, the checkboxes appear at the left end of the line
but they don't line up under a column header.
Are you aware of any way to give them a heading or other info?
- Once the check boxes are checked, how does the program get that information? I've tried all the methods I can find and none seem to recover any data about the state of the check box.
- Within the program, can you set the state of a check box on or off. For example, I'd like to make a button to SELECT ALL or CLEAR ALL.
Clicking on the CHECKBOX generates a CLICK event followed by a CHANGE event.
but note.....
The CLICK points to the last line that was CLICKED, The CHANGE points to the line on which the check box is clicked. It seems like the CLICK should point to the same line that the CHANGE oints to.
REPLY
The click event on all objects occur before the object processes the even. This is true with radio buttons, checkboxes, listviews and treeviews. Therefore, #Event* will usually contain the state of the object when the click occurred rather than the effect of the click.
From MSDN Library:
LVS_EX_CHECKBOXES Version 4.70. Enables check boxes for items in a list view control. Effectively, when set to this style, the control will create and set a state image list using DrawFrameControl. Check boxes are visible and functional with all list view modes. The state of the check box for a given item is obtained using the ListView_GetCheckState macro.
From Commctlr.h (windows sdk)
#define ListView_GetCheckState(hwndLV, i) \
((((UINT)(SNDMSG((hwndLV), LVM_GETITEMSTATE, (WPARAM)i,
LVIS_STATEIMAGEMASK))) >> 12) -1)
so in PLB, this translates to
;this value is defined in the PLB language reference
;under the GetItemState Method.
LVIS_STATEIMAGEMASK integer 2,"0xF000"
Listview.GetItemState giving state using Index,LVIS_STATEIMAGEMASK
divide "4096",state
sub "1",state
...
the state variable should now contain the setting of the check box. (if my math is right :o) )
To set the check box, reverse the process and use SetItemState.
AN INTERESTING DISCUSSION FROM THE SUNBELT WEB BOARD
JimKovalsky
Reged: 12/19/99
Loc: Hadley, MI
Checkbox in a Listview -- placement?
#12159 - 10/12/05 01:26 PM
Maybe I'm missing the obvious, or maybe this just can't be done.....
Can I make the checkboxes in a listview be anywhere other than the 1st column?
Jim
see
Reged: 05/29/98
Posts: 572
Loc: Liberty, MO USA
Re: Checkbox in a Listview -- placement? [Re: JimKovalsky]
#12160 - 10/12/05 02:16 PM
Jim:
What are you trying to accomplish?
--Stuart Elliott
--Clay County Judicial I/S
JimKovalsky
Reged: 12/19/99
Posts: 334
Loc: Hadley, MI
Re: Checkbox in a Listview -- placement? [Re: see]
#12161 - 10/12/05 02:25 PM
I want to have checkboxes in my LV, but I want them as the 4th column when displayed... Matthew has suggested that I try reordering the columns -- I'll see if that works...
Jim
JimKovalsky
Reged: 12/19/99
Posts: 334
Loc: Hadley, MI
Re: Checkbox in a Listview -- placement? [Re: JimKovalsky]
#12163 - 10/12/05 02:42 PM
OK.. That does work. You can set the CHECKBOX property=true, and then do a .setcolumnorder using *index=0,*order=whatyouwant
The check boxes will move to another position in the listview...
Jim
see
Reged: 05/29/98
Posts: 572
Loc: Liberty, MO USA
Re: Checkbox in a Listview -- placement? [Re: JimKovalsky]
#12165 - 10/12/05 03:11 PM
Yeah, but I was curious as to WHY you'd want the checkbox in the 4th column.
Is the "value" you want checkable just a field in a record (a property of the item in the list)? Does it have a more inherent "selection" purpose (do we want to include the item or not)?
If the latter, I would think it should be in the 1st column.
If the former, what if you have more than one Y/N-type field?
I'm just musing about your "issue".
--Stuart Elliott
--Clay County Judicial I/S
JimKovalsky
Reged: 12/19/99
Loc: Hadley, MI
Re: Checkbox in a Listview -- placement? [Re: see]
#12166 - 10/12/05 03:36 PM
This scenario has to do with items on an order that are being shipped, and whether or not each specific item had a process occur. I started off using a Y/N field in the listview, and realized that it seemed the perfect place for a checkbox. After all, isn't that the PURPOSE of a checkbox -- to represent an ON or OFF conidition?
Of course, as I went further, this became another case of reality and theory not coming together....
On further examination, there is a THIRD condition -- "Not Applicable". In this case I would need to disable to checkbox for specific lines in the listview, and I don't believe this is possible. Further, I'm using a routine that lets me edit-in-place on top of the listview, so that the user doesn't need the mouse. This doesn't play well with the checkboxes....
Now that I know it CAN be done, I guess I'll go back and use a Y/N/NA column... If it's Y or N, they can change it, but if it's NA, I'll prevent any changes.. I could make another hidden column to indicate the value of my tri-state condition, and still prevent them changing the checkbox value at the right time, but it would take still another column to indicate to the user why they can't change it....
It's been a good learning experience anyways... somewhere later I know I'll use it!
Jim
ssansom
Reged: 03/09/98
Loc: Austin, TX USA
Re: Checkbox in a Listview -- placement? [Re: JimKovalsky]
#12167 - 10/13/05 09:38 AM
This is possible in a regular checkbox via the "tristate" property that allows "Yes", "No", and "Neither" ... I don't know if tristate is possible in a Listview checkbox but might be worth a look ...
Stephen Sansom
Matt
Reged: 05/08/00
Loc: Rusk, Tx USA
Re: Checkbox in a Listview -- placement? [Re: ssansom]
#12168 - 10/13/05 04:47 PM
Another option would be to create your own checkbox images for use with the InsertItemEx *image. Then use the SetItemImage method to reflect the state.
Just another thing to think about/experiment with...
Matthew
OMS
Reged: 01/13/98
Loc: Herlev, Denmark
Re: Checkbox in a Listview -- placement? [Re: JimKovalsky]
#12169 - 10/14/05 02:53 AM
The "checkbox effect" (On/Off/NA) could be accomplished by inserting an AttrColumn into the listview.
This way the individual items can have individual font and/or colors.
E.g. Strike-out font for NA, GREEN for Yes and RED for No.
Just a thought.
Ole
A DATALIST
is a different object.
It is a box containing lines of data and a scroll bar
on the right side.
You can scroll to any line and click or double click on the
line to work with the data.
Link to DATALIST article.