Working with Forms in Dynamics AX: Part 2

Exclusive offer: get 50% off this eBook here
Microsoft Dynamics AX 2009 Development Cookbook

Microsoft Dynamics AX 2009 Development Cookbook — Save 50%

Solve real-world Microsoft Dynamics AX development problems with over 60 simple but incredibly effective recipes with this book and eBook

$29.99    $15.00
by Mindaugas Pocius | December 2009 | .NET Cookbooks Microsoft

Read Part One of Working with Forms in Dynamics AX here.

Adding form splitters

Commonly used forms like Sales orders or Projects in Dynamics AX have multiple grids. Normally, one grid is in the upper section and another one is in the bottom section of the form. Sometimes grids are placed next to each other.

The size of the data in each grid may vary, and that's why most of the forms with multiple grids have splitters in the middle so users can resize both grids at once by dragging the splitter with the help of a mouse. It is a good practice to add splitters to newly created forms.

Although Microsoft developers did a good job by adding splitters to most of the multi-grid forms, there is still at least one that has not got it. It is the Account reconciliation form in the Bank module, which is one of the most commonly used forms. It can be opened from Bank | Bank Account Details, Functions | Account reconciliation button, and then the Transactions button. In the following screenshot, you can see that the size of the bottom grid cannot be changed:

Microsoft Dynamics AX 2009 Development Cookbook

In this recipe, we will demonstrate the usage of splitters by resolving this situation. We will add a form splitter in the middle of the two grids in the mentioned form. It will allow users to define the sizes of both grids to make sure that the data is displayed optimally.

How to do it...

  • Open the BankReconciliation form in AOT, and create a new Group at the very top of the form's design with the following properties:
  •  

    Property

    Value

    Name

    Top

    AutoDeclaration

    Yes

    FrameType

    None

    Width

    Column width

     

  • Move the AllReconciled, Balances, and Tab controls into the newly created group.
  • Create a new Group right below the Top group with properties:
  • Property

    Value

    Name

    Splitter

    AutoDeclaration

    Yes

    Width

    Column width

    Height

    5

    FrameType

    Raised 3D

    BackgroundColor

    Window background

    HideIfEmpty

    No

    AlignChild

    No

  • Add the following line of code to the bottom of the form's class declaration:

  • SysFormSplitter_Y fs;
  • Add the following line of code to the bottom of the form's init():
  • fs = new SysFormSplitter_Y(Splitter, Top, element);
  • Override three methods in the Splitter group with the following code:
  • public int mouseDown(
    int _x,
    int _y,
    int _button,
    boolean _ctrl,
    boolean _shift)
    {
    return fs.mouseDown(_x, _y, _button, _ctrl, _shift);
    }
    public int mouseMove(
    int _x,
    int _y,
    int _button,
    boolean _ctrl,
    boolean _shift)
    {
    return fs.mouseMove(_x, _y, _button, _ctrl, _shift);
    }
    public int mouseUp(
    int _x,
    int _y,
    int _button,
    boolean _ctrl,
    boolean _shift)
    {
    return fs.mouseUp(_x, _y, _button, _ctrl, _shift);
    }
  • Change the following properties of the existing BankTransTypeGroup group:
  • Property

    Value

    Top

    Auto

    Width

    Column width

    Height

    Column height

  • Change the following property of the exiting TypeSums grid located in BankTransTypeGroup group:

    Property

    Value

    Height

    Column height

  • In AOT the Modified BankReconciliation form should look like the following screenshot:
  • Microsoft Dynamics AX 2009 Development Cookbook

  • Now, to test the results, open Bank | Bank Account Details, select any bank account, click Functions | Account reconciliation, choose an existing or create a new account statement, and click the Transactions button. Notice that now the form has a nice splitter in the middle, which makes the form look better and allows defining the size of each grid:
  • Microsoft Dynamics AX 2009 Development Cookbook

    How it works...

    Normally a splitter is placed between two form groups. In this recipe, to follow that rule, we need to adjust the BankReconciliation form's design. The filter AllReconciled, the group Balances and the tab Tab are moved to a new group called Top. We do not want this new group to be visible to user, so we set FrameType to None. Setting AutoDeclaration to Yes allows us to access this object from X++ code. And finally, we make this group automatically expand in the horizontal direction by setting its Width to Column width. At this stage, visual form layout did not change, but now we have the upper group ready.

    The BankTransTypeGroup group could be used as a bottom group with slight changes. We change its Top behavior to Auto and make it fully expandable in the horizontal and vertical directions. The Height of the grid inside this group also has to be changed to Column height in order to fill all the vertical space.

    In the middle of those two groups, we add a splitter. The splitter is nothing else but another group, which looks like a splitter. In order to achieve that, we set Height to 5, FrameType to Raised 3D, and BackgroundColor to Windows background. This group does not hold any other controls inside. Therefore, in order to make it visible, we have to set the property HideIfEmpty to No. The value No of the property AlignChild makes the splitter begin on the very left side of the form and the Column width value of the property Width forces the splitter to automatically fill the form's width.

    Mouse events are handled by the SysFormSplitter_Y application class. After it has been declared in the form's class declaration, we create the actual object in the form's init(). We pass the name of the splitter control, the name of the top group and the form itself as arguments when creating it.

    A fully working splitter requires three mouse event handlers. It is implemented by overriding the mouseMove(), mouseDown(), and mouseUp() methods in the splitter group control. All arguments are passed to the respective member methods of the SysFormSplitter_Y class which does all the job.

    In this way, horizontal splitters can be easily added to any form. The Dynamics AX application also contains nice examples about splitters, which can be found in AOT in the Tutorial_Form_Split form. Vertical splitters can also be added to forms using a very similar approach. For this, we need to use another application class called SysFormSplitter_X.

    Creating modal forms

    During my trainings and working with Dynamics AX users, I noticed that people who are not familiar with computers and software tend to get lost among open application windows. The same could be applied to Dynamics AX. I experienced many times when a user opened one form, clicked some button to open another one, and then went back to the first one without closing the second one. Sometimes this happens intentionally, sometimes—not, but the result is that the second form is hidden behind the first one and the user starts wondering why it is not possible to close or edit the first form.

    Such issues could be easily solved by making the child form a modal window. In other words, the second form always stays on top of the fi rst one until closed. In this recipe, we will do exactly that. As an example, we will make the Create sales order form a modal window.

    How to do it...

  • Open the SalesCreateOrder form in AOT, and set its Design property:
  • Property

    Value

    WindowType

    Popup

  • To test, open Accounts receivable | Sales Order Details, and start creating a new order. Notice that now the sales order creation form always stays on top of the Sales order form:
  • Microsoft Dynamics AX 2009 Development Cookbook

    How it works...

    Dynamics AX form design has a WindowType property, which is set to Standard by default. In order to make a form behave as a modal window, we have to change it to Popup. Such forms will always stay on top of the parent form.

    There's more...

    We already know that some of the Dynamics AX forms are created dynamically using the Dialog class. If we look deeper into the code, we could find that the Dialog class actually creates a runtime Dynamics AX form. That means we can apply the same principle, i.e. change the relevant form's design property. The following code could be added to the Dialog object and would do the job:

    dialog.dialogForm().buildDesign().windowType(
    FormWindowType::Popup);

    We get a reference to the form's design, by first using dialogForm() of the Dialog object to get a reference to the DialogForm object, and then we call buildDesign() on the latter object. Then, we set the design's property by calling its windowType() with an argument FormWindowType::Popup.

    Changing common form appearance

    In every single multi-company Dynamics AX project, in order to prevent user mistakes, I was asked to add functionality that allows setting the background color of every form per company. By doing that, users clearly see in which company account they are at the moment and can easily work within multiple companies at the same time.

    In this recipe, we will modify SysSetupFormRun class to change the background color for every form in Dynamics AX.

    How to do it...

  • Open SysSetupFormRun in AOT, and override its run() with the following code:

  • public void run()
    {;
    super();
    this.design().colorScheme(FormColorScheme::RGB);
    this.design().backgroundColor(WinAPI::RGB2int(255,0,0));
    }
  • To test the results, open any Dynamics AX form, for example, General ledger | Chart of Accounts Details and notice how the background color is changed to red:
  • Microsoft Dynamics AX 2009 Development Cookbook

    How it works...

    SysSetupFormRun is the application class that is called by the system every time a user runs a form. The best place to add our custom code is to override the run() method and place it under the super().

    We use this.design() to get a reference to the form's design. By calling colorScheme() and backgroundColor(), we set the color scheme to red/green/blue and the color code to red.

    We use WinAPI::RGB2int() to transform the human-readable red/green/blue code into the numeric color code.

    There's more...

    This recipe showed a very basic principle of how to change the common appearance of all forms with few lines of code. You noticed that the color in this recipe does not fi ll all areas of the form, which does not make the form look nice. An alternative to this could be to dynamically add a colored rectangle or something similar to the top of the form. The possibilities are endless here. New controls like input fields, buttons, menu items, and others could also be added to all forms dynamically using this class. But do not overdo as it may impact system performance.

    Storing last form values

    Dynamics AX has a very useful feature, which allows saving the latest user choices per user per form. This feature is implemented across a number of standard reports, periodic jobs, and other objects, which require user input.

    When developing a new functionality for Dynamics AX, I always try to keep that practice. One of the frequently used areas is custom filters for grid-based forms. Although, Dynamics AX allows users to use standard filtering for any grid, in practice sometimes it is not very useful, especially when the user requires something specific.

    In this recipe, we will see how to store the latest user filter selections. To make it as simple as possible, we will use existing filters on the General journal form, which can be opened from General ledger | Journals | General journal. This form contains two filters—Show and Show user-created only. Show allows displaying journals by their posting status and Show user-created only toggles between all journals and the currently logged user's journals.

    How to do it...

    1. Find the LedgerJournalTable form in AOT, and add the following code to the bottom of its class declaration:

      AllOpenPosted showStatus;
      NoYes showCurrentUser;
      #define.CurrentVersion(1)
      #localmacro.CurrentList
      showStatus,
      showCurrentUser
      #endmacro
    2. Create these additional form methods:

      public void initParmDefault()
      {;
      showStatus = AllOpenPosted::Open;
      showCurrentUser = true;
      }
      public container pack()
      {
      return [#CurrentVersion,#CurrentList];
      }
      public boolean unpack(container packedClass)
      {
      int version = RunBase::getVersion(packedClass);
      ;

      switch (version)
      {
      case #CurrentVersion:
      [version,#CurrentList] = packedClass;
      return true;
      default:
      return false;
      }
      return false;
      }
      public identifiername lastValueDesignName()
      {
      return element.args().menuItemName();
      }
      public identifiername lastValueElementName()
      {
      return this.name();
      }
      public UtilElementType lastValueType()
      {
      return UtilElementType::Form;
      }
      public userId lastValueUserId()
      {
      return curuserid();
      }
      public dataAreaId lastValueDataAreaId()
      {
      return curext();
      }
    3. xSysLastValue::getLast(this);
      AllOpenPostedField.selection(showStatus);
      ShowUserCreatedOnly.value(showCurrentUser);
      journalFormTable.designSelectionChangeAllOpenPosted();
      journalFormTable.designSelectionChangeShowUserCreateOnly();
    4. And the following code to the bottom of the form's close():
      showStatus = AllOpenPostedField.selection();
      showCurrentUser = ShowUserCreatedOnly.value();
      xSysLastValue::saveLast(this);
    5. Now to test the form, open General ledger | Journals | General journal, change filter values, close it, and run again. The latest filter selections should stay:

    6. Microsoft Dynamics AX 2009 Development Cookbook

    How it works...

    First of all, we define some variables. We will store the journal posting status filter value in showStatus and the current user filter value in showCurrentUser.

    The macro #CurrentList is used to define a list of variables that we are going to store. Currently, we have two variables.

    The macro #CurrentVersion defines a version of saved values. In other words, it says that the variables defined by the #CurrentList, which will be stored in system cache later, can be addressed using the number 1.

    Normally, when implementing last value saving for the first time for particular object, #CurrentVersion is set to 1. Later on, if we decide to add new values or change existing ones, we have to change the value of #CurrentVersion, normally increasing it by 1. This ensures that the system addresses the correct list of variables in the cache and does not break existing functionality.

    The initParmDefault()method specifies default values if nothing is found in the system cache. Normally, this happens if we run a form for the first time, we change #CurrentVersion or clean the cache. Later, this method is called automatically by the xSysLastValue object.

    The methods pack() and unpack() are responsible for formatting a storage container from variables and extracting variables from a storage container respectively. In our case, pack() returns a container consisting of three values: version number, posting status, and current user toggle. Those values will be sent to the system cache after the form is closed. During an opening of the form, the xSysLastValue object uses unpack() to extract values from the stored container. It checks the container version from cache first, and if it matches the current version number, then the values from the cache are considered correct and are assigned to the form variables.

    A combination of lastValueDesignName(), lastValueElementName(), lastValueType(), and lastValueDataAreaId() return values form unique string representing saved values. This ensures that different users can store last values for different objects without overriding each other's values in cache.

    The lastValueDesignName() method is meant to return the name of the object's current design in cases where the object can have several designs. In this recipe, there is only one design, so instead of leaving it empty, I used it for a slightly different purpose. The same LedgerJournalTable AOT form can represent different user forms like Ledger journal, Periodic journal, Vendor payment journal, and so on depending on the location from which it was opened. To ensure that the user's latest choices are saved correctly, we included the opening menu item name as part of the unique string.

    The last two pieces of code need to be added to the bottom of the form's run() and close(). In the run() method, xSysLastValue::getLast(this) retrieves saved user values from cache and assigns them to the form's variables. The next two lines assign the same values to the respective form controls. designSelectionChangeAllOpenPosted() and designSelectionChangeShowUserCreateOnly() execute a form query to apply updated filters. Although both of those methods currently perform exactly the same action, we keep both for the future in case this functionality is updated. Code lines in close() are responsible for assigning user selections to variables and saving them to cache by calling xSysLastValue::saveLast(this).

    Microsoft Dynamics AX 2009 Development Cookbook Solve real-world Microsoft Dynamics AX development problems with over 60 simple but incredibly effective recipes with this book and eBook
    Published: December 2009
    eBook Price: $29.99
    Book Price: $49.99
    See more
    Select your format and quantity:

    Using tree controls

    Frequent users should notice that some of the Dynamics AX forms have an option to switch to a tree layout. In some cases, especially when there are parent-child relations among records, it is a much clearer way to show the whole hierarchy as compared to a flat list. For example, projects and their subprojects displayed in the Project Details form give a much better overview when switched to a tree layout.

    This recipe will discuss the principles of how to build tree-based forms. As an example, we will use the Budget model form, which can be opened from General ledger | Setup | Budget | Budget model. This form contains a list of budget models and their submodels. Although the data is organized using a parent-child structure, currently this form does not have a hierarchy layout. The goal of this recipe is to correct that problem.

    How to do it...

    1. In AOT, create a new class called BudgetModelTree with the following code:

      class BudgetModelTree
      {
      FormTreeControl tree;
      BudgetModelId model;
      }
      void new(
      FormTreeControl _formTreeControl,
      BudgetModelId _budgetModelId)
      {;
      tree = _formTreeControl;
      model = _budgetModelId;
      }
      public static BudgetModelTree construct(
      FormTreeControl _formTreeControl,
      BudgetModelId _budgetModelId = '')
      {
      return new BudgetModelTree(
      _formTreeControl,
      _budgetModelId);
      }
      TreeItemIdx createNode(
      TreeItemIdx _parentIdx,
      BudgetModelId _modelId,
      RecId _recId)
      {
      TreeItemIdx itemIdx;
      BudgetModel modelSub;
      ;

      itemIdx = SysFormTreeControl::addTreeItem(
      tree,
      _modelId,
      _parentIdx,
      _recId,
      0,
      true);
      if (model == _modelId)
      {
      tree.select(itemIdx);
      }
      while select modelSub
      where modelSub.ModelId == _modelId &&
      modelSub.Type == HeadingSub::SubModel
      {
      this.createNode(
      itemIdx,
      modelSub.SubModelId,
      modelSub.RecId);
      }
      return itemIdx;
      }
      void buildTree()
      {
      BudgetModel modelMain;
      BudgetModel modelSub;
      TreeItemIdx itemIdx;
      ;

      tree.deleteAll();
      tree.lock();
      while select modelMain
      where modelMain.Type == HeadingSub::Heading
      notexists join modelSub
      where modelSub.SubModelId == modelMain.ModelId &&
      modelSub.Type == HeadingSub::SubModel
      {
      itemIdx = this.createNode(
      FormTreeAdd::Root,
      modelMain.ModelId,
      modelMain.RecId);
      SysFormTreeControl::expandTree(tree, itemIdx);
      }
      tree.unLock(true);
      }
    2. Open the BudgetModel form in AOT, and create a new tab page:

      Property

      Value

      Name

      TabTree

      Caption

      Tree

    1. Add a new Tree control to the new tab page:

    Property

    Value

     Name

    ModelTree

    Height

    Column height

    Width

    Column width

    1. Add the following code to the bottom of the form's class declaration:
      BudgetModelTree budgetModelTree;
    2. Override the form's init() with the following code:
      public void init()
      {;
      super();
      budgetModelTree = BudgetModelTree::construct(ModelTree);
      }
    3. Override pageActivated() on the TabTree tab page with the following code:
      public void pageActivated()
      {;
      super();
      budgetModelTree.buildTree();
      }
    4. In AOT the BudgetModel form should look like the following screenshot:

      Microsoft Dynamics AX 2009 Development Cookbook

    5. To test the tree control, open General ledger | Setup | Budget | Budget model, and select the Tree tab page. Notice how the ledger budget models are presented as a hierarchy:

      Microsoft Dynamics AX 2009 Development Cookbook

    How it works...

    In order to separate the tree from the rest of the layout, we create a new tab page. Then, we add the actual tree control to the tab, which we use as a basis. Tree nodes are always generated from code. We also place all the tree-building logic into a separate class to make sure that it can be reused elsewhere, and the form itself does not get messy.

    Besides the common new() and construct() methods, the class contains two methods, which actually generate the tree. The first method is createNode() and is used for creating a single budget model node or a whole branch. It is a recursive method, and it calls itself to generate the children of the current node. It accepts a parent node and a budget model as arguments. In this method, we create the node by calling the addTreeItem() method of the SysFormTreeControl class. The rest of the code loops through all submodels and creates subnodes (if there are any) for each of them.

    Secondly, we create buildTree() where the whole tree is built. Before we actually start building it, we delete all nodes and lock the Tree control. Then, we add nodes by looping through all parent budget models and calling the previously mentioned createNode(). We call the expandTree() of the SysFormTreeControl class to show every parent budget model expanded. Once the hierarchy is ready, we unlock the Tree control.

    Hierarchy generation might be time consuming, so we call it only when necessary, that is, when the tab page is actually opened. We override the tab page's pageActivated() and add a call to buildTree() there. Initially, to increase performance for bigger trees, only the first level of nodes has to be generated, and other nodes should be created only when the user clicks on the particular node. This could be achieved by placing the relevant code into the expanding() of the tree control in the form. Such an approach ensures that no time is spent on generating unused tree nodes.

    There's more...

    Besides hierarchical layout, tree controls also allow users to use drag-and-drop functionality. This makes daily operations much quicker and more effective. Let's modify the previous example to support drag-and-drop. We are going to allow users to move ledger budget submodels to different parents within the tree. In order to do that, we need to make some changes to the BudgetModelTree class and the BudgetModel form.

    Add the following code to the BudgetModelTree class declaration:

    TreeItemIdx dragItemIdx;
    TreeItemIdx lastItemIdx;

    Create the following additional methods in this class:

    boolean canMove()
    {
    BudgetModel budgetModel;
    RecId recId;
    ;
    recId = tree.getItem(dragItemIdx).data();
    select firstonly budgetModel
    where budgetModel.RecId == recId;
    return (budgetModel.Type == HeadingSub::SubModel);
    }
    void move(RecId _from, RecId _to)
    {
    BudgetModel modelFrom;
    BudgetModel modelTo;
    ;
    select firstonly modelTo
    where modelTo.RecId == _to;
    ttsbegin;
    select firstonly forupdate modelFrom
    where modelFrom.RecId == _from;
    modelFrom.ModelId = modelTo.SubModelId;
    if (modelFrom.validateWrite())
    {
    modelFrom.update();
    }
    ttscommit;
    }
    void stateDropHilite(TreeItemIdx _idx)
    {
    FormTreeItem item;
    ;
    if (lastItemIdx)
    {
    item = tree.getItem(lastItemIdx);
    item.stateDropHilited(false);
    tree.setItem(item);
    lastItemIdx = 0;
    }
    if (_idx)
    {
    item = tree.getItem(_idx);
    item.stateDropHilited(true);
    tree.setItem(item);
    lastItemIdx = _idx;
    }
    }
    int beginDrag(int _x, int _y)
    {;
    [dragItemIdx] = tree.hitTest(_x, _y);
    return 1;
    }
    FormDrag dragOver(
    FormControl _dragSource,
    FormDrag _dragMode,
    int _x,
    int _y)
    {
    TreeItemIdx currItemIdx;
    ;
    if (!this.canMove())
    {
    return FormDrag::None;
    }
    [currItemIdx] = tree.hitTest(_x, _y);
    this.stateDropHilite(currItemIdx);
    return FormDrag::Move;
    }
    void drop(
    FormControl _dragSource,
    FormDrag _dragMode,
    int _x,
    int _y)
    {
    TreeItemIdx currItemIdx;
    ;
    if (!this.canMove())
    {
    return;
    }
    this.stateDropHilite(0);
    [currItemIdx] = tree.hitTest(_x,_y);
    if (!currItemIdx)
    {
    return;
    }
    this.move(
    tree.getItem(dragItemIdx).data(),
    tree.getItem(currItemIdx).data());
    tree.moveItem(dragItemIdx, currItemIdx);
    }

    Locate the BudgetModel form in AOT, find its ModelTree control, and change the following property:

    Property Value
    DragDrop Manual

    Also, override the following methods of the ModelTree control:

    public int beginDrag(int _x, int _y)
    {
    return budgetModelTree.beginDrag(_x, _y);
    }
    FormDrag dragOver(
    FormControl _dragSource,
    FormDrag _dragMode,
    int _x,
    int _y)
    {
    return budgetModelTree.dragOver(
    _dragSource,
    _dragMode,
    _x,
    _y);
    }
    void drop(
    FormControl _dragSource,
    FormDrag _dragMode,
    int _x,
    int _y)
    {;
    budgetModelTree.drop(_dragSource, _dragMode, _x, _y);
    }

    Now when you open General ledger | Setup | Budget | Budget model, you should be able to move budget models within the tree with a mouse.

    The main element in the latter modification is the DragDrop property of the tree control. It enables the drag-and-drop function, once we set its value to Manual. The next step is to override drag-and-drop events on the tree control. Tree controls could have a number of methods covering various drag-and-drop events. A good place to start investigating them is the Tutorial_Form_TreeControl class in the standard Dynamics AX application. In this example, we will cover only three of them:

    • beginDrag() is executed when dragging begins. Here, we normally store the number of the item that is being dragged for later processing.
    • dragOver() is executed once the dragged item is over another node. This method is responsible for highlighting nodes when the dragged item is over them. Its return value defines the mouse cursor icon once the item is being dragged.
    • drop() is executed when the mouse button is released, i.e. dragged item is dropped over some node. Here, we normally place the code that does actual data modifications.

    In this example, all logic is stored in the BudgetModelTree class. Each of the mentioned form methods calls the relevant method in the class. This is to reduce the amount of code placed on the form and allow the code to be reused on multiple forms. We added the following methods to the class:

    • canMove() checks whether the currently selected node can be dragged. Although there might be more conditions, for this demonstration, we only disallow dragging of top nodes.
    • move() is where the actual movement of the budget model is performed, i.e. submodel is assigned with another parent.
    • stateDropHilite() is responsible for highlighting and removing highlight from relevant items. Using stateDropHilited(), we highlight the current item and we remove highlight from the previously highlighted one. This ensures that as we move the dragged item over the tree, items are highlighted once the dragged item is over them and the highlight is removed once dragged item leaves them. This method is called later from several places to make sure node highlighting works correctly.
    • beginDrag() saves the item currently being dragged into a variable.
    • dragOver() first checks if the currently selected item can be moved. If not, then it returns FormDrag::None, which changes the mouse cursor to the forbidden sign. Otherwise, the cursor is changed to an icon representing node movement. This method also calls stateDropHilite() to ensure correct node highlighting.
    • drop() also checks if the item being dropped can be moved. If yes, then it uses move() to update the data and moveItem() to visually change the node's place in the tree. It also calls stateDropHilite() to update tree node highlighting.

    >> Continue Reading Working with Forms in Dynamics AX: Part 3

     

    [ 1 | 2 | 3 ]

     

    If you have read this article you may be interested to view :

    Microsoft Dynamics AX 2009 Development Cookbook Solve real-world Microsoft Dynamics AX development problems with over 60 simple but incredibly effective recipes with this book and eBook
    Published: December 2009
    eBook Price: $29.99
    Book Price: $49.99
    See more
    Select your format and quantity:

    About the Author :


    Mindaugas Pocius

    Mindaugas Pocius is currently a freelance Dynamics AX technical and functional consultant and trainer at DynamicsLab Limited (http://www.dynamicslab.com). The company specializes in providing development, consulting, and training services for Microsoft Dynamics AX resellers and customers.

    Mindaugas started his IT consultant career back in 2001 while still in his Information Technology Master Studies at a Lithuanian University. Since then he has become a recognized Microsoft Certified Professional for Dynamics AX in most of the major areas: Development, Configuration & Installation, Financials, Trade & Logistics and Project Management. He is also a Certified Microsoft Trainer for Dynamics AX and has delivered Dynamics AX Development, Finance and other training across the world. From 2001 to 2012, Mindaugas participated in numerous Dynamics AX implementations, ranging from small local to large international projects reaching an 800 strong user base. He has held a wide range of development, consulting, and leading roles while always maintaining a significant role as a business application developer.

    Books From Packt

    Ext JS 3.0 Cookbook
    Ext JS 3.0 Cookbook

    Joomla! 1.5 Development Cookbook
    Joomla! 1.5 Development Cookbook

    WordPress 2.7 Cookbook
    WordPress 2.7 Cookbook

    WordPress 2.8 Themes Cookbook
    WordPress 2.8 Themes Cookbook

    Microsoft Dynamics AX 2009 Programming: Getting Started
    Microsoft Dynamics AX 2009 Programming: Getting Started

    Programming Microsoft Dynamics NAV 2009
    Programming Microsoft Dynamics NAV 2009

    Microsoft Office Live Small Business: Beginner’s Guide
    Microsoft Office Live Small Business: Beginner’s Guide

    Quality Assurance for Dynamics AX-Based ERP Solutions
    Quality Assurance for Dynamics AX-Based ERP Solutions

    Your rating: None Average: 1 (1 vote)

    Post new comment

    CAPTCHA
    This question is for testing whether you are a human visitor and to prevent automated spam submissions.
    F
    h
    4
    2
    E
    H
    Enter the code without spaces and pay attention to upper/lower case.
    Code Download and Errata
    Packt Anytime, Anywhere
    Register Books
    Print Upgrades
    eBook Downloads
    Video Support
    Contact Us
    Awards Voting Nominations Previous Winners
    Judges Open Source CMS Hall Of Fame CMS Most Promising Open Source Project Open Source E-Commerce Applications Open Source JavaScript Library Open Source Graphics Software
    Resources
    Open Source CMS Hall Of Fame CMS Most Promising Open Source Project Open Source E-Commerce Applications Open Source JavaScript Library Open Source Graphics Software