Telerik – RadControls or more appropriately BadControls…

24. March 2009
1331911801_6f960ea238_o

 

I’ve been a user of the Telerik RadControls for ASP.Net, RadControls for Winforms, and Reporting tools for the last few months. I’ve been using them since Q2 2008 to the latest release of Q3 2008.

In the last few months I can say this in no simpler terms that if you use any of the RadControls expect to be extremely irritated with the flat uselessness of these controls in any practical application scenario. Add a few multitudes of annoyance when you begin building composite control applications using RadControls as they do not interact well in abundance. I especially found the absolute worst case to be with the Telerik Reporting that is just not suitable for any reporting scenario inside of a corporate environment, let alone any business environment. Perhaps the reporting tools would be good for play toys by some stay at home moms to make flyers for the umpteen number of fundraisers that may happen in a child's life.

I don’t say this lightly. I’ve garnered my fair share of ‘Telerik’ points, the points awarded for discovering (don’t let me make that sound like a challenge) problems or quirks that otherwise do not function as expected when using Telerik controls. The worse of all is the ease at which I've found these problems countered with the sheer number of man hours spent determining whether it was my own doing (because of course I wouldn’t expect such a highly regarded toolset as being so shoddy), but of course speaking with the Telerik’s support confirms my suspision that the tools just that bad. Don’t get me wrong Telerik’s support is fantastic for providing points quickly and effectively, and for the occasional gotty work-around to the problem that shouldn’t be there.

Telerik Reporting – Why you shouldn’t use it.

I’ll start with a simple example. If a developer were to use the Telerik Reporting don’t expect the reports to support out-of-process session state. This may not be a problem for some company/personal sites because of the lack for scalable websites, but becomes a deal breaker for any self respecting web architecture.

Why do they not support out-of-process?

Because of some serialization/deserialization problems that cause the Telerik reports to throw a nice reflection error on page rendering. The error itself isn’t a page level error but an error caught only by the Global.asax’s Application_Error event. The great thing about the exception is when a custom error page is used on the site that same custom error page will display within the report viewer where one would expect their report to be generated. The error itself is something very generic, if you handle the Server.GetLastError() in the Global.asax file, the description is something like “A list corresponding to the selected DataMember was not found.” Thanks captain obvious error I really can pin that down quickly.

I will give them this they made reports class objects instead of something like the typed XML format of RDLC, which leads way to more dynamic instantiation of custom report classes, but at the same time they’re not much in the current form.

Another funny thing about the RadReports is the fact that each report can be bound to a DataSet, but the report itself will only handle ONE DataTable from within the DataSet. At least RDLC you can just add a new panel/table and bind to X number of DataTables from within the bound DataSet datasource. Telerik on the other hand requires that you create a sub-report that you then bind to another DataSet, and again only one DataTable from that DataSet is allowed to be used to bind controls to. This becomes incredibly annoying when trying to create a simple order report with a ‘header data’ table, and an itemized ‘detail data’ table inside one report. Instead you have to create a master report for the header table, a sub-report for the itemized data, and mix in the lack of support for out-of-process its massive headache.

There is a work-around, not great, but i’ll touch on it here. After having bound the DataSet to the report, bound the textboxes/report controls, remove the DataMember property of the report settings, and then dynamically handle the on-demand datasource event of the report. The problem you get into in that situation is you then have to do the same for all the sub-reports by going through the same process, and then handling the data binding for the sub-report within the demand datasource of the master report. The end result is twice the data retrieval calls necessary to create a report that could be handled by support multiple DataTable bindings from a single bound DataSet to a report.

For any scenario used for generating business reports, reports with drill-through, or multi-table application just do yourself a favor and skip right over Telerik Reporting. Don’t believe me? Well check out the Telerik Reporting ‘forums/documentation’ on-line and find one sample or one thread talking about mulit-datasource reports. Find one that implements something other than a SqlDataSource page level datasource. Go ahead… i’ll wait. Back already?

Anyway, supposedly Telerik says the out-of-process problem will be resolved in Q1 2009 release, but we’ll see. It didn’t do us a lot of good at the time since it took about six days bouncing back and forth with support (gap over the weekend), creating test projects for them, and to finally discover they just don’t support out-of-process session data. When all was said and done we ditched Telerik Reporting for RDLC. RDLC may not be pretty, but at least it’s dependable.

Next Post – ASP.Net BadControls! Stay tuned…

I think I'll conclude part 1 of my Telerik experience post. I’ll follow up with my infamous experience using RadControls for ASP.Net! I can’t wait to share more about the RadMenu that touts templated items, multi-column menu, but none of that is supported with load-on-demand from web services. I’ll talk about the attributes that don’t carry over on RadMenu, the RadTreeView viewstate client disconnected bombs, RadSplitters looking like what President Obama would say ‘Special Olypmic's’ in non-IE browsers, and much much more.

PS. Needless to say if you want to save yourself time try Infragistics, been a few years since i last used them, but at least they weren’t as bad as Telerik has been. It could be just me, but i doubt it. Hopefully Q1 2009 brings much needed improvement.

ASP.Net, Third Party Controls, Programming

Windows Server 2008 Mail Server using hMailServer

16. March 2009

scr_installation_big I’ve been toying with Windows Server 2008 for a little while now, nothing major, just hosting a few domains, and tweaking to get some experience with the latest server offerings. As such I’ve been really looking into getting an email server setup on Windows Server 2008. The problem is my budget doesn’t extend to the niceties of Exchange Server, or the likes of MailEnable. So after some snooping around i came across hMailServer that came with a somewhat decent support structure, decent documentation, and best of all free mail server software.

hMailServer allows for multiple domains, seemingly no limit on domain creation, multiple domain user accounts, and even a somewhat decent scripting system for automated maintenance. The only limitation on storage size I can see quickly would probably come from the particular database management system being used. The choices are between MS SQL, MySQL, or PostgreSQL.

After signing into the local hMailServer administration tool you can begin configuring your SMTP settings, setup domains, setup user accounts, configure mail rules, schedule backups, and in general configure your small or enterprise level hMailServer setup. Below you can see a glimpse of the hMailServer Administrator dialog in action.

scr_hmailadmin_iprange_big

Programs & Utilities

cXML and ASP.NET

12. March 2009

What is cXML? Commerce XML referred to as cXML is actually a protocol created by Ariba to carry out interactions between procurement companies to supply something of an updated EDI process to better support e-commerce systems of suppliers. That of course was the idea in 1999 when it was created and (un)fortunately it has been used up until the present to conduct business to business interactions through a PunchOut request/response process.

I have been (un)fortunate enough to be tapped to lead the integration of a cXML based PunchOut system for a client I've been working with in order to integrate into something called ACES. ACES is a e-commerce solution by a company called American Solutions for Business. While during the initial conversations were centered about a messaging mechanism to request and respond with messages I was not initially familiar with the cXML standard. I was at least with its older competitor EDI. So this began my quest to better understand, design, and develop an support system on the client system for implementing the protocol.

I began initially with scouring the forums, Google, Live.com, and other assorted search engines for exactly what others were doing to integrate cXML into ASP.Net systems. That is when I discovered that aside from www.cXML.org there is actually very little material on the subject at all let alone regarding using it with ASP.NET in any fashion. Besides bizTalk and a few forums about validating a received cXML document against the cXML DTD available from cXML.org the protocol is a ghost.

To top the fact that the protocol is essentially never mentioned except in a few out of publishing books available on Amazon.com on B2B based messaging architectures even the Ariba website lacks any samples available for ASP.NET developers. I was excited on further searching to find a link to the classic ASP example project available on the site but my enthusiasm was shortly lived. All links to the contents were to a page that informs the user that the content has been expired from the site permanently.

So now what. Well thankfully after digging through umpteen number of websites I managed to scarf up a few PDF files of cXML solutions guides, and the cXML version 1.2 that the procurement company current users. After reading through the documents one can see quickly how reliant cXML is to the protocol standard enforcing company Ariba. The protocol relies heavily on a Ariba supplier ID and (Dun & Bradstreet) DUN numbers available from www.dnb.com.

Within the solution guide I also managed to round up the only ASP related material showing snippets of messaging decoding performed with classic ASP. It was at least enough to get me started building out my cXML support framework which essentially consists of multiple synchronous XML GET/POST to continue the interaction. By doing so an outside site can direct users from the outside customers site to display your supplier site inline the users browser experience to configure the product/service as the user would like. The request of course is posted to your domain, handled by the request object to receive the XML input stream, cXML request is parsed, and then a response posted back to the calling page from a URL identified via the response cXML document. Based on identifying whether a shared key is correct the cXML request can be processed, and the user redirected to begin product configuration seamlessly believing they’re interacting with the external site never realizing they’re working on a external supplier site.

I guess on the plus side I could qualify myself as a cXML knowledgeable company capable of being sourced to integrate the standard in other company e-commerce solutions. I don’t think I'll throw my hat in that ring just yet it’s been a big pain to integrate this standard into a modern ASP.NET based e-commerce system considering that the only documentation of the protocol was updated on classic ASP I assume that this standard was meant to be let alone for modern systems. Of course compared to EDI it’s night and day more robust.

When my project is complete I'll take the time to post a good how-to on implementing the request response mechanism in a modern web language. Heaven knows I'd have appreciated finding one myself when i started to learn this system!

ASP.Net, Programming, .Net

ASP.Net 3.5 ListView Control – Using DataPager

2. March 2009

I was working on a new project recently and unlike many of the older projects i’ve been working on this new project was built on the .Net 3.5 framework. Meaning now, should i choose, i could use LINQ, or any other number of ‘new’ features available to me. I decided to start with a few basic updates to the project the first of which was to add a ‘paging’ ability to a data listing that previously had a screen limitation of 22 items max per group otherwise the items would not display in the visible screen area.

So the first thing that I decided to do was check out the ASP.Net 3.5 ListView control. For the longest time to create any number of data lists I've used a conjunction of data-bound readers with template fields or DataGrid lists. Going in using the ListView i was sold on two points, the first is the ability to create both a layout template, and the ability to great a group item template. For added icing there was also the ability to assign a DataPager to the ListView right out of the box, what fun!

What I ideally wanted to create was a data listing that had a previous, next, first, and last buttons on both the top and bottom of the data list. Along with this i wanted to be able to display the current row position, and have the movement controls show/hide based on the current index without having to implement my own data paging logic. Trust me i’ve had to create my own paging logic way more times than i can count, and have plenty of ‘code snippets’ to paste into place to provide it. The major draw to ListView was the data pager, and template support.

After looking around for a way to create the pager style i was looking for i came back highly dissatisfied. All i found was the same out-of-box Microsoft snippets displayed on the umpteen number of blog sites with wordy diatribes that only sufficed to say the same verbiage found on the Microsoft site with a few ‘neat’ and ‘look at what i did’ comments. So needless to say i had to pretty much just default to the plug and play method to make out what i wanted.

So after a number of hours reading, prodding, and poking here is what I came up with:

<asp:ListView ID="lvCartUsers" ItemPlaceholderID="itemPlaceholder" runat="server"
            OnPreRender="lvCartUsers_PreRender">
            <LayoutTemplate>
                <div class="cartUserNavControls">
                    <asp:DataPager runat="server" ID="dpProductTopPager" PagedControlID="lvCartUsers"
                        PageSize="5">
                        <Fields>
                            <asp:TemplatePagerField OnPagerCommand="cartUsers_OnPagerCommand">
                                <PagerTemplate>
                                    <div class="navMoveFirst">
                                        <asp:ImageButton CommandName="First" ID="ibFirst" Visible='<%# (Math.Ceiling(System.Convert.ToDouble(Container.TotalRowCount) / Container.PageSize) > 1) && (((Container.StartRowIndex / Container.PageSize) + 1) > 1) %>'
                                            ImageUrl='<%# GetFirstImagePath() %>' runat="server" />
                                    </div>
                                    <div class="navMovePrevious">
                                        <asp:ImageButton CommandName="Previous" ID="ibPrevious" Visible='<%# (Math.Ceiling(System.Convert.ToDouble(Container.TotalRowCount) / Container.PageSize) > 1) && (((Container.StartRowIndex / Container.PageSize) + 1) > 1) %>'
                                            ImageUrl='<%# GetPreviousImagePath() %>' runat="server" />
                                    </div>
                                    <div class="navMoveLast">
                                        <asp:ImageButton CommandName="Last" ID="ibLast" Visible='<%# (Math.Ceiling(System.Convert.ToDouble(Container.TotalRowCount) / Container.PageSize) > 1) && ((Container.StartRowIndex + Container.PageSize) < Container.TotalRowCount) %>'
                                            ImageUrl='<%# GetLastImagePath() %>' runat="server" />
                                    </div>
                                    <div class="navMoveNext">
                                        <asp:ImageButton CommandName="Next" ID="ibNext" Visible='<%# (Math.Ceiling(System.Convert.ToDouble(Container.TotalRowCount) / Container.PageSize) > 1) && ((Container.StartRowIndex + Container.PageSize) < Container.TotalRowCount) %>'
                                            ImageUrl='<%# GetNextImagePath() %>' runat="server" />
                                    </div>
                                    <div class="navPagerCount">
                                        Displaying Records
                                        <asp:Label runat="server" ID="CurrentPageLabel" Text="<%# Container.StartRowIndex %>" />
                                        -
                                        <asp:Label runat="server" ID="TotalPagesLabel" Text="<%# Container.StartRowIndex+Container.PageSize %>" />
                                        ( of
                                        <asp:Label runat="server" ID="TotalItemsLabel" Text="<%# Container.TotalRowCount%>" />
                                        records.)
                                    </div>
                                </PagerTemplate>
                            </asp:TemplatePagerField>
                        </Fields>
                    </asp:DataPager>
                </div>
                <div id="cartGrid">
                    <div id="cartListHeader">
                        <div id="headerFistName">
                            <asp:Literal ID="lblFirstHeader" runat="server" Text="First" />
                        </div>
                        <div id="headerLastName">
                            <asp:Literal ID="lblLastHeader" runat="server" Text="Last" />
                        </div>
                        <div id="headerCartNumber">
                            <asp:Literal ID="lblCartNumberHeader" runat="server" Text="Cart #" />
                        </div>
                        <div id="headerCartStatus">
                            <asp:Literal ID="lblCartStatusHeader" runat="server" Text="Status" />
                        </div>
                    </div>
                    <div runat="server" id="itemPlaceHolder">
                    </div>
                </div>
                <br style="clear: both;" />
                <div class="cartUserNavControls">
                    <asp:DataPager runat="server" ID="dpProductBottomPager" PagedControlID="lvCartUsers"
                        PageSize="5">
                        <Fields>
                            <asp:TemplatePagerField OnPagerCommand="cartUsers_OnPagerCommand">
                                <PagerTemplate>
                                    <div class="navMoveFirst">
                                        <asp:ImageButton CommandName="First" ID="ibFirst" Visible='<%# (Math.Ceiling(System.Convert.ToDouble(Container.TotalRowCount) / Container.PageSize) > 1) && (((Container.StartRowIndex / Container.PageSize) + 1) > 1) %>'
                                            ImageUrl='<%# GetFirstImagePath() %>' runat="server" />
                                    </div>
                                    <div class="navMovePrevious">
                                        <asp:ImageButton CommandName="Previous" ID="ibPrevious" Visible='<%# (Math.Ceiling(System.Convert.ToDouble(Container.TotalRowCount) / Container.PageSize) > 1) && (((Container.StartRowIndex / Container.PageSize) + 1) > 1) %>'
                                            ImageUrl='<%# GetPreviousImagePath() %>' runat="server" />
                                    </div>
                                    <div class="navMoveLast">
                                        <asp:ImageButton CommandName="Last" ID="ibLast" Visible='<%# (Math.Ceiling(System.Convert.ToDouble(Container.TotalRowCount) / Container.PageSize) > 1) && ((Container.StartRowIndex + Container.PageSize) < Container.TotalRowCount) %>'
                                            ImageUrl='<%# GetLastImagePath() %>' runat="server" />
                                    </div>
                                    <div class="navMoveNext">
                                        <asp:ImageButton CommandName="Next" ID="ibNext" Visible='<%# (Math.Ceiling(System.Convert.ToDouble(Container.TotalRowCount) / Container.PageSize) > 1) && ((Container.StartRowIndex + Container.PageSize) < Container.TotalRowCount) %>'
                                            ImageUrl='<%# GetNextImagePath() %>' runat="server" />
                                    </div>
                                </PagerTemplate>
                            </asp:TemplatePagerField>
                        </Fields>
                    </asp:DataPager>
                </div>
            </LayoutTemplate>
            <EmptyDataTemplate>
                <asp:Label ID="lblNoItems" runat="server" />
            </EmptyDataTemplate>
            <ItemTemplate>
                <div class="cartListRow">
                    <div class="rowFistName">
                        <asp:Literal ID="lblFirstName" runat="server" Text='<%# Eval("FirstName") %>' />
                    </div>
                    <div class="rowLastName">
                        <asp:Literal ID="lblLastName" runat="server" Text='<%# Eval("LastName") %>' />
                    </div>
                    <div class="rowCartNumber">
                        <a class="cartListRowCartNumber" href='<%# Request.Path + "?CartId=" + Eval("CartId") %>'>
                            <%# Eval("CartId") %></a>
                    </div>
                    <div class="rowCartStatus">
                        <asp:Literal ID="lblStatus" runat="server" Text='<%# Eval("Status") %>' />
                    </div>
                </div>
            </ItemTemplate>
        </asp:ListView>

Code-behind:

public partial class CartUserList : System.Web.UI.UserControl
    {
        #region Properties
 
        private int _pageSize = 5;
 
        public int PageSize
        {
            get { return _pageSize; }
            set { _pageSize = value; }
        }
 
        private DataSet CartDataSet
        {
            get
            {
                DataSet dsCarts = null;
 
                if (ViewState["DataSet"] != null)
                {
                    dsCarts = (DataSet)ViewState["DataSet"];
                }
 
                return dsCarts;
            }
            set { ViewState["DataSet"] = value; }
        }
 
        #endregion // </Properties>
 
        #region Events
 
        protected void Page_Load(object sender, EventArgs e)
        {
            try
            {
                if (!IsPostBack)
                {
                    <Call RebindCartList here>
                }
            }
            catch (Exception ex)
            {
            }
        }
 
        protected void cartUsers_OnPagerCommand(object sender, DataPagerCommandEventArgs e)
        {
            try
            {
                int position = e.Item.Pager.StartRowIndex + e.Item.Pager.PageSize;
 
                switch (e.CommandName)
                {
                    case "First":
                        e.NewStartRowIndex = 0;
                        e.NewMaximumRows = e.Item.Pager.MaximumRows;
                        break;
                    case "Last":
                        e.NewStartRowIndex = (e.TotalRowCount - e.Item.Pager.PageSize);
                        e.NewMaximumRows = e.Item.Pager.MaximumRows;
                        break;
                    case "Next":
                        if (position <= e.TotalRowCount)
                        {
                            e.NewStartRowIndex = position;
                            e.NewMaximumRows = e.Item.Pager.MaximumRows;
                        }
                        break;
                    case "Previous":
                        e.NewStartRowIndex = e.Item.Pager.StartRowIndex - e.Item.Pager.PageSize;
                        e.NewMaximumRows = e.Item.Pager.MaximumRows;
                        break;
                }
            }
            catch (Exception ex)
            {
            }
        }
 
        protected void lvCartUsers_PreRender(object sender, EventArgs e)
        {
            if (IsPostBack)
            {
                    <Call RebindCartList here>
            }
        }
 
        #endregion //</Events>
 
        #region Functions
 
        private void RebindCartList(int customerID, int userID)
        {
            try
            {
                DataTable tblCarts = null;
 
                if (CartDataSet == null)
                {
                    CartDataSet = <BLL get DataSet>
 
                    if (CartDataSet.Tables.Count > 0)
                    {
                        tblCarts = CartDataSet.Tables[0];
                    }
                }
 
                lvCartUsers.DataSource = tblCarts;
                lvCartUsers.DataBind();
            }
            catch (Exception ex)
            {
            }
        }
 
        #endregion //</Functions>

The first thing you’ll see is the LayoutTemplate. This is the core to the ListView control as the entire structure of the ‘list’ is formatted within this template area. If one was building a HTML table the beginning tag for <table> and end tag </table> would be contained within this template. Unlike many other controls there is no header template and footer template instead what represents the header and footer of the template is defined within the LayoutTemplate. However, the actual data being repeated, ie the body of the data, is defined in the ItemTemplate.

Since I wanted the DataPager to appear above and below the list of data I have to create the two new DataPager controls above and below my ItemTemplate area so that they again appear above and below the data being paged. In order to get the look that I was going for I also had to implement my own TemplatePagerFields with the PagerTemplate set to display my custom ASP.Net ImageButtons with the selected first, next, previous, and last images to display instead of text. This is another benefit to using the TemplatePagerFields is that instead of having to use the built in text or button movement controls I can create my own by simply providing my own ASP.Net ImageButton controls with a custom CommandName. By using a custom command name I can just handle the event for OnPagerCommand to set the pagers index position based on the control clicked by the user. The code-behind snippet will show the OnPagerCommand being used to page the data. The thing to note here, as I saw in many blogs about paging problems, is that after a paging action is performed the data has to be rebound. It seems like out of the box the control should hold a temporary cache/session data when paging, but it doesn’t. That’s why on my example i handle the OnPreRender of the ListView control to re-bind the data when IsPostBack.

Getting back to the ItemTemplate that is sandwiched between the two DataPager controls. Within the ItemTemplate the bound data can be formatted as this will be the template repeated for each row of data found within the DataSource used to bind with the ListView. Here is where using Eval() the fields can be formatted for display applying any HTML elements or ASP.Net controls within the ItemTemplate area.

So from our perspective the layout looks much like this:

<LayoutTemplate>
<DataPager />
<ItemTemplate>
</ItemTemplate>
<DataPager/>
</LayoutTemplate>

The best part is that by setting the PagedControlID of the DataPager controls to the ListView control I can simply add a few functions to check whether to display the paging controls based on the current PagerSize of the DataPager and I no longer need to handle calculating when to display the controls on the code-behind. You can see the functions for this on the Visible property of the DataPager’s PagerTemplate controls.

All in all not bad. The control is easy enough to use, but when searching for how to use the ListView i did come across many blogs where users couldn’t figure out why after a paging action occurred the ListView was empty. Easy enough, when it posts back the user still is responsible for rebinding the control with the data source. If a SqlDataSource control is used then a user can take advantage of the caching ability of the SqlDataSource otherwise on the post back the ListView must be re-bound with data. Keep that in mind and this is one sweet control.

There is one thing I've used but didn’t touch on and that's the GroupTemplate. The GroupTemplate sits within the LayoutTemplate where ItemTemplate would be displayed in my hierarchy above, but now the GroupTemplate itself contains a definition of the ItemTemplate within it. So what you’d get is the ability to span items, using <div> tags with float set, across columns, essentially creating a table arrangement across columns instead of just rows. Below is a rough hierarchy of how the GroupTemplate becomes involved. There is actually a great article here detailing how to use the ListView with a great snippet on setting up a GroupTemplate here http://msdn.microsoft.com/en-us/library/bb398790.aspx.

<LayoutTemplate>
<DataPager />
<GroupTemplate>
<ItemTemplate>
</ItemTemplate>
</GroupTemplate>
<DataPager/>
</LayoutTemplate>

For now I’ve just started using the control and I'm sure I'll find all kinds of nifty little tricks to perform this that and the other. Either way it goes I've found my new favorite control for quickly creating both grouped and lists of bound data in a way i can quickly put together using CSS to create quick lists of data that is both paging and dynamically syllable.

ASP.Net, Programming, .Net