#GPUGSummit | #INreno15 #GPUGSummit 25 DEVELOPMENT TRICKS AND HACKS IN 50 MINUTES David Musgrave MVP...
-
Upload
priscilla-george -
Category
Documents
-
view
217 -
download
1
Transcript of #GPUGSummit | #INreno15 #GPUGSummit 25 DEVELOPMENT TRICKS AND HACKS IN 50 MINUTES David Musgrave MVP...
#GPUGSummit | #INreno15
#GPUGSummit
25 DEVELOPMENT TRICKS AND HACKS IN 50 MINUTES
David Musgrave MVPManaging Director,
Winthrop Development Consultants
Mariano Gomez MVPSenior Software Engineer,
Mekorma
#GPUGSummit | #INreno15 2
Introductions
What is this madness?
25 Tricks and Hacks– Dexterity– Visual Studio Tools
Q & A
AGENDA
#GPUGSummit | #INreno15 3
Managing Director of Winthrop Development Consultants
Microsoft Dynamics GP Most Valuable Professional (MVP)
Worked with Microsoft for 13 and a half years
Lives in Winthrop, a suburb in the city of Perth
Where is Perth, Western Australia?
DAVID MUSGRAVE
#GPUGSummit | #INreno15
Fun facts:Perth is the most isolated capital city in the world
Perth has the largest inner city park in the world, Kings Park, yes, bigger than even New York’s Central Park. It was also the first park to be designated for public use in Australia in 1872 and host to Australia's largest wild flower show and exhibition.Sharks
Dolphins
Sharks
Sharks with Lasers for EyesStinging
Jellyfish
Razor Sharp Coral
Cyclones
Stingrays
Sharks
Fires
Poisonous Snakes
Crocodiles
Giant Rats
Dingoes
Scorpions
Scorching Desert
Giant Spiders
Mosquitoes
Man-eating Koalas
Drop Bears
NOTHINGFloods
Tasmanian Devils
#GPUGSummit | #INreno15
© Kirk Hille – All rights reserved. Photograph used with express permission from Kirk Hille.
#GPUGSummit | #INreno15 6
Senior Software Engineer at Mekorma
Microsoft Dynamics GP Most Valuable Professional (MVP)
Lives in Atlanta, Georgia, USA
Born on a small Colombian island off the coast of Nicaragua
Where is San Andres Island?
MARIANO GOMEZ
#GPUGSummit | #INreno15
Fun facts:At only 39 square km, the island of San Andres is one of the smallest islands in the Caribbean Sea
It belongs to Colombia, but was a former British territory up to the early 1800s
#GPUGSummit | #INreno15
© Thierry Desgans – All rights reserved. Photograph used with express permission from Thierry Desgans.
#GPUGSummit | #INreno15 9
Introductions
What is this madness?
25 Tricks and Hacks– Dexterity– Visual Studio Tools
Q & A
AGENDA
#GPUGSummit | #INreno15 10
Who has not worked with any of the tools? Who has worked with Modifier with VBA? Who has worked with Visual Studio Tools? Who has worked with Dexterity? Who has worked with Dexterity SBA or .Net Interop? Who has worked with more than one tool? Who has worked with more than one tool on a single project?
UNDERSTANDING THE AUDIENCE
#GPUGSummit | #INreno15 11
After the success of Mark Polino’s 50 tips in 50 minutes session at Convergence we thought we would try something similar
We wanted to provide an overview of some tricks that will make your development life easier
Also, we wanted to show some new techniques that you probably have not heard of before.
WHAT IS THIS MADNESS?
#GPUGSummit | #INreno15
LET’S GET STARTED
HOLD ON TIGHT
ASK QUESTIONS AT THE END
#GPUGSummit | #INreno15
DEXTERITY BEST PRACTICES
#GPUGSummit | #INreno15 14
Use a Binary sort order for your development and testing SQL Server environments
– This will ensure that any code executed directly on SQL Server, such as Pass through SQL, Range where clauses and stored procedures will have the correct case for database, table and column names.
– Avoids issues where supposedly working code starts failing at some customers when it works at other due to a Binary sort order.
– While Binary is not as commonly used now, there are many legacy sites still using it.
– Using Binary during development means that all code will work correctly for both Binary and DOCI.
1 OF 25: DEVELOP ON A BINARY SQL DATABASE
#GPUGSummit | #INreno15 15
Use a separate Dex.ini file for each project– Create a shortcut in the project folder to launch Dex.exe and pass the Dictionary file and Dex.ini file.
Source Code Control provides version control– Tools such as Visual Source Safe and Team Foundation Server provide version control and can assist
with multiple developers working on a single project. The generic text provide can still restore back to last version.
Source Code Control must be used for upgrades– As resource IDs can change between versions, you can no longer use Dexterity Utilities Transfer >>
Developer Update to upgrade between versions as this links using resource IDs when Source Code Control links using resource names. Using Developer Update can result in incorrect fields, datatypes, strings, etc. being used on a form.
– Developer Update is still great for combining an extracted source dictionary back into a clean Dynamics.dic dictionary of the same version to create a development dictionary.
2 OF 25: USE SOURCE CODE CONTROL (SCC)
#GPUGSummit | #INreno15 16
Use of an Index File with Source Code Control is not optional– As Source Code Control uses Resource Names as the primary key and does not store Resource IDs,
we must use an Index File to keep track of the Resource IDs assigned to resources.
– Always use the Update… option from the Explorer >> Source Control menu and check use Index File. Never use the Update button on the Resource Explorer window as this does not use the Index File.
– Always Update Index File when creating a new build for release to ensure any newly created resources are tracked.
What happens if you don’t use an Index File– Chunk Files can get corrupted when extracting over the previous extract dictionary.– Security records, shortcuts & command navigation become corrupted as resource IDs have
changed.
3 OF 25: USE AN INDEX FILE WITH SCC
#GPUGSummit | #INreno15 17
What is a _DUP Script?– A Dexterity event script on a form or window has two parts: The source code (with executable p-
code) and a link to a focus event, such as Form Pre, Window Post or Field Change.
– If you compile the code, but then fail to save the window or form, the script will exist in the dictionary, but the link will not have been saved.
– When you attempt the add the script again, the script name has already been used and so a _DUP suffix will be added.
Avoid _DUP Scripts with these simple steps– Immediately after creating a form or a window, close and save the window and click OK on the form.– Avoid discarding window changes or clicking cancel on forms after changes have been made to
scripts.
4 OF 25: SAVE FORMS & WINDOWS TO AVOID _DUP
#GPUGSummit | #INreno15 18
Create Worksets to group logical sections of your project together– Worksets are a collection of shortcuts to resources of any type in the
dictionary. Worksets can make finding resources much simpler. Using Worksets helps with memory
– Having a workset for a project will help when revisiting old projects or new developers to a project.
Create new resources while showing a workset– When new resources are created using the down arrow on the new button,
they are automatically added to the current workset. Backup the ctree files used to store the Worksets
– Make sure you backup the *ResExWsH.dat, *ResExWsH.idx, *ResExWsM.dat, *ResExWsM.idx files.
5 OF 25: USE DEXTERITY WORKSETS
#GPUGSummit | #INreno15 19
This is a little thing that can be very annoying– Please always add a blank line at the end of your scripts.
Why?– Because it allows the mouse to select the last line of the script when highlight with a
mouse.
– It allows for a new line to be added easily to the end of the script.
– It makes sure the syntax highlighting works as you are typing the script.
– It’s like putting the toilet seat down at home, it makes it much nicer for the next person.
6 OF 25: FINISH A SCRIPT WITH A BLANK LINE
#GPUGSummit | #INreno15 20
It is best to always create scripts so they compile with no errors or warnings.– This makes it easy to see when an issue has been introduced.
Handle Datatype mismatches– Either use the correct datatype to avoid overflow errors or use typing functions such as integer(),
long().– Avoid overflowing string datatypes, make sure they are long enough or use substring() to truncate.
As a last resort, use Pragma precompiler commands– For developer only warnings, pass through Dexterity sanScript or SQL code, use:– pragma(disable warning LiteralStringUsed); pragma(enable warning LiteralStringUsed); – For unused parameters on function and procedure trigger handling scripts, use:– pragma(disable warning UnusedVariable); – For placeholder scripts used for triggering against, use:– pragma(disable warning NoExecutableCode);
7 OF 25: CREATE CODE WITH 0 ERRORS & 0 WARNINGS
#GPUGSummit | #INreno15 21
When creating your trigger registrations make sure that every trigger failure error message is unique.– Include identification that the trigger is from your product:
<product name>:
– A suggestion is to use the fully qualified name of the resource:<field> of window <window> of form <form>
– Include the trigger type and attach type:field focus before change
Why?– It will make it simple for anyone to provide accurate information on exactly which trigger is causing
issues, probably due to a parameter change on a script, or a change on a 3 rd party form.
8 OF 25: UNIQUE ERRORS WHEN REGISTERING TRIGGERS
#GPUGSummit | #INreno15 22
Make the bare minimum changes needed to move or add fields.– Never delete an original field as this is likely to break existing code.– Add whatever additional fields you need, use align tools to help get user interface
layout correct.– Don’t forget to link prompts, link lookups and set the tab sequence.– No need to associate additional tables to the form.– Use Triggers to add scripts to form and include a check to see if alternate is in use to
avoid illegal address errors. Why?
– This approach is much easier to maintain, as you can identify where the scripts are without needing to check fields, run a script report, or find semicolons on the form.
– If you need to recreate the alternate window between versions, it is simple and you will get compile errors if you missed adding a field.
9 OF 25: DON’T ADD SCRIPTS TO ALTERNATE WINDOWS
#GPUGSummit | #INreno15
CHECKING IF ALTERNATE FORM IS IN USE - PART 1Global Procedure: Startup (excerpt)pragma(disable warning LiteralStringUsed);if Trigger_RegisterFocus(anonymous(form 'IV_Item_Maintenance'), TRIGGER_FOCUS_PRE, TRIGGER_BEFORE_ORIGINAL, script WDC_IV_Item_Maintenance_FORM_PRE) <> SY_NOERR then
warning “WDC: IV_Item_Maintenance form focus before pre trigger registration failed.";end if;pragma(enable warning LiteralStringUsed);
Global Trigger Procedure: WDC_IV_Item_Maintenance_FORM_PRE'WDC Alternate IV_Item_Maintenance' of globals = WDC_Check_Security_Alternate(Runtime_GetCurrentProductID(), FORMTYPE, technicalname(form IV_Item_Maintenance));
#GPUGSummit | #INreno15
CHECKING IF ALTERNATE FORM IS IN USE - PART 2Global Function: WDC_Check_Security_Alternate()function returns boolean OUT_Alternate;in integer l_AltDictID;in 'Restype' l_restype;in 'Resname' l_resname;local 'Resid' l_resid;local integer dictid;local string l_Alias;
OUT_Alternate = false;if l_AltDictID = DYNAMICS then
{ In Test Mode }OUT_Alternate = true;
elseif IMIntegrationMode of globals then
{ Integration Manager is running }OUT_Alternate = false;
else{ Runtime Mode }l_resid = Resource_GetID(l_AltDictID,l_restype,l_resname);dictid = DYNAMICS;
#GPUGSummit | #INreno15
CHECKING IF ALTERNATE FORM IS IN USE - PART 3Global Function: WDC_Check_Security_Alternate() cont.
case Security(dictid, l_restype, l_resid, l_Alias)in [REJECT_RECORD] { Access Denied }
OUT_Alternate = false;in [REJECT_SCRIPT] { Access to alternate and/or modified failed }
OUT_Alternate = false;else
if dictid <> DYNAMICS thenif dictid = l_AltDictID then
if l_Alias = str(l_resid) then{ Access to My Modified
Alternate }OUT_Alternate = true;
else{ Access to My Alternate }OUT_Alternate = true;
end if;end if;
end if;end case;
end if;end if;
#GPUGSummit | #INreno15 26
Using Runtime_GetCurrentProductID() creates code that works in both test mode and runtime mode.– In Dexterity Test mode, the function will return 0 as you are developing in a copy of the
Dynamics.dic dictionary.– In Runtime mode it will return the product ID of your product. – This is especially helpful when working with commands, command forms and menu
navigation.
When referencing your resources added by your product– Use Runtime_GetCurrentProductID().
When referencing existing resources in Dynamics.dic– Use the constant DYNAMICS, which has a value of 0.
10 OF 25: USE RUNTIME_GETCURRENTPRODUCTID()
#GPUGSummit | #INreno15 27
Well behaved ranges are inclusive ranges– Inclusive range: specify start record and end record and includes all records in between based on index
– Inclusive ranges behave the same on all database platforms, so will work with Ctree and Memory tables
Rules for a Well Behaved Range– In order of the segments/fields defined in the key/index:
1. Start and End segment must have 0 or more fields with the same value.
2. Start and End segment must have 0 or 1 field with different values (with Start less than End value)
3. Start and End segments must have the remaining values cleared/filled.
11 OF 25: USE WELL BEHAVED RANGES
#GPUGSummit | #INreno15 28
If you have a segment marked as descending in a key– All table actions involving that segment need to be treated in reverse.
Range Setting– If using different values, then Start value must be greater than End value
– If Clearing and Filling then Start value must be filled and End value cleared
Moving through the table– If wanting to move in ascending order, need to use get last table and get
prev table inside loop
12 OF 25: WORKING WITH DESCENDING KEY FIELDS
#GPUGSummit | #INreno15 29
Range Setting Requirements– Key fields for index in table buffer need to be set to start values when range start command issued.– Key fields for index in table buffer need to be set to end values when range end command issued.
Traditional Method of Range Setting– Clear table, set start values, range start table, set end value, fill remaining end values, range end table
Future Proof Range Setting– Clear table, set start values, range start table, fill table, set end values, range end table
Why?– Only need to add code for fields getting set to specific values, the rest are automatically cleared/filled– If additional key segments are added to the index, no need to revisit code to fix range
13 OF 25: USE FUTURE PROOF RANGE SETTING
#GPUGSummit | #INreno15
FUTURE PROOF RANGE SETTINGrange clear table <Table Name>;
clear table <Table Name>;‘<Field1>' of table <Table Name> = SingleValue;‘<Field2>' of table <Table Name> = StartValue;range start table <Table Name> by number <Sort>;
fill table <Table Name>;‘<Field1>' of table <Table Name> = SingleValue;‘<Field2>' of table <Table Name> = EndValue;range end table <Table Name> by number <Sort>;
#GPUGSummit | #INreno15
DEXTERITY ADVANCED TECHNIQUES
#GPUGSummit | #INreno15 32
Record a macro file while creating the chunk file– Start recording by closing Source, Editable and Destination Dictionaries– Record all steps including exiting Dexterity Utilities when finished– Edit the Macro file with Notepad.exe:
Change Second line: Logging file nulEnsure Last line: MenuSelect title File entry Exit
Launching the macro file– Create a shortcut to DexUtils.exe and pass the macro as a parameter; or– Create a batch file which runs DexUtils.exe and passes the macro as a parameter
Why?– Using a macro ensures that all builds are created with same settings and steps, avoids human error– You can manually edit the macro to include additional alternate forms and reports, if needed– You can manually edit build numbers when creating a new build– You can find and replace folder paths & update version numbers when starting a new version
14 OF 25: AUTOMATE CHUNK FILE CREATION
#GPUGSummit | #INreno15 33
Use a Batch File to automate chunk file deployment– Copy chunk file from development folder to application folder– Launch Dynamics application in unchunk only mode using /CNKONLY
Extend Batch File to create Dictionary Assemblies– Delete existing Dictionary Assembly *.xml and *.dll files– Run DAG.EXE (Dictionary Assembly Generator Tool)– Copy resulting Dictionary Assembly *.xml and *.dll files back to the development folder
Signing and Code Signing (Digital Signature)– DAG.EXE can sign the dll files generate using the /S option and passing in a *.snk key file– Signing should be used to give the dll files a unique and constant GUID. Required for web client– SIGNTOOL.EXE can be used to Code Signing the dll files to add a Digital Signature– If shipping a product, Code Sign the dll files so they are trusted & don’t need to be unblocked
15 OF 25: AUTOMATE CHUNK FILE DEPLOYMENT
#GPUGSummit | #INreno15
BATCH FILE TO AUTOMATE CHUNK FILE DEPLOYMENTCD C:\DEX1400\Projectcopy Project.cnk C:\DYN1400\*.*CD C:\DYN1400Dynamics.exe Dynamics.set /CNKONLY
if exist Application.ProjectName.dll del Application.ProjectName.dllif exist Application.ProjectName.xml del Application.ProjectName.xmlif exist Application.ProjectName.Metadata.dll del Application.ProjectName.Metadata.dllif exist Application.ProjectName.Metadata.xml del Application.ProjectName.Metadata.xml
C:\DEX1400\DAG.EXE ### /M /N:ProjectNamerem C:\DEX1400\DAG.EXE ### /M /N:ProjectName /S:C:\DEX1400\Project\Project.snkrem signtool sign /f "<Path to .pfx file>" /p <password> /t <web path to timestamp server>
Application.ProjectName.dll Application.ProjectName.Metadata.dll
copy /y Application.ProjectName.dll C:\DEX1400\Project\Application.ProjectName.dllcopy /y Application.ProjectName.xml C:\DEX1400\Project\Application.ProjectName.xmlcopy /y Application.ProjectName.Metadata.dll C:\DEX1400\Project\Application.ProjectName.Metadata.dllcopy /y Application.ProjectName.Metadata.xml C:\DEX1400\Project\Application.ProjectName.Metadata.xml
#GPUGSummit | #INreno15 35
A quick check for triggers against Dynamics.dic– Just going into Test Mode by pressing Ctrl-T from the Dexterity Development environment will
run the Startup script and its child scripts and validate that the triggers register without errors
– Pressing Ctrl-T once the login window is displayed returns you to the Development environment
Notes– Does not work for triggers against 3rd party products as Test mode does not have 3rd party
dictionaries loaded
– Does not work for triggers registered after login
– Handy quick check after upgrading to a new version of Dynamics to see if parameters have changed
16 OF 25: TRIGGER REGISTRATION ERRORS QUICK TEST
#GPUGSummit | #INreno15 36
What is the Command Form?– The Command Form is a hidden form used as a container for a products navigation commands– It is opened when the application starts and remains open until the application is closed– To make the form hidden, the form contains a window with AutoOpen=False and Title=~internal~
Store data on the hidden window on Command form– Once the form is open, the windows exist even if they are not displayed to the user– Additional fields can be added to the hidden window and used to store data instead of global variables– Additional hidden windows can also be created if you want to “organize” the fields
Why?– There is a limit to the memory that can be used as global variables, this method avoids hitting the limit– Faster than using memory tables or temporary tables to temporarily store data
17 OF 25: EXTRA USE FOR THE COMMAND FORM
#GPUGSummit | #INreno15 37
It is not always possible to stop Dexterity code– Reject script command only works for Focus event triggers running before the original– Sometimes it is just not possible to stop Dexterity code from running
Using a pre and post trigger you can change behavior– The pre trigger can store a value into a global variable or hidden field on command form and
replace the value with a custom value– The original code now runs with the custom value– The post trigger then restores the original value back from the global variable or hidden field
Why?– Depending the original code, this method can be used to either prevent original code from
working or make it work for you using your value rather than the original value– Re-uses existing code, can be much better than replacing the script and rejecting script– Allows other third party code triggering on the same location to still work
18 OF 25: THE TWO TRIGGER TECHNIQUE
#GPUGSummit | #INreno15
TWO TRIGGER TECHNIQUETrigger Procedure: WDC_Zoom_Button_CHG_PRE{ Back up Original Item Number }'WDC Item Number' of globals = 'Item Number';{ Replace Item Number with new value for Zoom }'Item Number' = 'New Item Number';
Trigger Procedure: WDC_Zoom_Button_CHG_POST{ Restore Original Item Number}'Item Number' = 'WDC Item Number' of globals;
#GPUGSummit | #INreno15 39
Two common uses for the technique– Limiting a trigger on a commonly called script so that the trigger is only executed when you want it– Capturing data or table references in one location to be used in a second location based on timing
Limiting execution of a Trigger to one location– Procedure A calls Function B, but Function B is used in many only locations. Want Trigger on Function B– Before Trigger on Procedure A sets global variable, After Trigger on Procedure A clears global variable– Trigger on Function B checks status of global variable and aborts if not set to true
Capturing Data or Table Reference– Procedure A calls Procedure B which passes temporary table buffer, then it populates the table, then calls
Procedure C, then processes table contents. Want to modify table contents before it is processed.– Before Trigger on Procedure B to capture table buffer reference can store it as a global variable– Trigger on Procedure C then uses table('Table Reference' of globals) to update table contents– After Trigger on Procedure A clears table reference global variable as it is no longer valid
19 OF 25: THE THREE TRIGGER TECHNIQUE
#GPUGSummit | #INreno15
THREE TRIGGER TECHNIQUE – TYPE 1Trigger Procedure: WDC_Procedure_A_PRE'WDC Function B Flag' of globals = true;
Trigger Function: WDC_Function_B_POSTif not 'WDC Function B Flag' of globals then
abort script;end if;{ Rest of code follows here }
Trigger Procedure: WDC_Procedure_A_POST'WDC Function B Flag' of globals = false;
#GPUGSummit | #INreno15
THREE TRIGGER TECHNIQUE – TYPE 2Trigger Procedure: WDC_Procedure_B_PREinout table TempTable;assign 'WDC Table Reference' of globals as reference to
table TempTable;
Trigger Function: WDC_Function_C_POSTclear table table('WDC Table Reference' of globals);{ Rest of code follows here }
Trigger Procedure: WDC_Procedure_A_POSTclear 'WDC Table Reference' of globals;
#GPUGSummit | #INreno15 42
Changing Context of Triggers using default command– Normally a trigger procedure runs in the same context as a global procedure.– Use default form to command to change context to form level (same as FORM_PRE or FORM_POST)– Use default window to command to change context to specific window on a form
Why?– This can make coding of scripts much simpler as there is no need to fully qualify the window and form– By changing context, you can access the form’s table buffers, including scrolling window or temp
tables
Notes– As scripts are now running in the context of the form, you cannot reference tables that are not
associated with the form.– To reference other tables, use global functions to perform any actions on other tables as needed.
20 OF 25: USING DEFAULT FORM / WINDOW TO
#GPUGSummit | #INreno15
DEFAULT FORM / WINDOW EXAMPLETrigger Procedure: WDC_IV_Item_Maintenance_WIN_PRE
if not 'WDC Alternate IV_Item_Maintenance' of globals thenabort script;
end if;
default form to IV_Item_Maintenance;default window to IV_Item_Maintenance;
hide 'Item Number';show 'New Item Number';
#GPUGSummit | #INreno15 44
Using Dexterity precompiler directives– Dexterity supports conditional compilation using #if, #elseif, #else and #end precompiler directives– Work with Constants defined in the dictionary, such as WDC_PROD_MAJ or DEBUG
Why?– You can use conditional compilation to maintain a single code base for a product for all current
versions of Microsoft Dynamics GP while still supporting new features. Conditional Compilation prevents compilation errors for missing resources, scripts or new sanScript commands and functions
– You can use conditional compilation based on a DEBUG constant to create “Debug” builds with additional messages which can be disabled by changing the constant to false before final builds
Notes– The Hash (#) must be the first character on the line to be identified as a pre-compiler directive– The lines are not terminated with a semicolon (;)
21 OF 25: USE CONDITIONAL COMPILATION
#GPUGSummit | #INreno15
CONDITIONAL COMPILATION EXAMPLE#if WDC_PROD_MAJ >= 12 then
{ Code for v12 or later }
#elseif WDC_PROD_MAJ = 11 then{ Code for v11 only }
#else{ Code for v10 or earlier }
#end if
#GPUGSummit | #INreno15 46
Passthrough SQL commands must be formed correctly– This includes commands executed by SQL_Execute(), where clauses used with range table where as
well as parameters passed to stored procedures
Strings and Maximum Character (missing Z issue)– Use SQL_FormatStrings(<Str>) to add single quotes and double up any single quotes to avoid injection– Use system 9600 command to obtain maximum character for SQL sort order. Fill gives char(255) = ÿ
Date and Time Issues– To avoid issues with date and time formats (both regional and user format) don’t use str() function– Use SQL_FormatStrings(sqlDate(<Date>)) for Dates, this uses format 'YYYYMMDD'– Use SQL_FormatStrings(sqlTime(<Time>)) for Times, this uses 24hr format 'HH:MM:SS'
More Information– http://blogs.msdn.com/b/developingfordynamicsgp/archive/2013/11/04/quick-tip-unusual-behaviou
r-when-working-with-sql-server-from-dexterity.aspx
22 OF 25: AVOID ISSUES WITH PASSTHROUGH SQL
#GPUGSummit | #INreno15
DEXTERITY WORKAROUND TECHNIQUE
#GPUGSummit | #INreno15 48
Version 12.0 of Dexterity changed form export format– This affects how pictures are associated with local push button fields in forms– Backward compatible: Versions 12.0 or later can import previous format without issues– However, version 11.0 or earlier will lose images from local pushbutton fields if importing new format
If working with a shared code base across versions– Suggest to always develop new code in v11.0 or earlier and export and import into v12.0 or later
How to fix exported form file to allow import– If you need to take an exported form back from v12.0 to v11.0, you can fix it with Notepad.exe– Find StaticType "Picture" Replace with StaticType "Mixed" but only for Control "PushButton"
Notes– Navigation commands defined on forms have additional options for v12.0 which can be lost if
importing from v11.0, it is best to just remake changes for each version for command forms– Global procedures on v14.0 can have SBA Metadata which will be lost if importing from v11.0
23 OF 25: ISSUE WHEN IMPORTING EXPORTED FORMS
#GPUGSummit | #INreno15
V11.0 OR EARLIER LOCAL FIELD FORMATDatatype "Export Button"
{Control "PushButton"DefaultDown "00000"DefaultMouseOver "00000"DefaultUp"00001"Prompt "E&xport"PromptDown ""PromptMouseOver ""StaticType "Mixed"~ItemImages{
PictItem "00001"{
Item "WindowToolbar_Transfer_PB_Up"}
}}
#GPUGSummit | #INreno15
V12.0 OR LATER LOCAL FIELD FORMATDatatype "Export Button"
{Control "PushButton"DefaultDown "00000"DefaultMouseOver "00000"DefaultUp"00001"Prompt "E&xport"PromptDown ""PromptMouseOver ""StaticType "Picture"~ItemImages{
PictItem "00001"{
Item "WindowToolbar_Transfer_PB_Up"}
}}
#GPUGSummit | #INreno15
DEXTERITY VERY ADVANCED TECHNIQUE
#GPUGSummit | #INreno15 52
What is the old() issue?– It is very common when writing custom code to need to change a field’s value– Usual method is to set field to the new value and use run script to execute the change script
– old() does not work when using set field, run script field, it returns the same as the current value– Hence no change is detected and diff() returns 0. This means that the code fails to work correctly– For example: Item allocations are not shifted between sites when changing the transaction location
The problem is focus– For old() to work, the field must have focus with the old value before the value is changed– Using the focus command does not actually take effect until all foreground scripts have completed
– Dexterity Help says: This function should not be used in a change script run using the run script or run script delayed statements. Focus must be in the field for which this function is run for it to operate properly.
24 OF 25: SOLVING THE OLD() ISSUE – METHOD 2 PT.1
#GPUGSummit | #INreno15 53
Solving the old() issue, this is better second method– Leverages functionality of open form return to <field> command and return command– When field is “returned”, the focus is placed on the field, the field is changed, then focus moves to
next field in the tab sequence which causes the change script to run
How it works– Create WDC_Return form with hidden Dummy window. On the hidden window have a local field of
each data type and a Return Pushbutton. Scripts then open form and return to the desired field
Notes– Cannot use ,nofocus option as that stops old() from working– Must use push button with run script to issue the return command– Cannot be used on last field in scrolling window as the tab off the field forces the scrolling window
to a new line, if necessary adjust tab sequence on modified window to workaround.
24 OF 25: SOLVING THE OLD() ISSUE – METHOD 2 PT.2
#GPUGSummit | #INreno15
SOLVING THE OLD() ISSUE EXAMPLETraditional Method'TRX Location' = '(L) Default Site';run script 'TRX Location';
New Method for which old() workscall WDC_Return, 'TRX Location', '(L) Default Site';
#GPUGSummit | #INreno15
SOLVING THE OLD() ISSUE CODE – PART 1Global Procedure: WDC_Returninout anonymous field INOUT_Field;in anonymous IN_Value;
if INOUT_Field <> IN_Value thencall Return of form WDC_Return, INOUT_Field, IN_Value;
end if;
#GPUGSummit | #INreno15
SOLVING THE OLD() ISSUE CODE – PART 2Form Procedure: Return of form WDC_Returninout anonymous field INOUT_Field;in anonymous IN_Value;
open form WDC_Return return to INOUT_Field;if isopen(form WDC_Return) then
case datatype(IN_Value)in [DATATYPE_BOOLEAN, DATATYPE_CHECK_BOX]
'(L) Mode' of window Dummy = DATATYPE_BOOLEAN;'(L) Boolean' of window Dummy = IN_Value;
in [DATATYPE_INTEGER, DATATYPE_PROGRESS_INDICATOR, DATATYPE_RADIO_GROUP, DATATYPE_TINY_INTEGER, DATATYPE_VISUAL_SWITCH]
'(L) Mode' of window Dummy = DATATYPE_INTEGER;'(L) Integer' of window Dummy = IN_Value;
in [DATATYPE_BUTTON_DROP_LIST, DATATYPE_DROP_DOWN_LIST, DATATYPE_HORIZONTAL_LIST_BOX, DATATYPE_LIST_BOX, DATATYPE_NON_NATIVE_LIST_BOX]
'(L) Mode' of window Dummy = DATATYPE_INTEGER;'(L) Integer' of window Dummy = IN_Value;
in [DATATYPE_LONG_INTEGER, DATATYPE_MULTI_SELECT_LIST_BOX]'(L) Mode' of window Dummy = DATATYPE_LONG_INTEGER;'(L) Long' of window Dummy = IN_Value;
#GPUGSummit | #INreno15
SOLVING THE OLD() ISSUE CODE – PART 3in [DATATYPE_CURRENCY]
'(L) Mode' of window Dummy = DATATYPE_CURRENCY;'(L) Currency' of window Dummy = IN_Value;
in [DATATYPE_VCURRENCY, DATATYPE_REAL]'(L) Mode' of window Dummy = DATATYPE_VCURRENCY;'(L) VCurrency' of window Dummy = IN_Value;
in [DATATYPE_DATE]'(L) Mode' of window Dummy = DATATYPE_DATE;'(L) Date' of window Dummy = IN_Value;
in [DATATYPE_STRING]'(L) Mode' of window Dummy = DATATYPE_STRING;'(L) String' of window Dummy = IN_Value;
in [DATATYPE_TIME]'(L) Mode' of window Dummy = DATATYPE_TIME;'(L) Time' of window Dummy = IN_Value;
elseabort script;
end case;run script '(L) Return' of window Dummy;
end if;
#GPUGSummit | #INreno15
SOLVING THE OLD() ISSUE CODE – PART 4Field Change Script: Dummy l_Return_CHGcase '(L) Mode'
in [DATATYPE_BOOLEAN]return '(L) Boolean';
in [DATATYPE_INTEGER]return '(L) Integer';
in [DATATYPE_LONG_INTEGER]return '(L) Long';
in [DATATYPE_CURRENCY]return '(L) Currency';
in [DATATYPE_VCURRENCY]return '(L) VCurrency';
in [DATATYPE_DATE]return '(L) Date';
in [DATATYPE_STRING]return '(L) String';
in [DATATYPE_TIME]return '(L) Time';
elseend case;close form WDC_Return;
#GPUGSummit | #INreno15
VISUAL STUDIO TOOLS
#GPUGSummit | #INreno15 60
Visual Studio Tools addons without WinForms– A Visual Studio Tools addon that does not have any WinForms, but only uses original or
modified Dexterity forms should be able to work on the Web Client without much additional work
DLL files must be signed– Both Dictionary Assembly and Addon dll files must be signed to work on the web client
Supported Platform must be enabled– The SupportedDexPlatforms property must be set for both DesktopClient (default) and
WebClient– Commands are similar but slightly different for C# and Visual Basic.Net, added above
class GPAddIn
25 OF 25: USING ADDONS WITH THE WEB CLIENT
#GPUGSummit | #INreno15
ENABLING ADDON FOR WEB CLIENTC#[SupportedDexPlatforms(DexPlatforms.DesktopClient | DexPlatforms.WebClient)]public class GPAddIn : IDexterityAddIn VB<SupportedDexPlatforms(DexPlatforms.DesktopClient Or DexPlatforms.WebClient)>Public Class GPAddIn
#GPUGSummit | #INreno15
SERVICE BASED ARCHITECTURE
#GPUGSummit | #INreno15 63
Form ServiceCodeGenerator– Hidden form in the Dynamics.dic dictionary
Create .Net Objects– Create a .Net Object script template from table definitions
Map Fields from Object to window fields– Creates the scripts to be used for a window wrapped service
Set Object from Table– Creates the scripts to be populate the object variable from a table record
26 OF 25: BONUS TIP: SERVICE CODE GENERATOR
#GPUGSummit | #INreno15 64
#GPUGSummit | #INreno15
ALMOST DONE
#GPUGSummit | #INreno15 66
If you liked this session and want more– Make sure you say so in your conference evaluation and feedback
We have more tricks and hacks– While preparing for this session, we created over 60 tricks and hacks– It was difficult to select the first 25 to include in this session
Do you have any cool tricks and hacks?– If you have something cool you have worked out, please let us know
and we might include it in future sessions and give you credit
27 OF 25: WANT MORE, JUST ASK
#GPUGSummit | #INreno15
WE MADE IT
LINKSQ & A
#GPUGSummit | #INreno15 68
David Musgrave’s Winthrop Development Consultants Bloghttp://www.winthropdc.com/blog
The Dynamics GP Blogster blog (by Mariano Gomez)http://dynamicsgpblogster.blogspot.com/
Developing for Microsoft Dynamics GP Blog – retired(by David Musgrave & the Developer Support Team)
http://blogs.msdn.com/DevelopingForDynamicsGP/ or http://aka.ms/Dev4DynGP
LINKS
#GPUGSummit | #INreno15
Click icon to add picture
CONTACT PRESENTERS
David [email protected]@winthropdc
Mariano [email protected] @dgpblogster