Pages

Thursday, March 29, 2012

LINQ to Text Files

LINQing it

If you ever need to find something specific inside a text file, I'd advise you to go with LINQ.
Below is a very useful extension method to work with Text Files' content with LINQ:

static class Extensions
{
    public static IEnumerable<string> Lines(this StreamReader reader)
    {
        string line;
        
        while (!string.IsNullOrEmpty(line = reader.ReadLine()))
        {
            yield return line;
        }
    }
}

So with the above extension method in context, you could simply do things like this:


using (var sr = new StreamReader(myPath))
{
    var results = from line in sr.Lines()
                  where line.StartsWith("Foo") && line.Contains("this text")
                  select line;
}

This makes life a little easier.

Tuesday, February 7, 2012

Dynamics AX Custom Lookup

As a Dynamics AX developer, you'll often have to perform custom lookups. Meaning that the user may only select records in another table depending on some condition on the form, or elsewhere.


There are two ways of creating custom lookups. I'll show the easiest here, and maybe show the other way in the future.

So if you're thinking you'll have to create a new form and all that, forget it, you won't. All you have to do is write some pieces of code.



First let's create our lookup method directly on the table. You can check other tables for these special lookup methods. They appear in lots of tables. Take for example the table ContactPerson. It has the following custom lookup methods by default:

  • lookupActionContactPerson
  • lookupCustContactPerson
  • lookupDirContactPerson
  • lookupDirContactPersionId
  • lookupVendContactPerson



Now, to create a custom lookup, we'll use the class SysTableLookup as it provides us a set of methods to do so.


Here's our method, to be created directly on the table DummyTable, with some dummy data and names.

public client static void lookupDummyTable(FormStringControl _ctrl,
                                           MyFilterEDT _filter)
{
  
    // Intantiantes a SysTableLookup object telling it which control activated the lookup, and
    // what table should be displayed inside the lookup form.
    SysTableLookup sysTableLookup = SysTableLookup::newParameters(tablenum(DummyTable), _ctrl);

    Query query = new Query(); // This query will be used by the lookup form.
    QueryBuildDataSource qbds;

    ;

    // The "addLookupField" method adds the the fields that will be shown inside the lookup's grid. 
    // The second, boolean parameter indicates that this field will be returned from the lookup when a
    // record is selected. In this case, we'll return our DummyPK.
    sysTableLookup.addLookupField(fieldnum(DummyTable, DummyPK), true); 
    sysTableLookup.addLookupField(fieldnum(DummyTable, Field1));
    sysTableLookup.addLookupField(fieldnum(DummyTable, Field2));
    sysTableLookup.addLookupField(fieldnum(DummyTable, Field3));


    // Using our dummy table as a DataSource Table.
    qbds = query.addDataSource(tablenum(DummyTable));

    // This is the key part, what we want to filter to be displayed in the grid.
    qbds.addRange(fieldnum(DummyTable, MyFilterColumn)).value(_filter);


    // Passes the query to the sysTableLookup.
    sysTableLookup.parmQuery(query);

    // Activates the lookup.
    sysTableLookup.performFormLookup();
}



With that little piece of code, Dynamics AX will already display a lookup form with the table you've specified as a DataSource, displaying the fields you specified with the method addLookupField and following the results criteria of the Query you've passed to it.



Now all we have to do is actually call our lookup method, by overriding the lookup of the correct field in our Form's Data Source:


public void lookup(FormControl _formControl, str _filterStr)
{
    ;

    DummyTable::lookupDummyTable(_formControl, "SomeFilter");
}


And that's it! The lookup will be performed. But there's one additional step we'll have to take here...

When the user tries to select our DummyTable reference, Dynamics AX will display the results of the query, based on the filter we've created. So the user will only be able to see what we want him to see, and so he'll only be able to select what we want. The problem is that if he types in an existing record for the DummyTable in the control, even if does not get shown in our lookup, Dynamics AX will accept that. Remember: we're only limiting what the user may see and select for that field, but we're not limiting what he can actually type in the control. So if we don't validate it somehow, Dynamics AX will run the standard validation, which will only check if a record exists for that EDT and so on. To avoid that the user does in fact type in something we don't want, we'll have to override the validate method for our field in our Data Source. The simplest logic is to check if the DummyTable record the user typed in does attend the filter we've specified:


public boolean validate()
{
    DummyTable    dummyTable;

    ;

    dummyTable = DummyTable::find(currentRecord.DummyTableRefField);

    return dummyTable.MyFilterColumn == "SomeFilter";
    
}



So if the user simply types in for a record that does not attend our filter, Dynamics AX won't let the user save that value.



This is the easiest way to effectively implement a custom lookup and validate the user input in case he doesn't select the record from the lookup. For this example I used a literal filter, a string. But in most cases, you'll have to filter according to what the user selected for another field in your form. Things should usually be dynamic.

Hope this helps...

Thursday, February 2, 2012

Refresh DataSource and retain position

Update: Also read "How to effectively refresh data on forms".

Refresh a DataSource is a very common task to Dynamics AX developers, as you'll most likely perform changes on a record and have to present it to the user, on a form.


The most commonly used method to refresh the DataSource to make a form show what you have changed on a record is the research method.
The only problem is that the research method doesn't retain the record that was selected, it positions the DataSource on the first record of the result, causing the user to see another record. To resolve this matter, the research method, only in Dynamics AX 2009, has a boolean parameter, retainPosition. Now if you call it by passing true to it, it will sure retain the position of the selected record after the refresh, right? Wrong...


This should only work when your form has DataSources linked by Inner Joins in it, what means that the generated query has Inner Joins in it. Having any other link types or temporary tables causes it to not retain the position, even if you pass true when calling it.

So I'll present you two ways of retaining the position of the selected record, one that works sometimes, and one that always works, although you should always try and see if it works with research(true) before trying anything else.


On the first way, you could simply get the position of the DataSource before calling research(), like this:

int position;

    ;

    position = myTable_ds.getPosition();
    myTable_ds.research();
    myTable_ds.setPosition(position);

But does this work perfectly? Not again...

The code above should select the record that was on the previously selected record's position. What does that mean? It means that if your query result changes the record's orders, you won't have the correct record selected. This could easily happen if your form handles the records' order in a special way, or if the action is performed when the record or any records are inserted, which will certainly cause the the form to reorder the records when you tell it to refresh the DataSource.


The second way to do it will actually look for the correct record and then select it, so you can imagine it will perform a little worse than the others, but at least it will never fail (unless you, somehow, delete the record you're looking for in the meantime).


Simply copy the RecId from the record you want to keep selected to a new table buffer, like this:


MyTable      myTableHolder;
    
    ;

    myTableHolder.RecId = myTableRecord.RecId;
    
    myTable_ds.research(); // or: myTableRecord.dataSource().research();

    myTable_ds.findRecord(myTableHolder); // or: myTableRecord.dataSource.findRecord(myTableHolder);     

We copy if to a new table buffer because the findRecord method expects a parameter of type Common. If you stored the RecId in a variable, you would have to pass it to a table buffer anyway...


And that's it, it'll first refresh the DataSource, and then look for your record and select it, so the screen might even "blink" for a second. As I said, this is not the best when thinking about performance, but at least it works...

Friday, December 23, 2011

Post or Validate Invent Journal

When you want to post an Invent Journal through code, you can use the class InventJournalCheckPost, like this:



static void Job1(Args _args)
{

    InventJournalCheckPost      inventJournalCheckPost;
    InventJournalTable          inventJournalTable;
    ;


    inventJournalTable = InventJournalTable::find("MyJournalID");

    
    inventJournalCheckPost = InventJournalCheckPost::newPostJournal(inventJournalTable);
    inventJournalCheckPost.parmThrowCheckFailed(true);

    try
    {

        inventJournalCheckPost.run();
    }
    catch
    {
        info("An error has occurred, journal not posted.");
    }
}


When setting parmThrowCheckFailed to true, when the journal fails to be posted by any reason, Dynamics AX will throw an error with the error message. If you don't, it will simply show the error message on the infolog, but, technically, you wont know wether it posted successfully or not, so it depends on what you're trying to do. This is specially useful when you're working with transactions and depend on the journal being posted successfully to continue with your code or to abort everything.



If you're simply trying to Validate (Check) an Invent Journal, all you have to do is change which constructor you'll use for the class InventJournalCheckPost, like this:


static void Job1(Args _args)
{

    InventJournalCheckPost      inventJournalCheckPost;
    InventJournalTable          inventJournalTable;
    ;


    inventJournalTable = InventJournalTable::find("MyJournalID");

    // Constructor to just check the journal:
    inventJournalCheckPost = InventJournalCheckPost::newJournalCheckPost(JournalCheckPostType::Check, inventJournalTable);
    inventJournalCheckPost.parmThrowCheckFailed(true);

    try
    {

        inventJournalCheckPost.run();
    }
    catch
    {
        info("The journal contains errors.");
    }
}


And all the other classes for Journals go with the same base, so for example, for a Production Journal, the correct class to perform checking or posting is ProdJournalCheckPost. That is because all of them inherit from JournalCheckPost or JournalCheckPostLedger (which inherits from JournalCheckPost).


So that's it, that's how you post or check a journal with code.


Wednesday, November 16, 2011

ASP.NET 4.0 has not been registered on the Web server.


ASP.NET 4.0 has not been registered on the Web server.
You need to manually configure your Web server for ASP.NET 4.0 in order for your site to run correctly.



If you ever come across this warning message when creating a new Web Application or configuring your Web Site to be hosted directly on IIS instead of Visual Studio's Development Server, simply run the regiis util, inside the correct Framework's version installation folder, with Administrator privileges, like this:




That should solve it.

I'm not sure but I think this happens when you install the .NET Framework before you install IIS. If it happens with other versions of the .NET Framework, you should simply switch its folder to the correct version.



Here's the text:
cd C:\Windows\Microsoft.NET\Framework\v4.0.30319
aspnet_regiis -i

Wednesday, July 6, 2011

Handling null values with bound RadioButtonList

Sometimes when you're binding a RadioButtonList and there's a null value in one of the records, you can end up getting an exception thrown. The problem here is that it tries to select a ListItem that has the same value as the record retrieved from the database, in this case, null.

There are plenty of workarounds for this issue, including changing your query to don't retrieve nulls, or using a COALESCE instead, for example. In this post, I'll show a simple trick to handle this.


Binding to nothing

As I said, the big secret is to have a ListItem with the same value retrieved from the query. If your retrieved value is NULL, your ObjectDataSource will convert it to an empty string automatically, so you'd only need to have a ListItem with an empty string marked to be its value, like the following:

<asp:RadioButtonList runat="server" ID="radioButtonList1" SelectedValue='<%# Bind("NullableColumn") %>'>
    <asp:ListItem Text="MyEmptyStringOption" Value=""/>
    <asp:ListItem Text="MyOption1" Value="1" />
    <asp:ListItem Text="MyOption2" Value="2"/>
    <asp:ListItem Text="MyOption3" Value="3"/>                    
</asp:RadioButtonList>


By doing that, when bound, the selected option will appear as the EmptyStringOption, so we have already prevented that Exception from happening. But what would we write to the empty string option text? "None"? It makes no sense having an option that says None on the screen, right?

So let's hide it. That way, we'll always have at least one option selected, even if the return value is null, but the user will only see the options with an actual value, making it look like the RadioButtonList has no selected option at all.

If you check ListItem's attributes, you'll see that it does not have a Visible attribute, so we're going to hide it through CSS. Even if it had a Visible attribute, if we'd set it to "False", the ListItem wouldn't even be rendered, causing the exception to return to happen, as we wouldn't have an option with an empty string value.


To hide it, simply add the style attribute to it, like this:

<asp:ListItem Text="MyEmptyStringOption" Value="" style="display: none;" />


But now you'll see something else. Even if not instantly, the compiler might show you a warning message, telling you that element ListItem does not have a 'style' attribute even though it works perfectly fine. If that warning message annoys you enough to make you want to get rid of it like it does to me, remove the style attribute from the ListItem, and add it programmatically on the Page's Load event, like this:

protected void Page_Load(object sender, EventArgs e)
{
    // First find the ListItem in question, and then add the style attribute to it
    MyRadioButtonList.Items.FindByValue("").Attributes.Add("style", "display: none");
}


This will have the same effect, and it will not show you that warning...

Monday, June 20, 2011

Implementing a ModalPopupExtender

It should be easy, but as I've encountered some problems along the way, I thought I'd best blog about it.


AjaxToolkit has an extender to make it easier for us to implement a modal dialog (or a Popup) for our ASP.NET Web Forms, the ModalPopupExtender. It could be used for confirmations, when a user choses to delete something, for example. I'll demonstrate a simple Popup with two options, and two buttons.


From now on I'll assume you have AjaxToolkit controls added to your toolbox. If you don't, you can go ahead and find out how to do it here.


Poping it up


After you've added AjaxToolkit to your toolbox, add a new WebSite to your solution, and open the default page code.

First of all, let us define our Popup's style:

.ModalPopupBackground
{
    background-color: Gray;
    filter: alpha(opacity=70);
    opacity: 0.7;
}

.PopupBody
{
    border: 1px solid silver;
    padding: 5px;
    background-color: #f5f5c5;
    text-align: left;
    font-size: 15px;
    color: Gray;
}        

This will add a Gray background when the Popup is active, and our PopupBody class will be defined to be our Panel's Css Class, which will be shown inside our Popup.

We also have to register our AjaxToolkit DLL on the page, and add a ScriptManager, like this:

<%@ Register Assembly="AjaxControlToolkit" Namespace="AjaxControlToolkit" TagPrefix="ajaxToolkit" %>

<ajaxToolkit:ToolkitScriptManager runat="server" ID="manager1" />



Now we can add our controls to the page. Add a button, and a label to interact with the Popup, and our ModalPopupExtender. Just remember that these controls must be inside a tag form with runat="server".


<asp:UpdatePanel runat="server">
        <ContentTemplate>
            <center>
                <asp:Button Text="Click Me" ID="btnClickMe" runat="server" />
                <br />
                <asp:Label ID="lblResult" Visible="false" Text="" runat="server" />
                <ajaxToolkit:ModalPopupExtender ID="modalPopup" runat="server" TargetControlID="btnClickMe"
                    PopupControlID="pnlPopup" OkControlID="btnHidden" CancelControlID="btnHidden"
                    BackgroundCssClass="ModalPopupBackground" DropShadow="true" />
            </center>
        </ContentTemplate>
    </asp:UpdatePanel>

I don't think I need to explain the ModalPopupExtender's properties. They're very straight-forward, and you can also find their explanation at the ASP.NET Ajax page.


One thing to note here is that we've set the OkControlID and CancelControlID to a dummy, hidden button. We do that because if we set them to the right controls, no PostBack is fired, so no code-behind is executed. It would be OK for the Cancel button though, but as I was developing this example, when I had the CancelControlID property set to the actual cancel button, after I clicked it once I had to reload the page for the Popup to be shown again. So instead, I put some JavaScript code on the Cancel's button OnClientClick event to hide the Popup. This was the simplest workaround I could think of.


And now this is our panel, which will be displayed as the Popup:

<asp:Panel runat="server" ID="pnlPopup" CssClass="PopupBody">
        <asp:RadioButtonList ID="radioList" runat="server" >
            <asp:ListItem Text="Option 1" Value="1" />
            <asp:ListItem Text="Option 2" Value="2" />
        </asp:RadioButtonList>
        
        <br />
        <center>
            <asp:Button Text="dummy" ID="btnHidden" runat="server" Style="display: none;" />
            <asp:Button Text="Ok" ID="btnOk" runat="server" style="padding-right: 10px;" 
                onclick="btnOk_Click" />
            <asp:Button Text="Cancel" ID="btnCancel" runat="server" OnClientClick="$find('modalPopup').hide();" />            
        </center>
    </asp:Panel>

As I said, the cancel button has some JavaScript to hide the Popup when it's clicked.

You can also notice that our dummy button doesn't have its visible property set to false, instead we've hidden it through style marks. We did that because if we set it to not be visible through the Visible property, it won't even be rendered, so the Popup won't find it, and won't be shown. But hidding it through style marks does the trick, and makes it work just fine. So if your Popup is not being shown, check if your dummy button has Visible = false and change it to style marks, as display: none.


To complete the trick, we add some code to handle our button OK click event:

protected void btnOk_Click(object sender, EventArgs e)
{
    lblResult.Text = string.Format("You have selected option {0}", radioList.SelectedValue);
    lblResult.Visible = true;
}


So when the Popup is shown and the Ok button is clicked, a PostBack is generated and the Click event handler code is executed and the Popup is hid. And when the Cancel button is clicked, it does nothing but hide the Popup. If you would add some code-behind to it, it would also be executed though.


And that's it, you should now have a modal Popup with two Radio Buttons as options, an OK and a Cancel button, like the images below. Once again, hope this helps...