-
April 13, 2010 at 18:14
(no comments)
A somewhat non-intuitive way of accessing controls on a form is using a built in enum named ‘control’. This enum automatically contains all controls of you form (tabs, buttons, etc), and you can reference them by name.
So, if for example your button is called ‘ButtonFunctions’, you can set the property ‘enabled’ to false like this:
element.control(Control::ButtonFunctions).enabled(false);
The main advantage here is that you don’t have to set the property autodeclaration to yes.
-
April 12, 2010 at 18:12
(no comments)
Sometimes, you can’t synchronize a table because is contains duplicate records. This can occur when you change field lengths on a extended data type for example.
I’ve seen people write huge SQL statements, and even jobs that create these SQL statements to remove these duplicates from a table, but there’s actually a easy way to do this in AX.
Say you have a table KLFTestDuplicates that contains duplicates, then simple run the following job to remove them.
static void KLFRemoveDuplicates(Args _args)
{
Set fieldSet = new set(Types::Integer);
DictIndex dictIndex = new DictIndex(
tablenum(KLFTestDuplicates),
indexnum(KLFTestDuplicates, AUniqueIdx));
int i;
;
if(dictIndex.numberOfFields())
{
for(i=1;i<=dictIndex.numberOfFields();i++)
{
fieldSet.add(dictIndex.field(i));
}
ReleaseUpdateDB::indexAllowDup(dictIndex);
ReleaseUpdateDB::deleteDuplicatesUsingIds(tablenum(KLFTestDuplicates), 0, fieldSet);
ReleaseUpdateDB::indexAllowNoDup(dictIndex);
}
info("done");
}
Remember: this is useful when developing and testing, but it will remove data from you database, so use it with caution.
-
April 9, 2010 at 10:43
(no comments)
When creating a query and adding ranges, you might notice that there is a limit on how the range string can be. This limit is defined by the extended data type Range that you can find in the AOT. You can try extending the size to a bigger stringsize, but you can’t go too high before you run into limits that the sql database will have (I think this value is around 1700 characters).
What you can do is add multiple smaller ranges on the same field in stead of one long range.
I’ve written a method that splits a long range and adds it to you queryBuildDataSource:
private static void splitAndAddRange(QueryBuildDataSource _qbds,
fieldId _fieldId,
str _sRange)
{
#AOT
// used to split and build ranges
int splitPos; // max position for splitting
int cPos; // position of ','
str fRange; // first part of the range
str lRange; // last part of the range
QueryBuildRange qbr; // the extra qbr if needed
;
// define max lenght
// note: make this function none static and get the stringsize property only once while initialising your class
// to speed up performance
splitPos = treenode::findNode(#ExtendedDataTypesPath + #AOTRootPath + 'Range').AOTgetProperty('StringSize');
// define range string
// init cPos
cPos = -1;
// if range is too long -> split (3 should be 1500)
if(strlen(_sRange) > splitPos)
{
// while no comma found
while(cPos == -1 && splitPos > 0)
{
// if char at current position is ',', split position found
if(substr(_sRange, splitPos, 1) == ',')
cPos = splitPos;
// else try prev char
else
splitPos--;
}
// get the first part of the range
fRange = substr(_sRange, 0, splitPos - 1);
// make new range and add it
qbr = _qbds.addRange(_fieldId);
qbr.value(fRange);
// get the remaining part of the range
lRange = substr(_sRange, splitpos, strlen(_sRange));
// split the rest of the range
if(strlen(lRange) > 0)
{
KLFSplitAndAddRangeTest::splitAndAddRange(_qbds, _fieldId, lRange);
}
}
else
{
// range is not too long, don't split, just add
qbr = _qbds.addRange(_fieldId);
qbr.value(_sRange);
}
}
When you put this method in a class along with the following one, you can test the method.
static void main(Args _args)
{
Query query;
QueryBuildDataSource qbds;
str accountNumRange;
// used for info-ing
int rangeLength;
int rangeLengthAOT;
#AOT
;
accountNumRange += 'A0000000001,A0000000002,A0000000003,A0000000004,A0000000005,A0000000006,A0000000007,A0000000008';
accountNumRange += '0000000009,A0000000010,A0000000011,A0000000012,A0000000013,A0000000014,A0000000015,A0000000016';
accountNumRange += '0000000017,A0000000018,A0000000019,A0000000020,A0000000021,A0000000022,A0000000023,A0000000024';
accountNumRange += '0000000025,A0000000026,A0000000027,A0000000028,A0000000029,A0000000030,A0000000031,A0000000032';
accountNumRange += '0000000033,A0000000034,A0000000035,A0000000036,A0000000037,A0000000038,A0000000039,A0000000040';
accountNumRange += '0000000041,A0000000042,A0000000043,A0000000044,A0000000045,A0000000046,A0000000047,A0000000048';
accountNumRange += '0000000049,A0000000050,A0000000051,A0000000052,A0000000053,A0000000054,A0000000055,A0000000056';
accountNumRange += '0000000057,A0000000058,A0000000059,A0000000060,A0000000061,A0000000062,A0000000063,A0000000064';
accountNumRange += '0000000065,A0000000066,A0000000067,A0000000068,A0000000069,A0000000070,A0000000071,A0000000072';
accountNumRange += '0000000073,A0000000074,A0000000075,A0000000076,A0000000077,A0000000078,A0000000079,A0000000080';
accountNumRange += '0000000081,A0000000082,A0000000083,A0000000084,A0000000085,A0000000086,A0000000087,A0000000088';
accountNumRange += '0000000089,A0000000090,A0000000091,A0000000092,A0000000093,A0000000094,A0000000095,A0000000096';
accountNumRange += '0000000097,A0000000098,A0000000099,A0000000100';
query = new Query();
rangeLength = strlen(accountNumRange);
rangeLengthAOT = treenode::findNode(#ExtendedDataTypesPath + #AOTRootPath + 'Range').AOTgetProperty('StringSize');
info(strfmt('Range length: %1',rangeLength));
info(strfmt('range length AOT: %1', rangeLengthAOT));
info(strfmt('There should be %1 ranges', roundup(rangeLength / rangeLengthAOT, 1)));
qbds = Query.addDataSource(tablenum(CustTable));
KLFSplitAndAddRangeTest::splitAndAddRange(qbds, fieldnum(CustTable, AccountNum), accountNumRange);
info(strfmt('Range count: %1',qbds.rangeCount()));
}
The output is:
Range length: 1175
range length AOT: 250
There should be 5,00 ranges
Range count: 5
Using a tool I have I can also print out the query for a closer look:

You can clearly see that the 5 ranges have been added.
-
April 8, 2010 at 22:49
(no comments)
Hi everyone.
When I was starting to learn about AIF, I made a list of the resources I used to get familiar with it.
I know how hard it is to learn AIF (there’s a lot you need to know), so I’d like to share this list of links with you.
General:
Blogs:
Videos:
Axaptapedia:
Books:
- Inside Microsoft Dynamics AX 2009: Chapter 17 The Application Integration Framework
Of course, there are also some articles on this blog that are tagged AIF.
Hope you find these links useful!
-
April 7, 2010 at 17:45
(no comments)
Some lines of code in AX look a bit strange, #if.never is one of those. I notice that some people don’t fully understand what this is for, or don’t know when and how to use it.
Take a look at the following code sample:
static void klforIfNever(Args _args)
{
;
#if.never
some text or maybe some code:
i = i++;
#endif
}
Notice that this will compile although the code between #if.never and #endif has no valid syntax.
How does this work?
The #if macro is one of the built in macro’s in ax. It tests if a macro has been defined.
As you know, you can define a macro like this:
To test if this macro has been defined, type:
When testing if a macro has been defined, you should end your #if with #endif to make your code compile, just like in the sample job above.
So basically, #if.never is a way to test if the macro named “never” has been defined.
A little side effect of testing whether a macro has been defined using #if and #endif is that if the macro hasn’t been defined, the code in between the #if and #endif will not be compiled or executed. Basically, you can write anything in there, as long as you don’t define the macro (#if.blabla would also work, but #if.never is clearer).
Why would you write code that doesn’t get compiled and executed? Here are two reasons I can think of:
- You can use it in interface classes to provide sample code on how the class can be implemented (See: sysPackable).
- You can use it as an alternative to commenting out code. Using #if.never will still show your code with markup in stead of everything in green.
So next time you see #if.never, you know what it is for :).
-
April 5, 2010 at 11:28
(no comments)
Last week, I attended TechDays 2010 in Antwerp. During day 2, Scott Hanselman did a half an hour session during lunch titled “How to make your blog suck less”. The session was on an article he wrote on his blog: 32 Ways to Keep Your Blog from Sucking.
Based on his session, I made my personal top 3 todo-list for my blog:
- Make a favicon for my blog;
- Create an about me page;
- Look into licensing (looks like putting © is not enough?).
One thing I would like to add to Scotts list: get your own domain name for you blog.
After reading Scott’s list, in what way does your blog suck?
-
April 2, 2010 at 16:05
(no comments)
Hi again. Yesterday was day two of two of TechDays 2010 in Antwerp for me, and here are my findings. Read about day one here.
I attended following sessions:
- UnKeynote – Lap Around .NET 4 by Scott Hanselman
- Deepdive into Windows Azure by Sumit Mehrotra
- How to make your blog suck less by Scott Hanselman
- Visual Studio 2010 IDE Tips by Sarah Ford
- What is new in WCF4 by Peter Himschoot
Same as the day before, I didn’t take any notes, so here’s what stuck in my head:
1. UnKeynote – Lap Around .NET 4 by Scott Hanselman
This session by Scott was more or less what Anders Hejlsberg talked about the day before (he even used some of the same demo’s), but he talked about it with so much passion that you weren’t bothered by that. Same as Anders, he talked about the ‘dynamic’ type that improves the interoperability between languages, the parallel processing and more. He’s such a good speaker that I decided to go to his session about blogging later that day.
2. Deepdive into Windows Azure by Sumit Mehrotra
I was totally new to Windows Azure [wiki] when I went to this session, but although it was a ‘deep dive’, I think the global idea about what Azure is got across just fine.
Basically, Microsoft has put data centers all over the world, and they have provided Azure as a service that you can subscribe to that deploys your applications on these servers. You don’t have to worry about setting up servers, making sure that the setup is redundant, that there is load balancing, etc, Azure does this for you. You just focus on creating applications so you have more time to create value for your customer, and then deploy them on the Azure “cloud”. From then on, if you want your application to be available on an other continent, if you want it to go faster, if you want to upgrade, etc, it’s just a few clicks away (and Benjamins from your bank account).
I find it a really interesting service, and you and your customers will probably benefit from Azure in the long run.
3. How to make your blog suck less by Scott Hanselman
A very interesting session by Scott for bloggers. In fact, so interesting that I will blog about it in a separate post later.
4. Visual Studio 2010 IDE Tips by Sarah Ford
This session was fun, but kind of trivial, although I can imagine some tips being helpful when you’re working with Visual Studio all day. If you’re a .NET developer, you can check out her website, she has all tips and tricks over there as well.
Note: This was the only session I was planing on taking notes, but she specifically said not to do that, because everything is already on her website. Nice.
5. What is new in WCF4 by Peter Himschoot
Although I don’t know a lot about WCF (only basic stuff about web services and SOA), I found this session interesting because I have been working with AIF lately. Although AIF takes care of all the WCF stuff, it’s handy to know how WCF works in case something goes wrong.
-
March 31, 2010 at 23:32
(2 comments)
Hi everyone. Today was day one of two at TechDays 2010 in Antwerp for me, and I would like to share my thoughts.
First of, the organization of the event was good and professional, and although it was a bit crowded at times, everything went pretty smooth and relaxing.
I attended the following sessions:
- Developer Keynote – Trends and future directions in programming languages by Anders Hejlsberg
- Branching & Merging strategies with Team Foundation Server 2010 by Pieter Gheyses
- XNAstroids: How To develop the asteroids game in 30 minutes with the XNA framework by Timothy Vanherberghen
- C# 4.0 and beyond by Anders Hejlsberg
- ORM With ADO.NET Entity Framework in .NET 4.0 by Kurt Claeys
- A lap aound Visula Studio 2010 Application Lifecycle Management by Brian Keller
- Making TDD work using Virtual Studio 2010 by Bart Wullems
I’ll go into detail a bit, but as I didn’t take any notes during these session, I will only talk about the things that really stuck and that I found interesting.
1. Developer Keynote – Trends and future directions in programming languages by Anders Hejlsberg
In his keynote, Anders Hejlsberg talked abou the trends that he sees in programming languages and where it is all going in the future.
The things that I found interesting (and remembered :-)) the most were:
- Programming languages will become a mix between dynamic languages and imperative languages. This means that we will see languages that will compile certains things at compile time, while also compiling things at runtime.
- Because processors will have more and more cores, program languages will have to provide a way perform parallel processing in a more developer friendly than is the case now. We’ll also have to find a solution to concurrency (= running one task on multiple cores/processors)
2. Branching & Merging strategies with Team Foundation Server 2010 by Pieter Gheyses
Wow, this is interesting, and impressive. Well, maybe it might not be for .NET developers, but it is for me as an AX developer. If you don’t know what branching with TFS is, you can read about it here for starters. From what I’ve seen today, it allows you to do parallel development, and provides a proper way of maintaining release build and hotfixes on these (which can be bundled to a service pack).
That’s way better then what I was picturing when thinking about a version control system for AX, but definitely something that should be supported in future versions of AX. It’s just so much more than a version control system, that makes you wish you were a .NET developer :-).
3. XNAsteroids: How To develop the asteroids game in 30 minutes with the XNA framework by Timothy Vanherberghen
This was actually a half an hour session during the lunch break. I must say, while it was impressive to see Timothy build a game in such a short time, there was a lot of copy and pasting involved, and you probably couldn’t write this game in a half an hour when starting from scratch.
Nevertheless, XNA looks like a powerful tool for creating games, even for those who are just game enthusiasts and not ‘real’ programmers.
4. C# 4.0 and beyond by Anders Hejlsberg
This was probably the most technical and abstract sessions, but also the most interesting one. The things I want to talk about:
- “Dynamic” in C#
After lecturing us about how good strongly typed languages are and such, Anders introduced the “dynamic” keyword (I keep typing Dynamics, don’t know why…). There’s a nice blog entry about it here, so I won’t try to explain myself.
Personally, while this ‘looks cool’ and such, I think it’s stupid (there I said it :-)) and you should strongly type it. While you could argue that ‘dynamic’ is a type itself, basically all it does is tell the compiler that it shouldn’t compile the code we write when calling methods and properties of this variable.
I’m totally against the kind of (meta) code below:
calcobject.callfunction("add", {1, 2});
Why? Because “add” is passed as a string and no compilation (=verification of code) that happens.
This is how it should be:
And this is how it *IS* with ‘dynamic’ variables, but it doesn’t do any good when the ‘add’ doesn’t get compiled (verified) imho.
It’s exactly the same, it just looks better, but I’m not impressed with some new fancy syntax.
- Named and optional parameters
Anyway, what I did agree with (and want in AX), are named parameters (optional parameters we already have). There are numeral articles on the web explaining this, so I wont do it here. Basically, if we had named parameters in AX, this would mean that we don’t have to specify all optional parameters that are declared before the parameter you want.
Concider this:
public void doSmth(str _str = "", int _i= 0)
When we want to pass _i, we have to specify _str, even if it’s optional.
So in stead of:
We would be able to do this:
public void doSmth(_i:1234)
For now, only in C#, but maybe in AX in 201? :-).
5. ORM With ADO.NET Entity Framework in .NET 4.0 by Kurt Claeys
Well, I haven’t really become a fan of ADO.NET EF during this session, so I will be brief about this. I would conciser using this for small applications that I know wont have a significant load on the sql side, but for big applications, I think the framework will just be to slow. You probably could optimize by writing stored procedures, but that’s a bridge to far for me.
Also, on principle, I’m against ADO, as in AX (at least by default) it only runs on client side, and not on server.
6. A lap around Visual Studio 2010 Application Lifecycle Management by Brian Keller
This session was partially about branching with TFS that was also covered in the session by Pieter Gheyses, but zoomed out a bit to the lifecycle level. This session confirmed how brilliant the TFS solution is. I was also impressed with the interaction between these applications and the office suite, sharepoint, etc.
7. Making TDD work using Virtual Studio 2010 by Bart Wullems
I thought this was an interesting sessions, as it made me think about methodologies like TTD (Test driven design), but also SCRUM, etc. I believe that you shouldn’t be to fanatic about what methodology to use, because these methodologies only really define the “how”. How what? How to implement certain design/programming principles and best practices.
I think it is more important to understand the ‘why’ of these principles exists, so you really understand why you are doing things the way you do them. Everyone can do things as there told (how), but only the best understand it (why). When you understand the basic principles and patterns, you can benifit from them always, no matter what methodology you are using.
The weak point of TDD is that you can’t use it in all circumstances. In some cases, you can’t write unit tests so you can use TDD. One benefit is that it helps you understand principles and patterns better (like encapsulation). Or as Bart says it: “You get all of that for free”.
Part 2: Recap: TechDays 2010 Antwerp – Day Two
-
February 24, 2010 at 22:57
(no comments)
A while ago, I talked about deleting an AX company on SQL, but of course, you can also use the sp_MSforeachtable stored procedure to rename a company (= change the DataAreaId of a company).
In the next example, I rename CEE to CEA:
exec sp_MSforeachtable 'update ? set DataAreaID = "CEA" where ?.DataAreaID = "CEE"'
UPDATE DataArea SET ID = 'CEA' WHERE DataArea.ID = 'CEE'
UPDATE CompanyDomainList SET CompanyID = 'CEA' WHERE CompanyID = 'CEE'
-
February 18, 2010 at 18:13
(5 comments)
I notice that a lot of people are looking for a way to syntax color their xpo’s when viewing them in Notepad++.
There is an article on Axaptapedia that covers this, that includes a sample xml snippet you can use.
For some reason though, when you google “x++ notepad++”, the Axaptapedia page is nowhere to be found.
I’ve also created a custom language based on the one on wikipedia, that uses colors that resemble the ones in the X++ editor a bit more:
<UserLang name="XPP_XPO" ext="xpo">
<Settings>
<Global caseIgnored="no" />
<TreatAsSymbol comment="no" commentLine="no" />
<Prefix words1="no" words2="no" words3="no" words4="no" />
</Settings>
<KeywordLists>
<Keywords name="Delimiters">"'0"'0</Keywords>
<Keywords name="Folder+">SOURCE { CLASS METHODS PROPERTIES CONTROL CONTAINER ARRAY INDEX FORM OBJECTBANK DATASOURCE OBJECTPOOL FIELDLIST JOINS DESIGN TYPEELEMENTS TYPEREFERENCES USERTYPE FIELDS GROUPS GROUPFIELDS GROUP DATAFIELD INDICES INDEXFIELDS REFERENCES REFERENCE FIELDREFERENCES DELETEACTIONS BEGINNODE MENUITEM MENU</Keywords>
<Keywords name="Folder-">ENDSOURCE } ENDMETHODS ENDCLASS ENDPROPERTIES ENDCONTROL ENDCONTAINER ENDARRAY ENDFORM ENDOBJECTBANK ENDDATASOURCE ENDOBJECTPOOL ENDFIELDLIST ENDJOINS ENDDESIGN ENDTYPEELEMENTS ENDTYPEREFERENCES ENDUSERTYPE ENDFIELDS ENDGROUPS ENDGROUPFIELDS ENDGROUP ENDINDEXFIELDS ENDINDICES ENDFIELDREFERENCES ENDREFERENCE ENDREFERENCES ENDDELETEACTIONS ENDTABLE ENDNODE ENDPROJECT ENDDATAFIELD ENDMENUITEM ENDMENU</Keywords>
<Keywords name="Operators">' ! " ?</Keywords>
<Keywords name="Comment">1/* 2*/ 0//</Keywords>
<Keywords name="Words1">Formatversion: Configuration: language directory company user client fetchahead opencursors database dsn sqluser hint sqlbuffer log hassqlpwd sqlpwd sqlparm retry dbserversqltrace haswarnings warnings share bindir startupmsg servermask localappl localappldoc locallabel localsysdoc applshare applexclusive doclanguage startupcmd logdir hascompwd compwd hasserveridletimeout serveridletimeout connectionidletimeout querytimelimit extracmdline port createdsn createdsn_tcpipport aos allowunauth exposeserverprers useserverprers sqlformliterals sqlcomplexliterals sqloraclefirstrowsfix ignoredatasourceindex aosencryption xppdebug dbcli ociconnectservice ociservice ocihost ocidbid ocitcpipport ociuser hasocipwd ocipwd preloadthresholdmsec preloadthresholdrecords dbunicodeenabled newconnectionretrydelayms newconnectionretrycount cachesynctime _clientmode _clientadname application broadcast internet aol aolcode sql native dbserver sqltrace exposeserverprinters useserverprinters ***Element FRMVERSION str container int select firstonly where while for Filename if return void public super element Return Yes No Delayed
 First Auto Default Normal Watch Vertical NoAccess Main Column width None Outside Opaque Button face View Grid Window background Highlight text Left Right only Text info run Version Else error Args Raised height Horizontal flush right boolean Common QueryRun FormTreeItem class Set FormTreeControl map anytype Dialog SysDictField this New new Query QueryBuildRange else QueryBuildDataSource Array mapiterator ttsbegin ttscommit FileName SaxReader SaxErrorHandler SaxAttributes TextBuffer protected catch try Called from classDeclaration Arrow USERTYPEVERSION TABLEVERSION FIELD Integer Absolute AutoReport AutoLookup REFERENCETYPE NORMAL static Miscellaneous Delete Not specified String Memo Cascade PROJECTVERSION SHARED JOBVERSION NoYes private null count Map DictTable break ttsBegin ttsCommit Error Exception true sum server DictField Counter throw join abstract extends endif index continue false switch case Version Restricted InnerJoin Active Standard Display VERSION MNUVERSION</Keywords>
<Keywords name="Words2">Text Int Name Table Index Company CounterField AllowCheck AllowEdit AllowCreate AllowDelete StartPosition AutoSearch AutoNotify AutoQuery OnlyFetchActive JoinSource LinkType DelayActive InsertAtEnd InsertIfEmpty Left Top Width Height Visible Caption TitleDatasource Frame WindowResize WindowType SaveSize SaveSize HideToolbar SetCompany ColorScheme CssClass ShowWebHelp BackgroundColor ImageName ImageResource Imagemode Mode SubmitMethod SupportReload LocalWebMenu AllowDocking Font FontSize Italic Underline Bold CharacterSet LabelFont LabelFontSize LabelItalic LabelUnderline LabelBold LabelCharacterSet DataSource TopMargin BottomMargin LeftMargin RightMargin ArrangeWhen ArrangeMethod Columns Columnspace ArrangeGuide HideIfEmpty AlignChildren AlignChild AllowUserSetup NeededAccessLevel AutoDeclaration VerticalSpacing Enabled Skip AlignControl HelpText ConfigurationKey SecurityKey DragDrop FrameType FramePosition BackStyle FrameOptionButton OptionValue DataGroup AutoDataGroup MultiSelect VisibleCols VisibleRows ShowColLabels ShowRowLabels HighlightActive ActiveBackColor ActiveForeColor GridLines LookupButton ReplaceOnLookup LimitText DisplayLength DisplayHeight Border Value Alignment SignDisplay RotateSign ShowZero DisplaceNegative AllowNegative ForegroundColor LabelForegroundColor ShowLabel Label LabelWidth LabelHeight LabelPosition LabelAlignment DataField Mandatory ArrayIndex SearchMode PasswordStyle ChangeCase MultiLine ExtendedDataType DataMethod ButtonDisplay NormalImage NormalResource DisabledImage DisabledResource ShowShortCut DefaultButton SaveRecord Tabs Tab TabAppearance ShowTabs TabPlacement TabLayout SelectControl TabAutoChange Extends RunOnFormHelp ArrayLength FormHelp ButtonImage StringSize Adjustment GroupPrompt SaveContents AliasFor AllowEditOnCreate FieldUpdate Type OldName AllowDuplicates Validate Field RelatedField TitleField1 TitleField2 Temporary TableContents Systemtable MaxAccessMode CreateRecIdIndex SaveDataPerCompany TableGroup PrimaryIndex ClusterIndex ModifiedDate ModifiedTime ModifiedBy ModifiedTransactionId CreatedDate CreatedTime CreatedBy CreatedTransactionId DeleteAction FormRef CacheLookup FILETYPE UTILTYPE UTILOBJECTID NODETYPE NAME EnumType Style RunOn AllowAdd Selection HideFirstEntry ComboType AppendNew SizeWidth SizeHeight MenuItemType MenuItemName DateValue DateFormat DateSeparator DateYear DateMonth DateDay RealValue ThousandSeparator DecimalSeparator NoOfDecimals AutoInsSeparator FormatMST Class Object Parameters EnumTypeParameter EnumParameter Web WebAccess WebSecureTransaction WebPage CountryConfigurationkey WebConfigurationkey WebTarget ProjectGroupType GroupMask PreventEditProperties</Keywords>
<Keywords name="Words3">FRM GRID INTEDIT STRINGEDIT BUTTON TAB TABPAGE CLS CLSVERSION || && UTI INT UTS STRING DBT TABLE PRN PROJECT END \ JOB ENUM COMBOBOX CHECKBOX STATICTEXT BUTTONGROUP MENUITEMBUTTON MENUBUTTON WINDOW DATEEDIT REALEDIT SEPARATOR FTM MNU UTE</Keywords>
<Keywords name="Words4">@</Keywords>
</KeywordLists>
<Styles>
<WordsStyle name="DEFAULT" styleID="11" fgColor="000000" bgColor="FFFFFF" fontName="" fontStyle="0" />
<WordsStyle name="FOLDEROPEN" styleID="12" fgColor="FF8040" bgColor="FFFFFF" fontName="" fontStyle="0" />
<WordsStyle name="FOLDERCLOSE" styleID="13" fgColor="FF8040" bgColor="FFFFFF" fontName="" fontStyle="0" />
<WordsStyle name="KEYWORD1" styleID="5" fgColor="0000FF" bgColor="FFFFFF" fontName="" fontStyle="1" />
<WordsStyle name="KEYWORD2" styleID="6" fgColor="007F00" bgColor="FFFFFF" fontName="" fontStyle="2" />
<WordsStyle name="KEYWORD3" styleID="7" fgColor="FF0000" bgColor="FFFFFF" fontName="" fontStyle="1" />
<WordsStyle name="KEYWORD4" styleID="8" fgColor="FF0000" bgColor="FFFFFF" fontName="" fontStyle="0" />
<WordsStyle name="COMMENT" styleID="1" fgColor="007F00" bgColor="FFFFFF" fontName="" fontStyle="0" />
<WordsStyle name="COMMENT LINE" styleID="2" fgColor="007F00" bgColor="FFFFFF" fontName="" fontStyle="0" />
<WordsStyle name="NUMBER" styleID="4" fgColor="000000" bgColor="FFFFFF" fontName="" fontStyle="0" fontSize="10" />
<WordsStyle name="OPERATOR" styleID="10" fgColor="FF0000" bgColor="FFFFFF" fontName="" fontStyle="1" />
<WordsStyle name="DELIMINER1" styleID="14" fgColor="FF0000" bgColor="FFFFFF" fontName="" fontStyle="0" />
<WordsStyle name="DELIMINER2" styleID="15" fgColor="FF0000" bgColor="FFFFFF" fontName="" fontStyle="0" />
<WordsStyle name="DELIMINER3" styleID="16" fgColor="000000" bgColor="FFFFFF" fontName="" fontStyle="0" />
</Styles>
</UserLang>
This is how it looks in Notepad++:

Syntax coloring X++ in Notepad++
Links:
Notepad++ homepage
Notepad++ on Axaptapedia
-
at 18:06
(1 comment)
In part 1, we talked about the use of a protected new method and a static constructor, and how it helps improve your classes.
Now let’s see how it helps you maintain you code better when extending classes.
In the previous example, we printed some general information about a record to the screen.
Let’s say that, in case of CustTable we want to have information printed to to screen that is specific to this record, like the AccountNum or Name field.
We will need to create a new class that extends the class we created earlier.
1. ClassDeclaration KLFShowRecordInfo_CustTable
In the ClassDeclaration, we include the keyword ‘extends’ to specify that our new class extends the functionality of the class KLFShowRecordInfo.
class KLFShowRecordInfo_CustTable extends KLFShowRecordInfo
{
}
2. Construct method KLFShowRecordInfo_CustTable
We will also add a construct method to this class. This will create an instance of this class for us, and set the parm method.
Because we extend a class that contains this parm method, we don’t need to add it to this class again.
public static KLFShowRecordInfo_CustTable construct(Common _record)
{
KLFShowRecordInfo_CustTable kLFShowRecordInfo_CustTable;
;
kLFShowRecordInfo_CustTable = new KLFShowRecordInfo_CustTable();
kLFShowRecordInfo_CustTable.parmRecord(_record);
return kLFShowRecordInfo_CustTable;
}
3. Run method KLFShowRecordInfo_CustTable
In the run method, we print data from the CustTable record to screen.
void run()
{
CustTable custTable;
;
custTable = this.parmRecord();
info(custTable.AccountNum);
info(custTable.Name);
}
You could call the super() method if you want the general information to be printed as well.
4. Construct method KLFShowRecordInfo
The only thing we need to change to the existing class is the construct method.
Modify it like this:
public static KLFShowRecordInfo construct(Common _record)
{
KLFShowRecordInfo kLFShowRecordInfo;
;
switch (_record.TableId)
{
case tablenum(CustTable):
kLFShowRecordInfo = KLFShowRecordInfo_CustTable::construct(_record);
break;
default :
kLFShowRecordInfo = new KLFShowRecordInfo();
kLFShowRecordInfo.parmRecord(_record);
break;
}
return kLFShowRecordInfo;
}
The switch will check which table is being processed, and will create an instance of KLFShowRecordInfo_CustTable in case of CustTable. For all other tables, it will construct an instance of KLFShowRecordInfo.
5. TestJob
Let’s test it:
static void KLF_TestRecordInfo(Args _args)
{
CustTable custTable;
VendTable vendTable;
;
select firstonly custTable;
select firstonly vendTable;
// custTable:
KLFShowRecordInfo::construct(custTable).run();
info("----");
// vendTable:
KLFShowRecordInfo::construct(vendTable).run();
}
Output:
00000001
IKEA
—-
VendTable
Conclusion
This concludes my articles about the protected new and construct methods. I realise that the examples are very simple, but the principle will remain the same, even in more complex scenarios.
To sum up:
My basic recommendations when creating classes are:
- Use a protected new method
- Use a construct method
- Use parm methods
- Create a run method that contains your business logic
- Instead of modifying existing classes, create new ones that extend existing
If you disagree, be sure to leave a comment :).
-
February 17, 2010 at 18:51
(no comments)
To have AIF import and export message, you have to have 4 batch tasks running (all about it here on TechNet). However, when developing, it is inefficient (and also tad tedious) to wait for those batches.
Here are two jobs to run inbound and outbound messages manually, so you don’t have to wait for the batches to pick them up.
Inbound:
static void KlForrunAIFInbound(Args _args)
{
;
// read the messages
new AifGateWayReceiveService().run();
// process the messages in queue
new AifInboundProcessingService().run();
info("done");
}
Outbound:
static void KlForrunAIFOutbound(Args _args)
{
;
// process messages in queue
new AifOutboundProcessingService().run();
// send messages
new AifGateWaySendService().run();
info("done");
}
Ofcourse, you can put this code in classes, or combine them so they are executed together. Because inbound messages can trigger outbound messages, it’s better to process the inbound messages before the outbound messages.
-
February 15, 2010 at 10:16
(no comments)
In this post I want to show you the use of the new() and construct() method when instantiating classes using a simple example class. This is a design that is used a lot an AX.
It is best practice to set the new method as protected (MSDN), and it is also best practice to have a construct method in your class (MSDN). So let’s see how we should implement these best practices.
Like a good recipe, a good class has a few important ingredients:
- Parm methods for variables
- A protected new method
- A static contructor
- A run method
1. ClassDeclaration
As an example, we will create a new class: KLFShowRecordInfo
The function of the class will be to display some information about a record.
class KLFShowRecordInfo
{
Common record;
}
This class has one variable, a record. The type is Common so we can store any record in this variable.
2. Parm method
First thing is to create a parm-method for this variable (created using parmFromCD editorscript):
public Common parmRecord(Common _record = record)
{
;
record = _record;
return record;
}
Your code will be much cleaner and easier to debug when you use parameter methods.
3. New Method
Next, we will override the new method and make it protected:
Methods that are declared as protected can only be called from methods in the class and in subclasses of the class where the protected method is declared. When we want to create an instance of this class from an other class, we’ll have to call the construct method.
When calling the new() method in any other class, we would get the following compiler error:
The method is declared protected and may only be called from methods in classes derived from KLFShowRecordInfo.
4. Construct method
When using the editorscript to create a construct() method (Right click – Scripts – template – method – construct), it will look like this:
public static KLFShowRecordInfo construct()
{
return new KLFShowRecordInfo();
}
However, I always rewrite it like like this because it is easier to extend afterwards:
public static KLFShowRecordInfo construct()
{
KLFShowRecordInfo kLFShowRecordInfo;
;
kLFShowRecordInfo = new KLFShowRecordInfo();
return kLFShowRecordInfo;
}
Now we’ll have to modify the construct method to receive a parameter, and use the parm method to set the variable in our class:
public static KLFShowRecordInfo construct(Common _record)
{
KLFShowRecordInfo kLFShowRecordInfo;
;
kLFShowRecordInfo = new KLFShowRecordInfo();
kLFShowRecordInfo.parmRecord(_record);
return kLFShowRecordInfo;
}
5. Run method
All we need now is some logic to add to this class. Most of the time, I name the method ‘run’, but you can give it any name you like. The method will display the name of the table of the record you pass to it.
void run()
{
;
info(tableid2name(this.parmRecord().TableId));
}
6. Test job
Next, a job to test it:
static void KLF_TestRecordInfo(Args _args)
{
CustTable custTable;
;
select firstonly custTable;
// this will not work because the new method is protected
// kLFShowRecordInfo = new KLFShowRecordInfo();
KLFShowRecordInfo::construct(custTable).run();
}
You can see that we only need one line of code to call our class, and we don’t need a variable, which makes the code cleaner.
Output:
CustTable
Conclusion
When creating a class, start with declaring the new method as protected. This will force you to use a static construct() method. This in turn will force you to think twice about the structure of your class, and will result in cleaner/better code with a more ‘AX-y’ feel.
In part 2, I will demonstrate how this design principle helps you to maintain your code when extending functionality.
-
February 5, 2010 at 14:46
(no comments)
On March 31 and April 1, I will be attending Microsoft TechDays 2010 conference in Antwerp, Belgium. Me and my colleagues will be wearing a T-shirt that says RealDolmen (no doubt followed by a catchy slogan the PR people will come up with).
With over 80 sessions from international speakers to choose from, it will be difficult to pick the most interesting ones.
See you at TechDays.
Link: Microsoft TechDays 2010
-
February 4, 2010 at 12:16
(no comments)
Here’s a little ‘be-aware’ I’d like to share.
The function str2int() that converts a string to an integer will return ’0′ if the input string is not an integer, so it is necessary to check if the string is an integer before casting it.
The function that does this is not IsNumeric() like in SQL or VB, but IsInteger().
Alternatively, you can also use str2IntOk(). Both are found in the global class.
I’ve seen people searching for this method (including me), so I just thought I’d share.
Here’s some sample code:
static void klforStr2intAndIsIntegerTest(Args _args)
{
str input_notint = "abc123";
str input_int = "123";
int ret;
;
// be aware, str2int will convert 'non integers' to '0'
// without warning
info(strfmt("%1", str2int(input_notint)));
info(strfmt("%1", str2int(input_int)));
// you can check if a string is an integer with
// the isInteger() function from the global class
// or you the str2IntOk() function
info(strfmt("%1", isInteger(input_notint)));
info(strfmt("%1", isInteger(input_int)));
// check if integer
if(isInteger(input_notint))
{
// cast to integer
ret = str2int(input_notint);
}
else
{
throw error(strfmt("'%1' is not an integer", input_notint));
}
}
-
« Previous Page
Next Page »