On Feb 28, 2007, at 10:00 PM, Ken Dibble wrote:

> So then, insofar as possible, I use the exact same database, table,  
> and
> Dabo code to bind a textbox to a single field in in my table and  
> populate
> it by requesting 1 record, and it doesn't work. Instead, the GRID  
> shows the
> one row I want, and the textbox shows nothing. If I get rid of the  
> grid and
> just do the textbox thing with everything else the same, it doesn't do
> anything at all.
>
> As best I can tell, it should work. Please try to show me the exact  
> point
> where I'm wrong.

        There are several, presented in-line.

> ## *!* ## Dabo Code ID: dButton-dForm
> def onHit(self, evt):
>          # Code from the wiki How-To:
>          # This connection works fine, as the rest of this code
>          # demonstrates.
>          self.Form.Application.addConnectFile("C:\DaboProjects 
> \MyMusic\mymusic.cnxml")

        First, you have lots of code in your button's event handler. This is  
never a good idea. Event handlers should always call methods. Buttons  
are just dumb objects that get clicked; if the behavior desired uppon  
being clicked is anything other than a one-liner, it should be in a  
form method, and the event handler should call that method.

>          # Code from the wiki How-To:
>          conn = self.Form.Application.getConnectionByName("main")
>
>          # Code from the wiki How-To:
>          biz = dabo.biz.dBizobj(conn)

        Again, buttons shouldn't know about files in the file system, or  
business objects, or any of this stuff.

>          # Code from the wiki How-To:
>          # These items are correct, as the grid will demonstrate.
>          biz.DataSource = "musSongs"
>          biz.KeyField = "pkid"
>
>          # Code from the wiki How-To (don't know why; it doesn't
>          # seem to serve any purpose, like giving me a form-level
>          # reference to the biz object that I can use, but if
>          # I comment it out, the grid doesn't populate):
>          # Add it to the form's collection of bizobj references
>          self.Form.addBizobj(biz)

        You say you don't know why you tell the form about this; the reason  
is that the form acts as the central mediator for all the UI  
controls, and it needs to know the bizobjs it has to deal with.
        
        The general paradigm is that controls only talk to their form, their  
siblings and their parents. The form talks to any UI object, the app,  
or its bizobjs. Bizobjs only talk to its cursors and the app, and  
cursors only talk to their data.

>          # Code from the wiki How-To:
>          # These items are correct, as the grid will demonstrate.
>          # Now set up the fields
>          biz.addField("pkid")
>          biz.addField("songname")
>          biz.addField("compdate")
>
>          # Code from the wiki How-To:
>          # Run the query
>          biz.requery()
>
>          # Code from the wiki How-To (works perfectly; if I
>          # don't enter a number in the keyBox textbox, all of my
>          # songs appear in the grid. This indicates that:
>          #   1. The MySQL database is configured properly for
>          #      Dabo.
>          #   2. The connection is configured properly in Dabo.
>          #   3. I'm referring to the proper fields and DataSource.
>          #   4. There is data in the table.)
>          # The grid may not exist yet; it may be created after
>          # this method is run. So use 'callAfter()' to delay
>          # populating the grid until we know it is ready.
>          dabo.ui.callAfter(self.Form.populateGrid)

        Actually, this code was written before I added the 'afterInitAll()'  
method. Were it written today, I would put the code in that method,  
eliminating the need for callAfter(). I've just updated the wiki to  
note this.

>          # Now try to show just one song in the thisSong textbox,
>          # assuming the user entered a number in the keyBox. If
>          # s/he didn't, the following code doesn't do anything
>          # that I can see.
>          myPK = self.Form.keyBox.Value
>
>          # Assuming I put 3 in the keyBox,Prints 3, as I'd expect.
>          print myPK
>
>          biz.setWhereClause("musSongs.pkid = " + myPK)
>
>          # Assuming I put 3 in the keyBox, this prints
>          # "musSongs.pkid = 3", also as I'd suspect.
>          # This tells me that the query is going to be correct.
>          print biz._CurrentCursor.sqlManager._whereClause
>
>       Or, more simply, biz.getWhereClause().
>
>          biz.requery()

        This has to be started at the form level. Dabo uses the Chain of  
Responsibility pattern, and you're bypassing the initial part of the  
chain by jumping directly to the bizobj. The form needs to be in the  
call, because the bizobj might raise an exception which the form is  
designed to catch. The form also causes the value of the active  
control to be flushed to the bizobj, preventing things from being out  
of sync. After the query, it raises events and refreshes the controls  
on the form.

>          # At this point, the grid shows one row--the row I
>          # wanted--but the thisSong texbox shows nothing.
>          dabo.ui.callAfter(self.Form.showSong)

        That's because a) the textbox doesn't have a DataSource/DataField  
set, and b) you bypassed the form in the requery call, which would  
have refreshed all controls to the current state of the data.

> ## *!* ## Dabo Code ID: dForm-top
> def afterInit(self):
>          # Even though I do this again in the button's onHit,
>          # I do it here because the form will not run if there's
>          # no code in this method.
>          self.Application.addConnectFile("C:\DaboProjects\MyMusic 
> \mymusic.cnxml")

        This should only be done once. All it does is register the  
connection definition with the app. It doesn't actually connect.

> def populateGrid(self):
>          # Just like in the Wiki How-To:
>          self.gridKen.DataSource = "musSongs"
>
> def showSong(self):
>          # Just like in the Wiki How-To except it's a dTextBox
>          # instead of a dGrid.
>          self.Form.thisSong.DataSource = "musSongs"

        I'm guessing this is probably some vestigial VFP thinking going on  
here. VFP has a single property to bind controls to data:  
ControlSource. In Dabo we've separated this into two logical pieces:  
DataSource and DataField. The reason we only set the DataSource in  
the grid is because the DataField is at the column level; columns in  
a grid share a common DataSource.

> Here's the .cdxml file for the form:

        This looks fine, but you really should set the data binding props  
for the control in the Designer, and not in code at runtime.
        
        So step one is to refactor this into a couple of form methods. One  
that does the setup stuff and happens automatically at startup (e.g.,  
creates the bizobj), and one that responds to the click event. Then  
move all of your button code out of the button, and put it where it  
belongs: in the form.
        
        BTW, another VFP-ism that I noticed someone else getting bit by is  
the meaning of 'self.Form'. In Dabo, it means 'the form that contains  
me'. This is different than VFP's 'Thisform', which means 'the form  
that contains me, or myself if I'm the form'. If you're in a method  
of the form and use the 'self.Form' reference, it will return None,  
since no form contains that form. Just use 'self'.

-- Ed Leafe
-- http://leafe.com
-- http://dabodev.com



_______________________________________________
Post Messages to: [email protected]
Subscription Maintenance: http://leafe.com/mailman/listinfo/dabo-users

Reply via email to