
{"id":193,"date":"2017-04-12T18:40:14","date_gmt":"2017-04-12T08:40:14","guid":{"rendered":"http:\/\/bakke.online\/?p=193"},"modified":"2017-04-12T18:40:14","modified_gmt":"2017-04-12T08:40:14","slug":"how-to-tree-lookups-in-dynamics-ax-reference-group-controls-part-2","status":"publish","type":"post","link":"https:\/\/www.bakke.online\/index.php\/2017\/04\/12\/how-to-tree-lookups-in-dynamics-ax-reference-group-controls-part-2\/","title":{"rendered":"How-to: Tree lookups in Dynamics AX reference group controls, part 2"},"content":{"rendered":"<p>This is something that keeps coming up on different projects, so I&#8217;d like to share how I usually do this. \u00a0There are some examples out there which try to describe a very generic solution which can be used in almost any case. \u00a0However, such solutions very quickly become <strong>very<\/strong> complex. \u00a0To keep things simple, I have tried to keep this example fairly specific but adaptable to a wide range of circumstances. \u00a0Specifically this example is using a fixed hierarchy structure with a fixed number of levels, each of which will be represented by a different table.<\/p>\n<p><a href=\"https:\/\/www.bakke.online\/index.php\/2017\/04\/11\/how-to-tree-lookups-in-dynamics-ax-reference-group-controls-part-1\/\">Last time<\/a> I introduced the data model that will sit behind the tree lookup. \u00a0This time I&#8217;ll show how to build the tree on demand to improve performance, and then\u00a0how to build the actual tree lookup form.<\/p>\n<p><!--more--><\/p>\n<h3>Building the tree<\/h3>\n<p>We&#8217;ll start at the top of the tree, the EB_Libraries table. \u00a0Add a new static method called buildTree.<\/p>\n<pre><span style=\"color: #0000ff;\"><strong>public static void<\/strong><\/span> buildTree( FormTreeControl _tree, ImageListAppl _imageListAppl )\n{\n  EB_Libraries libraries;\n  EB_Aisles aisles;\n\n<span style=\"color: #0000ff;\"><strong>  while select<\/strong><\/span> Name, RecId\n          <span style=\"color: #0000ff;\"><strong>from<\/strong><\/span> libraries\n         <span style=\"color: #0000ff;\"><strong>order by<\/strong><\/span> Name\n         <span style=\"color: #0000ff;\"><strong>group by<\/strong><\/span> Name, RecId\n         <span style=\"color: #0000ff;\"><strong>outer join count<\/strong><\/span>( RecId )\n          <span style=\"color: #0000ff;\"><strong>from<\/strong><\/span> aisles\n         <span style=\"color: #0000ff;\"><strong>where<\/strong><\/span> aisles.LibraryRecId == libraries.RecId\n  {\n    _tree.addItem( <span style=\"color: #ff0000;\"><strong>0<\/strong><\/span>, <span style=\"color: #ff0000;\"><strong>0<\/strong><\/span>, <span style=\"color: #0000ff;\"><strong>new<\/strong><\/span> FormTreeItem( libraries.Name, _imageListAppl.image( <span style=\"color: #ff0000;\"><strong>12039<\/strong><\/span> ), int642int( aisles.RecId ), libraries.RecId ) );\n  }\n}<\/pre>\n<p>Starting at the top, we take the tree control we&#8217;re taking the tree control we&#8217;re going to populate and an image list which contains the icons we can use. \u00a0I&#8217;ve chosen some icons that belong to the same icon family just to achieve a consistent look and feel, but choose whichever icons work best for you.<\/p>\n<pre><span style=\"color: #0000ff;\"><strong>public static void<\/strong><\/span> buildTree( FormTreeControl _tree, ImageListAppl _imageListAppl )<\/pre>\n<p>Next is the query to get the name and record ID of all libraries. \u00a0We also outer join to a count of EB_Aisles records related to each library. \u00a0 Why do we need to do that join? \u00a0Simple, we&#8217;re going to delay loading of the lower levels until the relevant branch is expanded in the tree control. \u00a0If we&#8217;re to be able to provide the user with a [+] indicator\u00a0to expand, we&#8217;ll need to know if there will be any child records.<\/p>\n<pre><span style=\"color: #0000ff;\"><strong>while select<\/strong><\/span> Name, RecId\n        <span style=\"color: #0000ff;\"><strong>from<\/strong><\/span> libraries\n<span style=\"color: #0000ff;\"><strong>       order by<\/strong><\/span> Name\n<span style=\"color: #0000ff;\"><strong>       group by<\/strong><\/span> Name, RecId\n<span style=\"color: #0000ff;\"><strong>       outer join count<\/strong><\/span>( RecId )\n        <span style=\"color: #0000ff;\"><strong>from<\/strong><\/span> aisles\n<span style=\"color: #0000ff;\"><strong>       where<\/strong><\/span> aisles.LibraryRecId == libraries.RecId<\/pre>\n<p>Then, inside the loop, we&#8217;ll create FormTreeItem objects for each library and insert at the root level of the tree (the first zero), at the end of the items list (the second zero), with a particular icon, number of aisles in the library and finally we&#8217;re stuffing the library&#8217;s RecId field into the data property of the item.<\/p>\n<pre>_tree.addItem( <span style=\"color: #ff0000;\"><strong>0<\/strong><\/span>, <span style=\"color: #ff0000;\"><strong>0<\/strong><\/span>, <span style=\"color: #0000ff;\"><strong>new<\/strong><\/span> FormTreeItem( libraries.Name, _imageListAppl.image( <span style=\"color: #ff0000;\"><strong>12039<\/strong><\/span> ), int642int( aisles.RecId ), libraries.RecId ) );<\/pre>\n<p>Now repeat the same process for the EB_Aisles table. \u00a0The purpose of the code is the same as for libraries, the only real difference being that we will accept a TreeItemIdx parameter for the library item we&#8217;re going to add to, as well as\u00a0the record ID of the library. \u00a0You&#8217;ll see these being used in the addItem() call and the query, respectively.<\/p>\n<pre><strong><span style=\"color: #0000ff;\">public static void<\/span><\/strong> buildTree( FormTreeControl _tree, ImageListAppl _imageListAppl, TreeItemIdx _parentIdx, EB_LibraryRefRecId _libraryRecId )\n{\n  EB_Aisles aisles;\n  EB_Racks racks;\n\n<span style=\"color: #0000ff;\"><strong>  while select<\/strong><\/span> Name, RecId\n          <span style=\"color: #0000ff;\"><strong>from<\/strong><\/span> aisles\n         <span style=\"color: #0000ff;\"><strong>order by<\/strong><\/span> Name\n         <span style=\"color: #0000ff;\"><strong>group by<\/strong><\/span> Name, RecId\n         <span style=\"color: #0000ff;\"><strong>where<\/strong><\/span> aisles.LibraryRecId == _libraryRecId\n         <span style=\"color: #0000ff;\"><strong>outer join count<\/strong><\/span>( RecId )\n          <span style=\"color: #0000ff;\"><strong>from<\/strong><\/span> racks\n         <span style=\"color: #0000ff;\"><strong>where<\/strong><\/span> racks.AisleRecId == aisles.RecId\n  {\n    _tree.addItem( _parentIdx, <span style=\"color: #ff0000;\"><strong>0<\/strong><\/span>, <span style=\"color: #0000ff;\"><strong>new<\/strong><\/span> FormTreeItem( aisles.Name, _imageListAppl.image( <span style=\"color: #ff0000;\"><strong>12040<\/strong><\/span> ), int642int( racks.RecId ), aisles.RecId ) );\n  }\n}<\/pre>\n<p>And for EB_Racks, which works in exactly the same way.<\/p>\n<pre><span style=\"color: #0000ff;\"><strong>public static void<\/strong><\/span> buildTree( FormTreeControl _tree, ImageListAppl _imageListAppl, TreeItemIdx _parentIdx, EB_AisleRefRecId _aisleRecId )\n{\n  EB_Racks racks;\n  EB_Shelves shelves;\n\n<span style=\"color: #0000ff;\"><strong>  while select<\/strong><\/span> Name, RecId\n          <span style=\"color: #0000ff;\"><strong>from<\/strong><\/span> racks\n         <span style=\"color: #0000ff;\"><strong>order by<\/strong><\/span> Name\n         <span style=\"color: #0000ff;\"><strong>group by<\/strong><\/span> Name, RecId\n         <span style=\"color: #0000ff;\"><strong>where<\/strong><\/span> racks.AisleRecId == _aisleRecId\n         <span style=\"color: #0000ff;\"><strong>outer join count<\/strong><\/span>( RecId )\n          <span style=\"color: #0000ff;\"><strong>from<\/strong><\/span> shelves\n         <span style=\"color: #0000ff;\"><strong>where<\/strong><\/span> shelves.RackRecId == racks.RecId\n  {\n    _tree.addItem( _parentIdx, <span style=\"color: #ff0000;\"><strong>0<\/strong><\/span>, <span style=\"color: #0000ff;\"><strong>new<\/strong><\/span> FormTreeItem( racks.Name, _imageListAppl.image( <span style=\"color: #ff0000;\"><strong>12041<\/strong><\/span> ), int642int( shelves.RecId ), racks.RecId ) );\n  }\n}<\/pre>\n<p>The buildTree() method for EB_Shelves is simpler, as there are no children below this level. \u00a0Thus, we don&#8217;t need the outer join and group by this time, and we&#8217;ll just pass a zero to the addItem() call&#8217;s children parameter.<\/p>\n<pre><span style=\"color: #0000ff;\"><strong>public static void<\/strong><\/span> buildTree( FormTreeControl _tree, ImageListAppl _imageListAppl, TreeItemIdx _parentIdx, EB_RackRefRecId _rackRecId )\n{\n EB_Shelves shelves;\n\n<span style=\"color: #0000ff;\"><strong>while select<\/strong><\/span> Name, RecId\n <span style=\"color: #0000ff;\"><strong>from<\/strong><\/span> shelves\n <span style=\"color: #0000ff;\"><strong>order by<\/strong><\/span> Name\n <span style=\"color: #0000ff;\"><strong>where<\/strong><\/span> shelves.RackRecId == _rackRecId\n {\n _tree.addItem( _parentIdx, <span style=\"color: #ff0000;\"><strong>0<\/strong><\/span>, <span style=\"color: #0000ff;\"><strong>new<\/strong><\/span> FormTreeItem( shelves.Name, _imageListAppl.image( <span style=\"color: #ff0000;\"><strong>12047<\/strong><\/span> ), <span style=\"color: #ff0000;\"><strong>0<\/strong><\/span>, shelves.RecId ) );\n }\n}<\/pre>\n<h3>The lookup form<\/h3>\n<p>Next, create a new form EB_ShelfLookup and set its design properties as shown here:<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"alignnone size-full wp-image-194\" src=\"https:\/\/www.bakke.online\/wp-content\/uploads\/2017\/04\/lookup_design.png\" alt=\"\" width=\"256\" height=\"137\" \/><\/p>\n<p>To the design, add a Tree control and set width\/height to be column width\/height:<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"alignnone size-full wp-image-195\" src=\"https:\/\/www.bakke.online\/wp-content\/uploads\/2017\/04\/tree_properties.png\" alt=\"\" width=\"244\" height=\"69\" \/><\/p>\n<p>Let&#8217;s start adding supporting members and methods to the form. \u00a0First we need an ImageListAppl object to store the icons for the tree control. \u00a0As it will be used from different places around the form we&#8217;ll add it to the form&#8217;s class declaration.<\/p>\n<pre><span style=\"color: #0000ff;\"><strong>public class<\/strong><\/span> FormRun <span style=\"color: #0000ff;\"><strong>extends<\/strong> <\/span>ObjectRun\n{\n  ImageListAppl imageListAppl;\n}<\/pre>\n<p>Override the init() method:<\/p>\n<pre><span style=\"color: #0000ff;\"><strong>public void<\/strong><\/span> init()\n{\n<span style=\"color: #0000ff;\"><strong>  super<\/strong><\/span>();\n\n  imageListAppl = <span style=\"color: #0000ff;\"><strong>new<\/strong> <\/span>ImageListAppl( <span style=\"color: #ff0000;\"><strong>32<\/strong><\/span>, <span style=\"color: #ff0000;\"><strong>32<\/strong> <\/span>);\n  imageListAppl.add( <span style=\"color: #ff0000;\"><strong>12039<\/strong> <\/span>);\n  imageListAppl.add( <span style=\"color: #ff0000;\"><strong>12040<\/strong> <\/span>);\n  imageListAppl.add( <span style=\"color: #ff0000;\"><strong>12041<\/strong> <\/span>);\n  imageListAppl.add( <span style=\"color: #ff0000;\"><strong>12047<\/strong> <\/span>);\n\n\u00a0 EB_Libraries::buildTree( Tree, imageListAppl );\n\n  this.buildTree();\n}<\/pre>\n<p>This creates a new ImageListAppl object, setting the icon size to 32 by 32 pixels. \u00a0It then adds the IDs of the 4 icons we will use to represent the different levels of the tree.<\/p>\n<p>Next, it calls buildTree() to build the initial state of the tree. \u00a0As we&#8217;re doing delayed loading, this will actually only load the libraries level. \u00a0We&#8217;ll take care of loading the rest of the information on demand, and we&#8217;ll do that by overriding the expanding() method on the tree control:<\/p>\n<pre><span style=\"color: #0000ff;\"><strong>public boolean<\/strong><\/span> expanding( <span style=\"color: #0000ff;\"><strong>int<\/strong> <\/span>_idx, FormTreeExpand _action, <span style=\"color: #0000ff;\"><strong>anytype<\/strong> <\/span>_data )\n{\n  <span style=\"color: #0000ff;\"><strong>boolean<\/strong>      <\/span>ret;\n  FormTreeItem expandingItem = Tree.getItem( _idx );\n  <span style=\"color: #0000ff;\"><strong>int<\/strong>          <\/span>depth   = <span style=\"color: #ff0000;\"><strong>0<\/strong><\/span>;\n  <span style=\"color: #0000ff;\"><strong>int<\/strong>          <\/span>nextIdx = _idx;\n\n  <span style=\"color: #0000ff;\"><strong>if<\/strong><\/span>( !expandingItem.stateExpandedOnce() )\n  {\n    <span style=\"color: #008000;\"><em>\/\/ Find which depth level of the tree we're on, by moving up the parent() relationship until we hit the root.<\/em><\/span>\n    <span style=\"color: #0000ff;\"><strong>while<\/strong><\/span>( nextIdx != <span style=\"color: #ff0000;\"><strong>0<\/strong><\/span> )\n    {\n      depth++;\n      nextIdx = Tree.getParent( nextIdx );\n    }\n\u00a0\n<span style=\"color: #008000;\"><em> \/\/ Load the child records based on the expanding index and the data() property of the item.<\/em><\/span>\n    <span style=\"color: #0000ff;\"><strong>switch<\/strong><\/span>( depth )\n    {\n      <span style=\"color: #0000ff;\"><strong>case<\/strong> <span style=\"color: #ff0000;\"><strong>1<\/strong><\/span><\/span>:\n        EB_Aisles::buildTree( Tree, imageListAppl, _idx, <span style=\"color: #0000ff;\"><strong>any2int64<\/strong><\/span>( _data ) );\n        <span style=\"color: #0000ff;\"><strong>break<\/strong><\/span>;\n\n      <span style=\"color: #0000ff;\"><strong>case<\/strong> <span style=\"color: #ff0000;\"><strong>2<\/strong><\/span><\/span>:\n        EB_Racks::buildTree( Tree, imageListAppl, _idx, <span style=\"color: #0000ff;\"><strong>any2int64<\/strong><\/span>( _data ) );\n        <span style=\"color: #0000ff;\"><strong>break<\/strong><\/span>;\n\n      <span style=\"color: #0000ff;\"><strong>case<\/strong> <span style=\"color: #ff0000;\"><strong>3<\/strong><\/span><\/span>:\n        EB_Shelves::buildTree( Tree, imageListAppl, _idx, <span style=\"color: #0000ff;\"><strong>any2int64<\/strong><\/span>( _data ) );\n        <span style=\"color: #0000ff;\"><strong>break<\/strong><\/span>;\n    }\n  }\n\n  <span style=\"color: #0000ff;\"><strong>return super<\/strong><\/span>( _Idx, _action, _data );\n}<\/pre>\n<h3>Returning a value<\/h3>\n<p>Returning a value to a reference group control is different from returning to a string. \u00a0A reference group control needs a record buffer rather than a string. \u00a0As a consequence we can&#8217;t use closeSelect() to return the value, but have to call closeSelectRecord() instead. \u00a0Let&#8217;s add a new method to the form:<\/p>\n<pre><span style=\"color: #0000ff;\"><strong>public void<\/strong><\/span> selectAndClose()\n{\n  <span style=\"color: #0000ff;\"><strong>int<\/strong><\/span>          idx  = Tree.getSelection();\n  FormTreeItem item = Tree.getItem( idx );\n  EB_Shelves   shelves;\n\n  <span style=\"color: #0000ff;\"><strong>select firstOnly<\/strong><\/span> shelves\n   <span style=\"color: #0000ff;\"><strong>where<\/strong><\/span> shelves.RecId == item.data();\n\n  element.closeSelectRecord( shelves );\n}<\/pre>\n<p>The tree control does not work well in a lookup without a bit more work. \u00a0We&#8217;ll want to be able to select a value by hitting Enter or by double-clicking on an item, so we need to override task(). \u00a0Overriding task() is getting a hook into the event loop for the form. \u00a0Hitting the Enter key causes AX to call this method with a _taskId code of 288.<\/p>\n<pre><span style=\"color: #0000ff;\"><strong>public int<\/strong><\/span> task( <span style=\"color: #0000ff;\"><strong>int<\/strong> <\/span>_taskId )\n{\n  <span style=\"color: #0000ff;\"><strong>int<\/strong> <\/span>ret = <span style=\"color: #0000ff;\"><strong>super<\/strong><\/span>( _taskId );\n\n<span style=\"color: #0000ff;\"><strong>  if<\/strong><\/span>( _taskId == <span style=\"color: #ff0000;\"><strong>288<\/strong> <\/span>)\n  {\n    element.selectAndClose();\n  }\n  <span style=\"color: #0000ff;\"><strong>return<\/strong> <\/span>ret;\n}<\/pre>\n<p>If we get a _taskId of 288, we&#8217;ll call selectAndClose().<\/p>\n<p>To handle the double-click, override mouseDblClick() on the tree control.<\/p>\n<pre><span style=\"color: #0000ff;\"><strong>public int<\/strong><\/span> mouseDblClick( <span style=\"color: #0000ff;\"><strong>int<\/strong> <\/span>_x, <span style=\"color: #0000ff;\"><strong>int<\/strong> <\/span>_y, <span style=\"color: #0000ff;\"><strong>int<\/strong> <\/span>_button, <span style=\"color: #0000ff;\"><strong>boolean<\/strong> <\/span>_ctrl, <span style=\"color: #0000ff;\"><strong>boolean<\/strong> <\/span>_shift )\n{\n  <span style=\"color: #0000ff;\"><strong>int<\/strong> <\/span>ret = <span style=\"color: #0000ff;\"><strong>super<\/strong><\/span>( _x, _y, _button, _ctrl, _shift );\n\n  element.selectAndClose();\n\n<span style=\"color: #0000ff;\"><strong>  return<\/strong> <\/span>ret;\n}<\/pre>\n<p>Simple and easy, we just call super() and then selectAndClose().<\/p>\n<p>There&#8217;s one more thing to do: \u00a0We&#8217;ll need to change the behaviour of mouse clicks. \u00a0When clicking in the tree control, the standard behaviour is to process the event and then pass the click up to the parent control for further processing. \u00a0(The event bubbles up) \u00a0When that event reaches the form, the standard behaviour of a lookup form is to close when something is clicked.<\/p>\n<p>To prevent that we&#8217;ll need to override the mouseUp() method on the tree control to return a 1. \u00a0This indicates to AX that we have handled the click and we do not want the event to bubble up.<\/p>\n<pre><span style=\"color: #0000ff;\"><strong>public int<\/strong><\/span> mouseUp( <span style=\"color: #0000ff;\"><strong>int<\/strong> <\/span>_x, <span style=\"color: #0000ff;\"><strong>int<\/strong> <\/span>_y, <span style=\"color: #0000ff;\"><strong>int<\/strong> <\/span>_button, <span style=\"color: #0000ff;\"><strong>boolean<\/strong> <\/span>_ctrl, <span style=\"color: #0000ff;\"><strong>boolean<\/strong> <\/span>_shift )\n{\n  <span style=\"color: #0000ff;\"><strong>super<\/strong><\/span>( _x, _y, _button, _ctrl, _shift );\n\n  <span style=\"color: #0000ff;\"><strong>return<\/strong> <\/span><span style=\"color: #ff0000;\"><strong>1<\/strong><\/span>; <span style=\"color: #008000;\"><em>\/\/ Prevent the lookup form from closing on single click.<\/em><\/span>\n}<\/pre>\n<p>Last thing to do on the form for now is to add a data source. \u00a0We will not actually\u00a0<strong>use<\/strong> the data source, but AX requires a lookup form to have a data source of the type that we are looking up in. \u00a0In our case, we&#8217;re looking up in the EB_Shelves table, so add that as a datasource, with properties as shown here:<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"alignnone size-full wp-image-198\" src=\"https:\/\/www.bakke.online\/wp-content\/uploads\/2017\/04\/eb_shelves_properties.png\" alt=\"\" width=\"248\" height=\"85\" \/><\/p>\n<h3>Update to the EB_ShelfRefRecId data type<\/h3>\n<p>In order for AX to use our custom lookup form by default, we need to set the FormHelp property on the EB_ShelfRefRecId data type to point to EB_ShelfLookup.<\/p>\n<p><a href=\"https:\/\/www.bakke.online\/wp-content\/uploads\/2017\/04\/eb_shelfrefrecid_formhelp.png\"><img loading=\"lazy\" decoding=\"async\" class=\"alignnone size-full wp-image-208\" src=\"https:\/\/www.bakke.online\/wp-content\/uploads\/2017\/04\/eb_shelfrefrecid_formhelp.png\" alt=\"\" width=\"267\" height=\"86\" \/><\/a><\/p>\n<h3>Back to the EB_Books form<\/h3>\n<p>Now go back to and bring up the context menu on\u00a0the EB_Books form. \u00a0Select &#8220;Restore&#8221; and &#8220;Compile&#8221;. \u00a0This should make sure that the form will pick up and use the new lookup form. \u00a0I have seen that AX does not consistently pick up such changes without a restore and recompile, so I have made it a habit to always do so.<\/p>\n<p>Open the form and it will look similar to before. \u00a0That is to be expected, as we haven&#8217;t made any changes to the form itself, yet. \u00a0However, when you create a new record and click the drop-down (Isn&#8217;t that odd, that a drop\u00a0<strong>down\u00a0<\/strong>is actually a look\u00a0<strong>up&#8230;<\/strong>?) there will be some changes.<\/p>\n<p><a href=\"https:\/\/www.bakke.online\/wp-content\/uploads\/2017\/04\/new_lookup.png\"><img loading=\"lazy\" decoding=\"async\" class=\"alignnone size-full wp-image-201\" src=\"https:\/\/www.bakke.online\/wp-content\/uploads\/2017\/04\/new_lookup.png\" alt=\"\" width=\"478\" height=\"324\" \/><\/a><\/p>\n<p>That&#8217;s cool&#8230; \u00a0Now try to expand some of the items, all the way down to a shelf.<\/p>\n<p><a href=\"https:\/\/www.bakke.online\/wp-content\/uploads\/2017\/04\/new_lookup_expanded.png\"><img loading=\"lazy\" decoding=\"async\" class=\"alignnone size-full wp-image-202\" src=\"https:\/\/www.bakke.online\/wp-content\/uploads\/2017\/04\/new_lookup_expanded.png\" alt=\"\" width=\"441\" height=\"514\" \/><\/a><\/p>\n<p>Oh wow&#8230; This is really starting to look promising.<\/p>\n<p>Select a shelf, either by hitting Enter or by double-clicking.<\/p>\n<p><a href=\"https:\/\/www.bakke.online\/wp-content\/uploads\/2017\/04\/books_populated.png\"><img loading=\"lazy\" decoding=\"async\" class=\"alignnone size-full wp-image-203\" src=\"https:\/\/www.bakke.online\/wp-content\/uploads\/2017\/04\/books_populated.png\" alt=\"\" width=\"255\" height=\"137\" \/><\/a><\/p>\n<p>Nice, AX has filled in the reference fields for us. \u00a0It did show some warning messages about missing joined cursors which are caused by the lookup only returning a single record whereas the replacement key contains fields from multiple tables. \u00a0Don&#8217;t worry, we&#8217;ll fix that in the next part.<\/p>\n<h3>Wrapping up<\/h3>\n<p>The lookup form can be improved in a couple of areas to make it more user friendly, and we&#8217;ll need a better way to represent the selected shelf. \u00a0 And that&#8217;s what I&#8217;ll show\u00a0<a href=\"https:\/\/www.bakke.online\/index.php\/2017\/05\/02\/how-to-tree-lookups-in-dynamics-ax-reference-group-controls-part-3\/\">next time<\/a>.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>This is something that keeps coming up on different projects, so I&#8217;d like to share how I usually do this. \u00a0There are some examples out there which try to describe a very generic solution which can be used in almost any case. \u00a0However, such solutions very quickly become very complex. \u00a0To keep things simple, I &hellip; <a href=\"https:\/\/www.bakke.online\/index.php\/2017\/04\/12\/how-to-tree-lookups-in-dynamics-ax-reference-group-controls-part-2\/\" class=\"more-link\">Continue reading<span class=\"screen-reader-text\"> &#8220;How-to: Tree lookups in Dynamics AX reference group controls, part 2&#8221;<\/span><\/a><\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[4,1],"tags":[2,3,5],"class_list":["post-193","post","type-post","status-publish","format-standard","hentry","category-dynamics-ax","category-uncategorized","tag-ax2012r3","tag-dynamics-ax","tag-user-interface"],"_links":{"self":[{"href":"https:\/\/www.bakke.online\/index.php\/wp-json\/wp\/v2\/posts\/193","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.bakke.online\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.bakke.online\/index.php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.bakke.online\/index.php\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.bakke.online\/index.php\/wp-json\/wp\/v2\/comments?post=193"}],"version-history":[{"count":0,"href":"https:\/\/www.bakke.online\/index.php\/wp-json\/wp\/v2\/posts\/193\/revisions"}],"wp:attachment":[{"href":"https:\/\/www.bakke.online\/index.php\/wp-json\/wp\/v2\/media?parent=193"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.bakke.online\/index.php\/wp-json\/wp\/v2\/categories?post=193"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.bakke.online\/index.php\/wp-json\/wp\/v2\/tags?post=193"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}