Alpha Software Mobile Development Tools:   Alpha Anywhere    |   Alpha TransForm subscribe to our YouTube Channel  Follow Us on LinkedIn  Follow Us on Twitter  Follow Us on Facebook

Announcement

Collapse

The Alpha Software Forum Participation Guidelines

The Alpha Software Forum is a free forum created for Alpha Software Developer Community to ask for help, exchange ideas, and share solutions. Alpha Software strives to create an environment where all members of the community can feel safe to participate. In order to ensure the Alpha Software Forum is a place where all feel welcome, forum participants are expected to behave as follows:
  • Be professional in your conduct
  • Be kind to others
  • Be constructive when giving feedback
  • Be open to new ideas and suggestions
  • Stay on topic


Be sure all comments and threads you post are respectful. Posts that contain any of the following content will be considered a violation of your agreement as a member of the Alpha Software Forum Community and will be moderated:
  • Spam.
  • Vulgar language.
  • Quotes from private conversations without permission, including pricing and other sales related discussions.
  • Personal attacks, insults, or subtle put-downs.
  • Harassment, bullying, threatening, mocking, shaming, or deriding anyone.
  • Sexist, racist, homophobic, transphobic, ableist, or otherwise discriminatory jokes and language.
  • Sexually explicit or violent material, links, or language.
  • Pirated, hacked, or copyright-infringing material.
  • Encouraging of others to engage in the above behaviors.


If a thread or post is found to contain any of the content outlined above, a moderator may choose to take one of the following actions:
  • Remove the Post or Thread - the content is removed from the forum.
  • Place the User in Moderation - all posts and new threads must be approved by a moderator before they are posted.
  • Temporarily Ban the User - user is banned from forum for a period of time.
  • Permanently Ban the User - user is permanently banned from the forum.


Moderators may also rename posts and threads if they are too generic or do not property reflect the content.

Moderators may move threads if they have been posted in the incorrect forum.

Threads/Posts questioning specific moderator decisions or actions (such as "why was a user banned?") are not allowed and will be removed.

The owners of Alpha Software Corporation (Forum Owner) reserve the right to remove, edit, move, or close any thread for any reason; or ban any forum member without notice, reason, or explanation.

Community members are encouraged to click the "Report Post" icon in the lower left of a given post if they feel the post is in violation of the rules. This will alert the Moderators to take a look.

Alpha Software Corporation may amend the guidelines from time to time and may also vary the procedures it sets out where appropriate in a particular case. Your agreement to comply with the guidelines will be deemed agreement to any changes to it.



Bonus TIPS for Successful Posting

Try a Search First
It is highly recommended that a Search be done on your topic before posting, as many questions have been answered in prior posts. As with any search engine, the shorter the search term, the more "hits" will be returned, but the more specific the search term is, the greater the relevance of those "hits". Searching for "table" might well return every message on the board while "tablesum" would greatly restrict the number of messages returned.

When you do post
First, make sure you are posting your question in the correct forum. For example, if you post an issue regarding Desktop applications on the Mobile & Browser Applications board , not only will your question not be seen by the appropriate audience, it may also be removed or relocated.

The more detail you provide about your problem or question, the more likely someone is to understand your request and be able to help. A sample database with a minimum of records (and its support files, zipped together) will make it much easier to diagnose issues with your application. Screen shots of error messages are especially helpful.

When explaining how to reproduce your problem, please be as detailed as possible. Describe every step, click-by-click and keypress-by-keypress. Otherwise when others try to duplicate your problem, they may do something slightly different and end up with different results.

A note about attachments
You may only attach one file to each message. Attachment file size is limited to 2MB. If you need to include several files, you may do so by zipping them into a single archive.

If you forgot to attach your files to your post, please do NOT create a new thread. Instead, reply to your original message and attach the file there.

When attaching screen shots, it is best to attach an image file (.BMP, .JPG, .GIF, .PNG, etc.) or a zip file of several images, as opposed to a Word document containing the screen shots. Because Word documents are prone to viruses, many message board users will not open your Word file, therefore limiting their ability to help you.

Similarly, if you are uploading a zipped archive, you should simply create a .ZIP file and not a self-extracting .EXE as many users will not run your EXE file.
See more
See less

Problems with <ARRAY>.SORT() and PROPERTIES_ENUM() functions

Collapse
X
 
  • Filter
  • Time
  • Show
Clear All
new posts

  • exeye
    replied
    Re: Problems with &lt;ARRAY&gt;.SORT() and PROPERTIES_ENUM() functions

    An update...

    I finally got my Progressive Find working well enough to use. It's listed below (configured to work with AlphaSports) in case it proves useful to anyone. I still haven't figured out why the <array>.SORT() function doesn't work as it should, but I found a workaround. I'm still hoping someone can enlighten me as to what I am doing wrong.

    This Progressive Find reads the whole customer table into an array, which is processed into the form needed for display in a Listview control. For this reason it is not appropriate for large customer files, but it works fine with files up to 1000 records or so. (The Progressive Find generated by ActionScript works differently, only reading the records that need to be displayed.) My version automatically selects the first record displayed, so the user doesn't have to select a record if his typing has narrowed it down to one name.

    Thanks to all who helped...

    Code:
    'Date Created: 02-Aug-2013 11:59:38 PM
    'Last Updated: 12-Aug-2013 12:08:06 AM
    'Created By  : Jim
    'Updated By  : Jim
    'Inline-Xbasic. Converted from: Create an Xdialog Progressive Search.
    
    DIM SHARED vLastname as C   ' search string
    DIM SHARED varC_result as C ' "ok" means success
    DIM SHARED vSelID AS C      ' Customer_ID of selected customer
    
    DIM cFuncSrc AS C = <<%code%
    
    ' trace function for debugging
    FUNCTION cTrcPropArr AS C(aArray AS P)
      DIM cResult AS C = ""
      DIM cPropList AS C
      cPropList = STRTRAN(TRIM(PROPERTIES_ENUM(aArray[1])),CRLF(),"|")
      cResult = aArray.DUMP_PROPERTIES(cPropList)
      cTrcPropArr = cResult
    END FUNCTION
    
    ' set selection to top name in list
    FUNCTION vSetSelTop AS V(aContents AS P)
      vSelID = ""
      IF aContents.SIZE()>0
        IF PROP_VALID(aContents[1],"Customer_ID")
          vSelID = aContents[1].Customer_ID
        END IF
      END IF
    END FUNCTION
    
    ' sort aContents array when a column is picked
    FUNCTION cDoSort AS C(BYREF cCurSrtFld AS C,\
                          BYREF cCurSrtDir AS C,NewSrtFld AS C,\
                          pTableDef AS P,aContents AS P)
      DIM cDirFld AS C
      IF NewSrtFld=cCurSrtFld
        cCurSrtDir = IIF(cCurSrtDir="D","AB","D")
      ELSE
        cCurSrtFld = NewSrtFld
        cCurSrtDir = "AB"
      END IF
      ' why is this necessary?  why is cCurSrtDir ignored in the sort?
      cDirFld = IIF(cCurSrtDir="D","INVERT("+cCurSrtFld+")",cCurSrtFld)
      aContents.SORT(cCurSrtDir,cDirFld)
      vSetSelTop(aContents)
      cDoSort = cEvalContents(pTableDef,aContents)
    END FUNCTION
    
    ' get selected records from table and return desired fields as CRLF list
    FUNCTION cGetContents AS C(pTableDef AS P,aContents AS P,pVarFrame AS P)
      DIM cFieldList AS C
      DIM cContentExp AS C
      DIM pTable AS P
      DIM cFilter AS C
      aContents.CLEAR()
      IF .NOT.FILE.EXISTS(TABLE.FILENAME_GET(pTableDef.TableName))
        UI_MSG_BOX("ProgressiveLookup:cGetContents", \
                   "Table missing:"+TABLE.FILENAME_GET(pTableDef.TableName))
        EXIT FUNCTION
      END IF
      pTable = TABLE.OPEN(pTableDef.TableName,FILE_RO_SHARED)
      aContents.RESIZE(pTable.RECORDS_GET())
      cFilter = cGetFltrArgs(pTableDef,pVarFrame)
      cFilter = CONVERT_EXPRESSION(cFilter,"VC","",pVarFrame)
      aContents.INITIALIZE_FROM_TABLE(pTable,cFilter,pTableDef.Order)
      pTable.CLOSE()
      cCurSrtFld = "LASTNAME"
      cCurSrtOrd = "AB"
      vSetSelTop(aContents)
      cGetContents = cEvalContents(pTableDef,aContents)
    END FUNCTION
    
    ' convert from a property array of fields to a CRLF list
    FUNCTION cEvalContents AS C(pTableDef AS P,aContents AS P)
      DIM cEval AS C
      pTableDef.FieldList = ALLTRIM(pTableDef.FieldList)
      IF pTableDef.FieldList=""
        cEvalContents = ""
        EXIT FUNCTION
      END IF
      cEval = *FOR_EACH(x,"TrimCRLFs(x."+x+")",pTableDef.FieldList)
      cEval = ALLTRIM(cEval)
      cEval = STRITRAN(cEval,CRLF(),"+"+QUOTE("|")+"+")
      IF pTableDef.cRetValExp <> ""
        cEval = QUOTE("{DATA=")+"+TrimCRLFs(x."+pTableDef.cRetValExp+ \
                ")+"+QUOTE("}")+"+"+cEval
      END IF
      cEval = "*FOR_EACH(x,"+cEval+",aContents)"
      cEvalContents = EVAL(cEval)
    END FUNCTION
    
    ' replace CRLFs with blanks in a string
    FUNCTION TrimCRLFs AS A(aData AS A)
      IF TYPEOF(aData)="C"
        TrimCRLFs = STRITRAN(aData,CRLF()," ")
      ELSE
        TrimCRLFs = aData
      END IF
    END FUNCTION
    
    ' replace arguments in filter expression with their values
    FUNCTION cGetFltrArgs AS C(pTableDef AS P,pVarFrame AS P)
      DIM cFilter AS C = pTableDef.Filter
      FOR EACH x IN pTableDef.pArgs.arrArgs
        IF (":"+x.Name) $ pTableDef.Filter
          x.Value = A5_ArgGetValue(x,pVarFrame)
          cFilter = STRITRAN(cFilter,":"+x.Name,x.Value)
        END IF
      NEXT
      cGetFltrArgs = cFilter
    END FUNCTION
    %code%
    
    vLastname = ""
    DIM pFuncObj AS P = COMPILE_TEMPLATE(cFuncSrc)
    DIM aContents[1] AS P
    DIM cCurSrtFld AS C = "Lastname"
    DIM cCurSrtDir AS C = "AB"
    DIM pTableDef AS P
    DIM pTableDef.cRetValExp AS C = "Customer_ID"
    DIM pTableDef.FieldList AS C = <<%txt%
    LASTNAME
    FIRSTNAME
    COMPANY
    %txt%
    DIM pTableDef.TitleRow AS C = "{WIDTH=1.5}Last name|{WIDTH=1.3}First name|{WIDTH=1.3}Company"
    DIM pTableDef.Arguments AS C = <<%txt%
    <lastbutton="ok">
    <arrArgs<[1]<Name="argLastname">
    <DataType="Character">
    <Source="Variable">
    <SourceDisplay="Get value from variable">
    <VariableScope="Session Variable">
    <VariableName="vLastname">
    <VariableWithScope="vLastname (Session Variable)">
    <DefaultValue="">
    <Prompt=.F.>
    <PromptText="">
    <PromptControlType="Text Box">
    <PromptChoices="">
    <PromptDlgTitle="Missing Argument">
    <PromptTextAbove="">
    <PreviewValue="">
    >
    >
    %txt%
    DIM pTableDef.pArgs AS P
    property_from_string(pTableDef.pArgs,pTableDef.Arguments)
    DIM pTableDef.TableName AS C = "customer"
    DIM pTableDef.Filter AS C = "LEFT(Lastname,LEN(\":argLastname\"))=\":argLastname\" .AND. (recno() > 0)" ' recno() > 0 is required in order to avoid showing deleted variables
    DIM pTableDef.Order AS C = "LASTNAME+FIRSTNAME"
    
    pLV.titlerow = pTableDef.titlerow
    pLV.titleevents = "Sort_LASTNAME|Sort_FIRSTNAME|Sort_COMPANY"
    pLV.style = "report,singlesel,showselalways,gridlines,fullrowselect"
    pLV.dragbehaviour = ""
    pLV.dropbehaviour = ""
    pLV.contents = pFuncObj.cGetContents(pTableDef,aContents,LOCAL_VARIABLES())
    pLV.events = <<%code%
    
    FUNCTION OnDoubleClick AS C(pLV AS P,pListView AS P,pArgs AS P)
      WITH pLV
        vSelID = pListView.GETROWVALUE(pArgs.GETCLICKROW())
        ' here we need to somehow close the dialog
      END WITH
    END FUNCTION
      
    FUNCTION OnSelectionChanged AS V(pLV AS P,pListView AS P)
      WITH pLV
        vSelID = pListView.Selection
      END WITH
    END FUNCTION
    %code%
    
    DIM cHdgTxt AS C = "Start typing the desired last name in the box below..."
    DIM cDlgNam AS C = "Find Customer"
    varC_result = UI_DLG_BOX(cDlgNam,<<%dlg%
    {watch=vLastname!refresh}
    {on_activate=activate}
    {can_exit=close}
    {auto_external_refresh}
    {region0}
    {region_xmargin=.5}
    {region_ymargin=.5}
    {region1}
    {text=95,1:cHdgTxt};
    {lf};
    {endregion1};
    {region2}
    Type partial last name:| [.40vLastname];
    {lf};
    Select Customer:| {listview=82,20vSelID^=pLV}|;
    {endregion2};
    {line=1,0};
    {region3}
    {sp}
    <*20&Generate Invoice!ok>
    {sp=5}
    <15&Cancel!cancel>
    {endregion3};
    {endregion0};
    %dlg%,<<%code%
    
    IF LEFT(A_DLG_BUTTON,LEN("Sort_"))="Sort_"
      DIM cRetVal AS C
      cRetVal = pFuncObj.cDoSort(cCurSrtFld,cCurSrtDir, \
                                 SUBSTR(A_DLG_BUTTON,LEN("Sort_")+1), \
                                 pTableDef,aContents)
      pLV.Contents = cRetVal
      A_DLG_BUTTON = ""
    END IF
    
    IF A_DLG_BUTTON="activate"
      A_DLG_BUTTON = ""
    END IF
    
    IF A_DLG_BUTTON="refresh"
      pLV.CONTENTS = pFuncObj.cGetContents(pTableDef,aContents,LOCAL_VARIABLES())
      A_DLG_BUTTON = ""
    END IF
    
    IF A_DLG_BUTTON="ok"
    END IF
    
    IF A_DLG_BUTTON="cancel"
    END IF
    
    IF A_DLG_BUTTON="close"
    END IF
    %code%)
    
    END
    -jimc

    Leave a comment:


  • johnkoh
    replied
    Re: Problems with &lt;ARRAY&gt;.SORT() and PROPERTIES_ENUM() functions

    Jim,

    I've spent the past couple of weeks trying to learn Xbasic and Xdialog,
    I have spent a few years, not weeks but still learning and try the samples..


    The progressive code is monster and you may not understand now. search for "progressive with Jim Champman or Charles Crimmel"
    I have just made a simple code and it created by action scripts with some changes



    'created with action scripts and modified the actions
    'Create an XDialog dialog box to prompt for parameters.
    DIM SHARED vType as C
    DIM SHARED vList as C
    DIM SHARED varC_result as C

    ok_button_label = "&Create a Invoice"
    cancel_button_label = "&Cancel"


    DIM vList_rl_def_orig as C
    vList_rl_def_orig = "kl=customers,{keylist_build(\"H=.05,1:35[Name],2:25[City]\",''+Customer_id,left(''+Name,35),left(''+City,25))}{left(Name,len([varC->vType])) = [varC->vType]}"
    DIM vList_rl_def as C
    vList_rl_def = replace_parameters(vList_rl_def_orig,local_variables())

    vshowflag =.f.
    varC_result = ui_dlg_box("Find",<<%dlg%
    {region}
    Type Name here:| [.40vType!vType_changed];
    Show List:| [%@vList_rl_def%.70,5vList!vList_changed]; '<<== This is Record List-List Box, you can select the Check Box but trouble with an array code
    {endregion};
    {line=1,0};
    {region}{justify=right}
    <*25=ok_button_label!OK?vShowflag> <15=cancel_button_label!CANCEL>
    {endregion};
    %dlg%,<<%code%
    If a_dlg_button = "vType_changed" then
    vList_rl_def = replace_parameters(vList_rl_def_orig,local_variables())
    a_dlg_button = ""
    end if

    If a_dlg_button = "vList_changed" then
    vListId = vlist
    vshowflag = .t.
    a_dlg_button =""
    'ui_msg_box("test",""+vlistid)
    end if
    %code%)
    If varC_result = "ok" then
    vCustNm = lookup("customers","customer_id="+quote(vListId),"Name")
    'open a Invoice form
    ui_beep(UI_STANDARD_BEEP)
    ui_msg_box("Test","Create a Invoice form for ---- "+vCustNm)
    'form.view("yourform")
    '

    end if

    Leave a comment:


  • exeye
    replied
    Re: Problems with &lt;ARRAY&gt;.SORT() and PROPERTIES_ENUM() functions

    Thanks for your answer.

    I've spent the past couple of weeks trying to learn Xbasic and Xdialog, and as part of that effort I've read, printed out, and studied all of the threads with "Progressive" in them. Up to now I've managed on my own, working through problems without having to bother the people on the message board.

    These message boards are an imperfect way of conveying information about the level of knowledge of the person making a request. I seem to have done a rather bad job of it.

    I've got a pretty good handle on the mechanics of progressive search, including the two fundamental approaches to it that people have been using. My code also works pretty well, though I realize it is limited to relatively small tables. I'm having trouble with a very specific thing, the sorting that takes place when a column heading is clicked, and my trouble is that I think I understand how to do it and have done it correctly, but it doesn't do what I think it should.

    As I said in my initial post, I've learned a lot recently, but still am subject to making beginner's mistakes. I'm hoping someone can see what I'm doing wrong.

    Thanks again for your reply,

    -jimc

    Leave a comment:


  • johnkoh
    replied
    Re: Problems with &lt;ARRAY&gt;.SORT() and PROPERTIES_ENUM() functions

    Jim,

    Search forum by "Progressive" and you can find a lot of the discussion.

    Leave a comment:


  • Problems with <ARRAY>.SORT() and PROPERTIES_ENUM() functions

    Hi,

    I've gotten a bit more advanced since I last posted... I've been learning Xbasic and Xdialogs and seem to have gotten past the steep part of the learning curve. But remember that I am a beginner and could easily have made a beginner's mistake.

    The attached script is a heavy modification of the ActionScript Progressive Search. For the purpose of posting it here I've changed it to work with ActionSports (which was easy), so you can plop it in there and run it. It's not finished... I've been working on the listing of names and have gotten stuck on the sort that occurs when you select a column heading. I haven't worked much on the buttons, selection, or return value. There's a little bit of trace output (via UI_MSG_BOX) that you can ignore.

    Two things are wrong. First, there is an <array>.SORT() call in the cDoSort function that does nothing. I've traced the input to it and everything looks ok. It just won't sort. Next, I wrote a function to dump the contents of an array of property variables, which is what the sort works with, and which I wrote mostly to help debug the sort problem. I used the ENUM_PROPERTIES() function, which also seems to do nothing.

    Common sense says the two problems are related, and I've either made some dumb error and/or there's something wrong with the property array in question. But the array is used successfully elsewhere, and if I look at it with the debugger it has the right stuff in it. My conclusion is that there's something wrong with Alpha, but I'm experienced enough to know that that's probably not the case. I'm hoping that some experienced eyes can see what I've missed. (It'll also be a nice progressive search (for small tables) when it's done...).

    It's a pretty big script but the code in question is near the top of the file, so you don't have to look very far to see what's not working.

    Here's the code (I've also attached it as a txt file...):

    Code:
    'Date Created: 02-Aug-2013 11:59:38 PM
    'Last Updated: 03-Aug-2013 01:08:06 AM
    'Created By  : Jim
    'Updated By  : Jim
    'Inline-Xbasic. Converted from: Create an Xdialog Progressive Search.
    
    DIM SHARED vLastname as C
    DIM SHARED varC_result as C
    DIM SHARED vListview AS C
    
    DIM cFuncSrc AS C = <<%code%
    
    FUNCTION cDumpPropArray AS C(aArray AS P)
      DIM cResult AS C = ""
      FOR EACH x IN aArray
        cResult = cResult+CRLF()
        cResult = PROPERTIES_ENUM(x,"R;O=p|v|t")
      NEXT
      cDumpPropArray = cResult
    END FUNCTION
    
    FUNCTION cDoSort AS C(BYREF CurSrtFld AS C,\
                          BYREF CurSrtDir AS C,NewSrtFld AS C,\
                          pTableDef AS P,aContents AS P)
      IF NewSrtFld=CurSrtFld
        CurSrtDir = IIF(CurSrtDir="D","AB","D")
      ELSE
        CurSrtFld = NewSrtFld
        CurSrtDir = "AB"
      END IF
      UI_MSG_BOX("cDoSort","CurSrtFld="+CurSrtFld+CRLF()+"CurSrtDir="+CurSrtDir)
      UI_MSG_BOX("cDoSort","aContents 1: "+cDumpPropArray(aContents))
      'aContents.SORT(CurSrtDir,STRITRAN(CurSrtFld,"->","__"))
      aContents.SORT(CurSrtDir,CurSrtFld)
      UI_MSG_BOX("cDoSort","aContents 2: "+cDumpPropArray(aContents))
      cDoSort = cDumpContents(pTableDef,aContents)
      UI_MSG_BOX("cDoSort","RETURN: "+cDoSort)
    END FUNCTION
    
    FUNCTION cGetContents AS C(pTableDef AS P,aContents AS P,pContext AS P)
      DIM cFieldList AS C
      DIM cContentExp AS C
      DIM pTable AS P
      DIM cFilter AS C
      aContents.CLEAR()
      IF .NOT.FILE.EXISTS(TABLE.FILENAME_GET(pTableDef.TableName))
        UI_MSG_BOX("ProgressiveLookup:cGetContents","Table missing:"+ \
                TABLE.FILENAME_GET(pTableDef.TableName))
        EXIT FUNCTION
      END IF
      pTable = TABLE.OPEN(pTableDef.TableName,FILE_RO_SHARED)
      aContents.RESIZE(pTable.RECORDS_GET())
      pTable.CLOSE()
      cFilter = pTableDef.Filter
      cFilter = cGetArgs(pTableDef,pContext)
      cFilter = CONVERT_EXPRESSION(cFilter,"VC","",pContext)
      aContents.INITIALIZE_FROM_TABLE(pTableDef.TableName,cFilter,pTableDef.Order)
      cGetContents = cDumpContents(pTableDef,aContents)
    END FUNCTION
    
    FUNCTION cDumpContents AS C(pTableDef AS P,aContents AS P)
      DIM cEval AS C
      pTableDef.FieldList = ALLTRIM(pTableDef.FieldList)
      IF pTableDef.FieldList=""
        cDumpContents = "No Fields"
        EXIT FUNCTION
      END IF
      cEval = *FOR_EACH(x,"TrimCRLFs(x."+ \
                          STRITRAN(TABLE.NAME_NORMALIZE(x),"->","__")+")", \
                        pTableDef.FieldList)
      cEval = ALLTRIM(cEval)
      cEval = STRITRAN(cEval,CRLF(),"+"+QUOTE("|")+"+")
      IF pTableDef.cRetValExp <> ""
        cEval = QUOTE("{DATA=")+"+TrimCRLFs(x."+ \
                STRITRAN(TABLE.NAME_NORMALIZE(pTableDef.cRetValExp), \
                         "->","__")+")+"+QUOTE("}")+"+"+cEval
      END IF
      cEval = "*FOR_EACH(x,"+cEval+",aContents)"
      cDumpContents = EVAL(cEval)
    END FUNCTION
    
    FUNCTION TrimCRLFs AS A(aData AS A)
      IF TYPEOF(aData)="C"
        TrimCRLFs = STRITRAN(aData,CRLF()," ")
      ELSE
        TrimCRLFs = aData
      END IF
    END FUNCTION
    
    FUNCTION cGetArgs AS C(pTableDef AS P,pContext AS P)
      DIM cFilter AS C = pTableDef.Filter
      FOR EACH x IN pTableDef.pArgs.arrArgs
        IF (":"+x.Name) $ pTableDef.Filter
          x.Value = A5_ArgGetValue(x,pContext)
          cFilter = STRITRAN(cFilter,":"+x.Name,x.Value)
        END IF
      NEXT
      cGetArgs = cFilter
    END FUNCTION
    %code%
    
    vLastname = ""
    DIM pFuncObj AS P = COMPILE_TEMPLATE(cFuncSrc)
    DIM aContents[1] AS P
    DIM CurSrtFld AS C = "Lastname"
    DIM CurSrtDir AS C = "AB"
    DIM pTableDef AS P
    DIM pTableDef.cRetValExp AS C = "Customer_ID"
    DIM pTableDef.FieldList AS C = <<%txt%
    LASTNAME
    FIRSTNAME
    COMPANY
    %txt%
    DIM pTableDef.TitleRow AS C = "{WIDTH=1.5}Last name|{WIDTH=1.3}First name|{WIDTH=1.3}Company"
    DIM pTableDef.Arguments AS C = <<%txt%
    <lastbutton="ok">
    <arrArgs<[1]<Name="argLastname">
    <DataType="Character">
    <Source="Variable">
    <SourceDisplay="Get value from variable">
    <VariableScope="Session Variable">
    <VariableName="vLastname">
    <VariableWithScope="vLastname (Session Variable)">
    <DefaultValue="">
    <Prompt=.F.>
    <PromptText="">
    <PromptControlType="Text Box">
    <PromptChoices="">
    <PromptDlgTitle="Missing Argument">
    <PromptTextAbove="">
    <PreviewValue="">
    >
    >
    %txt%
    DIM pTableDef.pArgs AS P
    property_from_string(pTableDef.pArgs,pTableDef.Arguments)
    DIM pTableDef.TableName AS C = "customer"
    DIM pTableDef.Filter AS C = "LEFT(Lastname,LEN(\":argLastname\"))=\":argLastname\" .AND. (recno() > 0)" ' recno() > 0 is required in order to avoid showing deleted variables
    DIM pTableDef.Order AS C = "LASTNAME+FIRSTNAME"
    
    pLV.titlerow = pTableDef.titlerow
    pLV.titleevents = "Sort_LASTNAME|Sort_FIRSTNAME|Sort_COMPANY"
    'pLV.style = "report,singlesel,showselalways,gridlines,fullrowselect"
    pLV.style = "report,singlesel,showselalways,fullrowselect"
    pLV.dragbehaviour = ""
    pLV.dropbehaviour = ""
    pLV.contents = pFuncObj.cGetContents(pTableDef,aContents,LOCAL_VARIABLES())
    pLV.events = <<%code%
    
    FUNCTION OnRightClick AS C(pLV AS P,pListView AS P,pArgs AS P)
      WITH pLV
        pListView.Selection = pListView.GETROWVALUE(pArgs.GETCLICKROW())
        OnSelectionChanged(pLV,pListView)
        UI_MSG_BOX("OnRightClick",pListView.Selection)
      END WITH
    END FUNCTION
          
    FUNCTION OnDoubleClick AS C(pLV AS P,pListView AS P,pArgs AS P)
      WITH pLV
        vListview = pListView.GETROWVALUE(pArgs.GETCLICKROW())
        UI_MSG_BOX("OnDoubleClick","vListview="+vListview+CRLF()+"pArgs.GETCLICKROW()="+pArgs.GETCLICKROW()+CRLF()+"pListView.Selection="+pListView.Selection)
      END WITH
    END FUNCTION
      
    FUNCTION OnSelectionChanged AS V(pLV AS P,pListView AS P)
      WITH pLV
        vListview = pListView.Selection
      END WITH
    END FUNCTION
        
    FUNCTION OnKeyDown AS V(pVars AS P,pListView AS P,pArgs AS P)
      DIM cKey AS C
      DIM cKeyName AS C
      cKey = pArgs.GETKEYNAME()
      cKeyName = cKey
      IF cKey="{Num Del}"
        cKeyName = "{Delete}"
      ELSE IF pArgs.KEYCODE=13
        cKeyName = "{Enter}"
      ELSE IF pArgs.KEYCODE=8
        cKeyName = "{Backspace}"
      END IF
      UI_MSG_BOX("OnKeyDown",cKeyName)
    END FUNCTION
    %code%
    
    DIM cHdgTxt AS C = "Start typing the desired last name in the box below..."
    DIM cDlgNam AS C = "Find Customer"
    varC_result = UI_DLG_BOX(cDlgNam,<<%dlg%
    {watch=vLastname!refresh}
    {auto_external_refresh}
    {can_exit=close}
    {region0}
    {region_xmargin=.5}
    {region_ymargin=.5}
    {region1}
    {text=95,1:cHdgTxt};
    {lf};
    {endregion1};
    {region2}
    Type partial last name:| [.40vLastname];
    {lf};
    Select Customer:| {listview=82,20vListview^=pLV}|;
    {endregion2};
    {line=1,0};
    {region3}
    {sp}
    <*20&Generate Invoice!ok>
    {sp=5}
    <15&Cancel!cancel>
    {endregion3};
    {endregion0};
    %dlg%,<<%code%
    
    IF LEFT(a_dlg_button,LEN("Sort_"))="Sort_"
      UI_MSG_BOX(a_dlg_button+" 1","vLV.aContents="+pFuncObj.cDumpPropArray(aContents))
      pFuncObj.cDoSort(CurSrtFld,CurSrtDir,SUBSTR(a_dlg_button,LEN("Sort_")+1), \
                       pTableDef,aContents)
      UI_MSG_BOX(a_dlg_button+" 2",pFuncObj.cDumpPropArray(aContents))
      a_dlg_button = ""
    END IF
    
    IF a_Dlg_button="refresh"
      pLV.CONTENTS = pFuncObj.cGetContents(pTableDef,aContents,LOCAL_VARIABLES())
      a_dlg_button = ""
    END IF
    
    IF a_dlg_button="ok"
      UI_MSG_BOX(a_dlg_button,"RETURN: vListview: "+vListview)
    END IF
    
    IF a_dlg_button="cancel"
      'UI_MSG_BOX(a_dlg_button,"")
    END IF
    
    IF a_dlg_button="close"
      'UI_MSG_BOX(a_dlg_button,"")
    END IF
    %code%)
    
    END
    [I hope I formatted that right...]

    I don't expect anyone to go over the whole thing, but I'm open to any suggestions for improving the code.

    Thanks for looking!

    -jimc
    Attached Files
Working...
X