| Conditionally Colouring Records |
|
|
|
| Written by Jan Stegehuis | |
| 05 September 2007 | |
|
Preface I have included a fully functional demo (Paradox® 9),
based on the technique described below. After downloading into the folder of
your choice, make that folder :WORK:, run the form and
follow the instructions. The form allows to select individual
records and indicate those with a different colour. Introduction I have many times wanted to be able to colour
tableRecords in a tableFrame, based on certain conditions: like a certain value
in one of the recordFields or to indicate, that the user selected them for some
reason. When I started working with Paradox I tried to get this to work, but
failed to let the colours stick to their own records. One scroll and everything
was out of place. The years after, I never tried again, convinced that
it could not be done and/or was too complicated to be of any real use. And I
certainly was not about to place any real amount of code in things like the
newValue event. Much too tricky for my taste. A short time ago however, I had to solve a problem
with a form and working on that I realised that I had been wrong al that time
and that it cóuld be done. And that it actually was not too complicated at all.
I simply had been going about the problem the wrong way; looking at it from a
wrong mindset. BackGround Somewhere in my mind I had stashed the information,
that the tableFrameRecord was 1:1 the same as the tableRecord. Well, it isn't.
The tableFrameRecord is a uiObject, that acts as a
window for 1 tableRecord at a time. If you set a record’s colour using oPal,
you set the colour of the record-uiObject; nót that of the tableRecord. The
tableRecord hás no colour property. After a scroll the record-uiObject is still the same,
it only acts as a window for a different tableRecord. If you had coloured it
before, then it will now probably have the wrong colour and you will have to
reset/recolour it. When running a form, the record-uiObject gets
replicated as many times as will fit in the tableFrame, whereby each of it’s occurrences gets it’s own uiObject-name. So there are
not as many record-uiObjects as there are tableRecords; there are only as many
as will fit in the frame. Each record-uiObject acts as a row for the tableFrame
grid. Knowing the names of the different record-uiObjects,
you can approach each of them separately and set it’s
properties. These are the properties (and methods and events), that are
surfaced through the object inspector. During runtime however, the
record-uiObject also gives access to the properties of the underlying tableRecord.
And to those of the fields of that tableRecord. Panting the rows. So if you know the (uiObject)names
of the individual rows, then you can colour them at wil, based on some sort of
condition. There is however an easier and more flexible way, to do it, using
the First and Next properties of the tableFrame and it’s “children”. So all you
need, to colour the rows of any tableFrame at any point in time, is this little
piece of code: proc paintRows() var tableChild uiObject loopSwitch logical endVar loopSwitch = true tableChild.attach(myTableFrame.first) while loopSwitch if
tableChild.class = "Record" then switch case tableChild.myField.value meets condition : tableChild.color = red otherwise : tableChild.color = white endSwitch endIf if tableChild.next = blank() then loopSwitch =
false else tableChild.attach(tableChild.next) endIf endWhile endProc Remarks
Catching
the scroll (readonly table) The above piece of code works fine if you can call it
every time a scroll took place. Paradox unfortunately does not surface a
scroll-event, so you have to simulate one yourself. This is not too difficult
either. The first thing that happens after a scroll, is an
arrive on a new record. So all you need to do, is to catch the arrive and see if the record underneath the first row is
still the same record as it was at the previous arrive-event. If it isn’t, the
tableFrame scrolled it’s records. The next piece of
code will do the trick. The tableFrame’s Var window var firstRecord uiObject ;// the first
record-uiObject (row) in the frame firstRecordNo longInt ;//
the tableRecord underneath firstRecord endVar The tableFrame’s Open event var tableChild uiObject loopSwitch logical endVar method open(var eventInfo Event) doDefault loopSwitch = true tableChild.attach(myTableFrame.first) while loopSwitch switch case tableChild.class = "Record" : firstRecord.attach(tableChild) firstRecordNo =
firstRecord.recNo loopSwitch =
false case tableChild.next
= blank() : msgStop("program
error", "there are no records in this tableFrame") loopSwitch =
false otherwise : tableChild.attach(tableChild.next) endSwitch endWhile endMethod The record’s Arrive event method arrive(var eventInfo
MoveEvent) doDefault If
firstRecord.recNo <> firstRecordNo then firstRecordNo =
firstRecord.recNo paintRows() endif endMethod Remarks
Catching
the scroll (editable tables) The above code is sufficient for readonly tables; for
editable tables however, more is involved. Thére, also things like partial
scrolls occur as a result of inserting and deleting records from the table. You
can’t catch these, using the code above, since the record underneath the first
row doesn’t change. You have to catch the Insert- and Delete events themselves
and call paintRows() there. For an example on how to
do that, check the code in the demo form. There is one remark however, that I want to make on
that subject. When Paradox inserts a new record, it completely locks the entire
tableFrame. As soon as you arrive at that newly inserted blank record, there is
nó way, you can do ánything with any of the other records. Not even something
as simple as checking a fieldvalue. Still you need to repaint the rows and there is only
one way to do that: paint them befóre you let the insert take place. Catch the
dataInsertRecord action, paint the rows beforehand and then let doDefault do it’s thing. But you can’t do that with the paintRows() method above; you need an adapted version, that
is capable of colouring one-out-of-sequence (see the proc tableBox.paintRowsBeforeInsert()
in the demo form). Appending new records is yet another story. If you
don’t need it, I would advise to set “autoAppend” for the table to “False. The
demo form does support it, but I don’t know, how
stable it is in exceptional cases. Appended records simply show up, they don’t
have the “Inserting” property set to True and they
don’t trigger a dataInsertRecord action. Not until the user types his first
character in the first field. The only way you can distinguish this situation,
is the fact, that the active (= the appended) record has it’s
BlankRecord property set to True. This property normally means that there is no
tableRecord underneath the record; a situation that normally only occurs when
there are more rows in the tableFrame, then there are records in the table. Paradox oddities (important!) Paradox would not be Paradox if it would not have a
thing or two, that makes something simple not really
want to work the way you would expect it. That is also the case for this
technique and it has to do with setting the colour of objects in connection
with the keyboard focus. Something that gives problems in other area’s to. Paradox seems to be moving an underwater focus around
in order to change the colour of it’s uiObjects. And
sometimes it seems to get confused about what the focus was,
that it moved. In those cases it moves the keyboard focus away from what should
be the active field or it looses track of the keyboard focus altogether. Two
examples: If you open the form and after the doDefault of it’s Open event do an initial colouring of the rows, you
will find the keyboard focus on the first field of the last row in the
tableFrame. There is no reason whatsoever why this should be the case, but it
still is. So after the initial paintRows() you have to
use myTableFrame.home() to send the keyboard focus back to where it belongs. It
is only during the Open that this happens. During normal operation all seems to
work fine. When during normal operation, you do something with an
object outside the tableframe, that causes a paintRows()
within the tableFrame, Paradox looses track of the keyboard focus. It will
still indicate the active field as having the focus, but it will not respond to
keyboard commands like Up, Down, Left and Right anymore. I found this one out,
when putting a button on the form, that allows the user to set his selected
records back to “none”, which causes a repaint. You have to grab the mouse and click within the
tableFrame, to get the focus back to work or do something like press F2 (toggle
FieldView). Of course you don’t want to burden your users with this. In the
demo I use oPal to do an active.postAction(EditToggleFieldView)
twice after each paintRows(), in order to get the keyboard focus back to it’s
normal operational state. I haven’t found any real problems with this
“workaround” yet. In more intricate forms however, it might possibly become a
problem of it’s own. |
|
| Last Updated ( 05 September 2007 ) |
| < Prev | Next > |
|---|





