(Ax 2012) How to change the site URL of your Enterprise Portal

My first post on this blog was the way to change the URL of your Enterprise Portal in Ax 2009.
(Ax 2009) Change URL of your EP

Now in 2012 this is made way easier.
You can just change it during the install.

(Ax2009) Validatewrite only called once fix in RU6

I already encountered the following problem:
I have an Axform with validations in the validatewrite, so when I try to save my record and I don’t get through the validation the first time I get the correct info.
But when I correct my data and I make an other validation mistake the validatewrite doesn’t get triggered a second time.

Finding no direct solution for this problem I used to call my validatewrite and insert manual instead of using the build in function of the Axform control.
But recently when I read the description of RU6 I saw the following KB.

KB number 2410532
The ValidateWrite method on the CustTable table is not called a second time in the Enterprise Portal website.

As it is situated under the kernel hotfixes I suppose it’s fixed for every situation now.
So if you ever encounter the same problem and stumble upon this blog, install RU6 and your problem will (hopefully) be fixed.

Bypass firewall and send files through Business Connector

If you work with a separate servers for AOS and IIS it is likely that the AOS is behind a firewall, which means you cannot access the file directory. When you generate files using X++ code this will most likely be saved on the AOS server were the user that made the request from the IIS server has no access to.

To bypass this restriction you can send the generated files through the business connector and construct the file again on the IIS server. In this example a generated PDF file is send through.

Ax client
Create a static method which returns a container containing the file.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public static client container getFile()
{
    BinData                     binData;
    FileIOPermission          ioPermission;
    container                   cFile;
    FileName                    generatedFilename;
    ;
    generatedFilename = BLGGeneratedFiles::getPDFFilename();
 
    ioPermission = new FileIOPermission(generatedFilename,"r");
    ioPermission.assert();
 
    binData = new BinData();
    binData.loadFile(generatedFilename);
    cFile = binData.getData();
 
    return cFile;
}

Create a custom BinData class which will be used in C#.

1
2
3
4
class BLGBinData
{
    BinData                 binData;
}
1
2
3
4
5
6
7
public static BLGBinData construct()
{
    BLGBinData blgBinData = new BLGBinData();
    ;
 
    return new BLGBinData();
}
1
2
3
4
public str base64Encode()
{
    return binData.base64Encode();
}
1
2
3
4
void new()
{   ;
    binData = new BinData();
}
1
2
3
4
5
6
7
8
public boolean saveFile(str _filename)
{
    boolean ret;
 
    ret = binData.saveFile(_filename);
 
    return ret;
}
1
2
3
4
public void setData(container _data)
{   ;
    binData.setData(_data);
}

Visual Studio
Call the X++ method to retrieve the container containing the file and initiate the custom BinData class with the file data.
Using the class you can put the binary file data into a byte array and write it to the page.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
IAxaptaContainerAdapter conPDFFile = ApplicationProxy.BLGGeneratedFiles.getFile(this.AxSession.AxaptaAdapter);
 
ApplicationProxy.BLGBinData binData = ApplicationProxy.BLGBinData.construct(this.AxSession.AxaptaAdapter);
binData.setData(conPDFFile);
String fileContent = binData.base64Encode();
byte[] fileBytes = Convert.FromBase64String(fileContent);
 
if (fileBytes.Length > 0)
{
	Response.Clear();
	Response.ContentType = "application/pdf";
	Response.BinaryWrite(fileBytes);
	Response.Flush();
	Response.End();
}
else
{
	lblInfo.Text = "NO DATA TO PRINT";
}

Other file types
To use other file types you can use other content types. If the file type you want to use is not a standard supported type you can add content types to associate file extensions to content types. If this is not associated with each other, the browser will not know which program to open for the file. For example if outlook .msg files are not supported you can add the MIME type.

This can be done in the IIS Manager. Go to properties of the website and on the HTTP Headers tab, select MIME Types…

Choose New…

Fill in the extension and content type and click OK.

For a list of common MIME types you consult the Wikipedia Internet media type page.

Technical conference 2011

Last week me and some colleague went to the technical conference in Nice.
For more post about other topics then EP, be sure to check about the technical conference Doens.be
In this post I want to mention some new Enterprise portal features that I saw.

In my post about the convergence in Atlanta I already mentioned some new features, see Convergence Atlante.

The What’s new in EP 2012 was similar to the one in Atlanta so new things I didn’t see there. The installation/deployment session was well interesting.

- Installation has been improved, more feedback if certain features or parameters are filled in wrong or missing during the installation.
- The AxUpdatePortal Command Line Utility is updated, e.g. you can specify which items you want to deploy, you can deploy everything, deploy all the webcontrols or deploy 1 specific webcontrol.
- Not longer needed to work on the iis server, but visual studio can just connect locally to the correct Aos.
- The search capabilities of Sharepoint are integrated with Ax Data. Set up the correct query’s, run through a wizard and the data will be indexed and then you have the ability to search for it.

In global I really like the new approach of working with modal forms.
We used to work with an overview page that contained a list and when we wanted to perform an action like e.g. creating a new record we navigated to an edit page that contained an axform.
Now you just pop up a modal form and you can stay in the same page.

A second thing I really like is the closer integration with C# in general. It’s now much easier to call managed code that can be a C# code file.

I can’t wait to start working in Ax2012 EP.

Persist selected jQuery tab after postback

When using the jQuery tab, I had the problem that when a postback occured the first tab would be selected instead of the tab that was selected. To solve this I’ve used a asp:HiddenField to store the selected index of the tab. This is filled in every time a tab is selected.

On the initialization of the tab I give the value of the HiddenField as a parameter to the “selected” option of the jQuery tab function.

Javascript code

1
2
3
4
5
6
7
8
9
10
11
12
Sys.Application.add_load(init);
 
function init() {    
    $(document).ready(function() {
        $('#tabs').tabs({
            select: function(event, ui) {
                $("#<%= hfSelectedTAB.ClientID %>").val(ui.index);
            }
        });
        $("#tabs").tabs("option", "selected", [$("#<%= hfSelectedTAB.ClientID %>").val()]);
   });
}

ASP Mark-up

1
<asp:HiddenField ID="hfSelectedTAB" runat="server"  Value="0"/>

Unable to acces values from AxForm

I got the following situation: an AxForm with a custom button under it.

1
2
3
4
5
6
7
8
9
10
11
12
13
<dynamics:AxForm ID="frmHRMApplicationBasket" runat="server" DataKeyNames="RecId"
                    DataMember="HRMApplicationBasket_Current" 
                    DataSourceID="dsHRMEPRecruitingJobApply"
                    DefaultMode="Insert" >            
                                <dynamics:AxGroup ID="grpSolicitation" runat="server"  AutoGenerateCaption="False">    
                                    <Fields> 
                                    <dynamics:AxBoundField DataField="name" DataSet="BLOGRecruitingJobApply" DataSetView="HRMApplicationBasket" runat="server">
                                                            </dynamics:AxBoundField>
                                    <dynamics:AxBoundField DataField="BirthDate" DataSet="BLOGRecruitingJobApply" DataSetView="HRMApplicationBasket" runat="server">
                                                            </dynamics:AxBoundField> 
 
...
<asp:Button runat="server" ID="btnOk" Width="100px" Text="BlogInsertRecord"  />
1
2
3
4
 void btnOk_Click(object sender, EventArgs e)
 {
            this.dsHRMEPRecruitingJobApply.GetDataSet().DataSetRun.
                            AxaptaObjectAdapter.Call("insertHRMApplicationBasket");

When I fired the click event of the button I was unable to acces the values of the AxBoundFields in my form.
When I tried to fetch e.g HRMApplicationBasket.name it would return nothing although it was filled in on my AxForm in the browser.

Being sure that it wasn’t the first time that I did this I could not see what I was doing wrong.
And then I decided to try my luck on Google and found the following blog post:
updateonpostback-property-of-axform.aspx

After reading this I added the updatepostback property on my AxForm and changed my AxBoundFields that they trigger a postback.

1
2
3
4
<dynamics:AxForm ID="frmHRMApplicationBasket" runat="server" DataKeyNames="RecId"
                    DataMember="HRMApplicationBasket_Current" 
                    DataSourceID="dsHRMEPRecruitingJobApply"
                    DefaultMode="Insert" UpdateOnPostBack="true">
1
2
<dynamics:AxBoundField DataField="name" DataSet="BLOGRecruitingJobApply" DataSetView="HRMApplicationBasket" runat="server" AutoPostBack="true">
                                                            </dynamics:AxBoundField>

That did the trick for me.

Convergence 2011: EP sessions

Last week I attended the 2011 convergence in Atlanta.
In this post I want to share some thoughts and new things I saw there about Enterprise portal.

I followed the following sessions concerning EP.
- Enterprise portal tips and tricks
- Collaboration, Communities & social networking with Microsoft Sharepoint 2010
- Simplifying acces to Information with portals in Microsoft Dynamics 2012
- Unlocking the Power of Media Plus Technology: Sharepoint Outside the Firewall

Social network session

The sentence that I found the most interesting was:
High employee engagement -> better business

Why should you implement this?

  • To know that other people are doing the same thing
  • Network with your colleagues
  • Improve the commitment of your employees
  • Staying connected
  • Capture unstructured information
  • Finding people expertise
  •  

    New features in the 2012 EP that I saw

  • Hierarchies added to the quick launch menu.
  • Quick filter added on top of a gridview
  • Multi select added to the gridview
  • Now possible to open your detail form as a modal form (popup)
  • Pluggable authentication added, you can allow external users on your portal trough form based authentication (e.g add users to a sql database instead of AD) or windows live authentication.
  • The possibility to deploy a list page to the EP and show that list page in the portal. Note here that the list page isn’t converted to a regular webcontrol but instead will be rendered in the portal and the connection isn’t provided by the business connector but through web services.


  • And much more.

    For me it is all new, I didn’t have the time yet to explore the 2012 EP.
    So I hope to explore and learn a lot the coming months and posting them here.

    During the convergence the 2012 beta also came out.
    For more info about that read the post about it on doens.be

    Change the culture of a webpart

    If you have users with different languages, for example a Dutch (nl-be) user and an English (en-gb) user, and you want each user to have it’s own language but have one culture which is used for every user (date format, number format, …), it is possible to set the culture info on the webpart itself.

    This however is only possible for “Dynamics User Control Web Part” which is available since Ax 2009 and not for the old “Web Form Web Part” which was used in Ax 4.0.

    To change the culture you only need to implement one line of code:

    1
    
    webpart.CultureInfo = new System.Globalization.CultureInfo("en-gb");

    Here is an example of an implementation:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    
    public partial class CustomerListGrid : System.Web.UI.UserControl
    {
        protected const string EDITCUSTOMER = "customeredit";
        protected const string DELETECUSTOMER = "customeractiondeleteselected";
     
        protected void Page_Init(object sender, EventArgs e)
        {
            AxBaseWebPart webpart = AxBaseWebPart.GetWebpart(this);
            if (webpart != null)
            {
                webpart.SetMenuItemProperties += new EventHandler<SetMenuItemPropertiesEventArgs>(webpart_SetToolbarMenuItemProperties);
                webpart.ActionMenuItemClicked += new EventHandler<ActionMenuItemEventArgs>(webpart_ActionMenuItemClicked);
                webpart.ActionMenuItemClicking += new EventHandler<ActionMenuItemClickingEventArgs>(webpart_ActionMenuItemClicking);
                //->DDBegin
                // override CultureInfo for this WebPart
                webpart.CultureInfo = new System.Globalization.CultureInfo("en-us");
                //<-DDEnd
            }
        }
        ...
    }

    Thanks to our colleague Yves De Jaeger for sending this in.

    Convergence 2011

    Convergence 2011

    As you may or may not know, April 10 – 13 is Microsoft Convergence 2011 in Atlanta, Georgia.
    The annual conference concerning everything Dynamics which Youri and me will be attending.

    During the convergence we will try to get as much info as possible regarding Ax and Enterprise Portal and will be sharing this info with you.

    If you are attending Convergence and would like to meet up you also can do this using the ‘Contact Us’ form, or request a meeting on the Convergence ‘Connect’ website. Our names are Youri De Brabandere and Christof Decraene.

    Use custom button in an Ax gridview

    A few days ago we got the following question:

    I have come across some trouble with the LinkButton in AxGridView.
    I have a LinkButton in the TemplateField in the AxGridView. I would like to ask if there is possibility that can trigger both PostBackURL and OnClick event?
    Or are there any ways that can Save the editing record and Then redirect to the next page of the respective record?
    Here is my situation:
    1) I have a axgridview in my user control, and inside, I used a templateField to contain the LinkButton.
    2) The LinkButton will bring the user to the “Line-level” gridview, which is in the next page. Currently, I achieved this by using “PostbackUrl = ‘<%#GetLineURL%>’” to bind the URL, but the onClick event is not fired.
    3) However, when the grid is still in Edit mode, the current record cannot be saved as the user clicks on the linkbutton.
    So, are there any solutions that can help me out?

    My solution or more a workaround is to use a regular button instead of a linkbutton so we have better control over our actions.
    In the clicked event of the button we can then perform all the actions we want in the order we choose.
    So in this case we can save the record and redirect the page.

    In the gridview we have a templatefield that contains a imagebutton. The gridview has an onrowdatabound, in this event we can set the command argument of the button so we can see on which record there has been clicked.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    
    <dynamics:AxGridView ID="AxGridView1" runat="server"
     onrowdatabound="AxGridView1_RowDataBound" AllowEdit="True">
     <Columns>
        <asp:TemplateField HeaderText="Redirect">
            <ItemTemplate>
                <asp:ImageButton ID="ImageButton1" runat="server" onclick="ImageButton1_Click" />
            </ItemTemplate>
        </asp:TemplateField>
      </Columns>
    </dynamics:AxGridView>

    In the row databound event we can fetch the button out of the templatefield and set the command argument with the needed data.
    So in the clicked event itself we can fetch the command argument and get the needed data to perform the actions.
    e.g. you can set the primary key fields as argument and fetch the entire record from ax, or just pass al the field you need. The choice is yours.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    
    protected void AxGridView1_RowDataBound(object sender, GridViewRowEventArgs e)
    {
        if (e.Row.RowType == DataControlRowType.DataRow)
        {
            ImageButton btn= (ImageButton)e.Row.FindControl("ImageButton1");
             //set commandArgument
            btn.CommandArgument = DataBinder.Eval(e.Row.DataItem, "Year") + ";" + DataBinder.Eval(e.Row.DataItem, "EmplId");
        }
    }
     
    protected void ImageButton1_Click(object sender, ImageClickEventArgs e)
    {
         //get parameters from the button to save the correct record of perform an other action
         ImageButton btn = (ImageButton)sender;
         string[] parms = new string[2];
         parms = btn.CommandArgument.Split(';');
     
        string sParam1 = parms[0];
        string sParam2 = parms[1];
     
        //execute code to save the record
        //redirect to different page
     }

    I hope this is an answer to the question.
    If you have any questions or subjects you want to see a blogpost about, consult the contact page and ask away.