|
Who Are We?Fluent Consulting is a software development and consulting firm that specializes in enterprise application integration, web applications, and software product development. We are a dedicated team focused on providing the highest level of quality and value for our clients. Please feel free to visit our corporate site or get in touch! |
Category Archive: Aspect-Oriented Components
MultiLineTextBoxValidator component released
Fluent.MultiLineTextBoxValidator is a validator control which will restrict the length of MultiLine TextBox controls. The MaxLength property of the .NET TextBox control does not have any effect if the TextMode property is set to MultiLine. This is because MultiLine TextBox controls are rendered in HTML as TEXTAREA tags, which cannot have a restricted length. This control adds Javascript support to achieve the length restriction. In addition it can provide feedback of the number of characters remaining before the MaxLength cutoff will be reached. Demo and download now!<%@ Register TagPrefix="fluent" Namespace="Fluent.MultiLineTextBoxValidator" Assembly="Fluent.MultiLineTextBoxValidator" %> Message:<br/> <asp:TextBox id="TestTextBox" Runat="server" Rows="10" Columns="50" TextMode="MultiLine" /> <br/> <fluent:MultiLineTextBoxValidator Runat="server" ControlToValidate="TestTextBox" MaxLength="10" OutputControl="TestTextBox2" ErrorMessage="Too long!" ShowJavascriptAlert="True" EnableClientSideRestriction="True" ShowCharacterCount="True" /> <br/><br/> <asp:TextBox id="TestTextBox2" Runat="server" /> characters remaining <br/><br/><br/> <asp:Button Runat="server" Text="Submit" />
ListTransfer Component Released
The ListTransfer Control simplifies the transfer of ListItems and is useful in the creation of double or mutliple listbox controls. With this component you can lay out your typical ListControls such as the ListBox, then just drag on the ListTransfer Control and wire it up. More details.<script runat="server"> void AddEmployees(object sender, EventArgs args){ ListTransferEmployees.CopySelected(); } void AddAllEmployees(object sender, EventArgs args){ ListTransferEmployees.CopyAll(); } void RemoveEmployees(object sender, EventArgs args){ ListTransferEmployees.RemoveSelected(); } void RemoveAllEmployees(object sender, EventArgs args){ ListTransferEmployees.RemoveAll(); } void MoveUp(object sender, EventArgs args){ ListTransferEmployees.MoveUpListControlTo(); } void MoveDown(object sender, EventArgs args){ ListTransferEmployees.MoveDownListControlTo(); } </script> ... <asp:ListBox ID="ListBoxEmployees" Runat="server" SelectionMode="Multiple" CssClass="listbox" /> ... <fluent:ListTransfer Runat="server" ID="ListTransferEmployees" ListControlTo="ListBoxProjectMembers" ListControlFrom="ListBoxEmployees" /> <asp:LinkButton Runat="server" OnClick="AddEmployees"><img border="0" src="images/right.gif"></asp:LinkButton> <asp:LinkButton Runat="server" OnClick="RemoveEmployees"><img border="0" src="images/left.gif"></asp:LinkButton> <asp:LinkButton Runat="server" OnClick="AddAllEmployees"><img border="0" src="images/rightAll.gif"></asp:LinkButton> <asp:LinkButton Runat="server" OnClick="RemoveAllEmployees"><img border="0" src="images/leftAll.gif"></asp:LinkButton> ... <asp:ListBox ID="ListBoxProjectMembers" Runat="server" SelectionMode="Multiple" CssClass="listbox" /> ... <asp:LinkButton Runat="server" OnClick="MoveUp" ><img border="0" src="images/up.gif"></asp:LinkButton> <asp:LinkButton Runat="server" OnClick="MoveDown"><img border="0" src="images/down.gif"></asp:LinkButton>
Fluent.ControlFocus Component Now Available
We have released a new aspect oriented .NET component which may be of interest to developers. It is a very simple non-visual web control which will automatically set focus to a given WebControl after the page has completed loading. This may be useful in situations such as page validation, for example, if you want the cursor to be positioned in a given field which needs correction after a validation failure on postback. The control has one simple attribute, and is used as follows:
<%@ Register TagPrefix="fluent" Namespace="Fluent.ControlFocus" Assembly="Fluent.ControlFocus" %> ... <asp:TextBox runat="server" ID="FirstTextBox" /><br/> <%-- the control below, SecondTextBox, will be focused on page load --%> <asp:TextBox runat="server" ID="SecondTextBox" /> <fluent:ControlFocus runat="server" Control="SecondTextBox" />
Please visit the Fluent Consulting aspect oriented components section for more information or to download this free component!
Aspect Oriented Components: An Introduction
I'd like to describe a component development strategy that we've been using at Fluent Consulting for some time that has proven very useful. It has become common knowledge that software functionality which is implemented in a similar manner over and over again is a good candidate for being re-written and deployed as a component. In the .NET framework this would typically be done by wrapping your reusable functionality into a custom server control and deploying it as a standalone DLL, which can be dropped into the /bin folder of any application.
In many situations these types of components are very valuable, and we have built up a library of such components that we often use when developing applications. There are times, however, when the code we would like to be able to reuse is not standalone functionality, but rather needs to be interwoven with existing code. In these cases we have adopted a modified component development strategy, based on aspect-oriented software development. For more information about aspects you can review a list of papers published by XEROX PARC (where AOP was invented), a list of aspect development tools, or an article about implementing aspects in C#. I'll describe our methodology with an example.
The .NET framework offers a number of rich web controls which can be bound to data sources and populated at runtime. The DataGrid is the best known of these. Suppose you wanted to make a few changes to how the DataGrid control worked; here's an example of an enhancement. If you bind a DataGrid to a data source which is empty at runtime, when the control renders you will see the header and footer but no contents of the DataGrid. A better behavior would be for the DataGrid to not display the header and footer, but rather display to the user a friendly message saying "No results were returned from your search." or something to similar effect.
The traditional strategy for implementing this feature as a component in .NET would be to create a custom server control, which inherits from the DataGrid control, and adds the required functionality to handle empty data sources. There are a number of considerations which make this approach less than ideal however:
- Because the custom control inherits from the DataGrid control, if you'd like similar behavior for similar data-bound controls such as RadioButtonLists or Repeaters, you would have to create new custom controls for each one, subclassing its respective parent class.
- If you want to incorporate this new functionality into existing code, you would have to change all of your existing DataGrids to implement your new control class.
- Using a custom server control tag in your .aspx page instead of the built-in ASP.NET DataGrid control tag, you will lose the nice Visual Studio .NET tag auto-completion features. These IDE completion features can be very useful on controls as complex as the DataGrid, which have many public properties and subtemplates.
A better solution would be to implement this behavior as an Aspect-Oriented Component. With this methodology, our component does not reimplement any DataGrid features directly, but rather hooks in to an existing DataGrid control at runtime and modifies its properties as needed. The original DataGrid control tag remains as is. Here's a code-snippet of the source for our control:
using System; using System.Web.UI.WebControls; using System.Web; using System.Web.UI; namespace Fluent.EmptyRepeaterPanel { /// <summary> /// Fluent.EmptyRepeaterPanel is an aspect oriented component which checks a data source /// bound to a repeater at runtime. If the data source is empty, the repeater is hidden /// and the contents of the panel are shown in its place. /// </summary> public class EmptyRepeaterPanel : Panel, INamingContainer { private string control; public EmptyRepeaterPanel() { } protected override void AddParsedSubObject(object obj) { this.Controls.Add((Control)obj); } protected override void OnPreRender(System.EventArgs e) { base.OnPreRender(e); string controlName = control.Trim(); try { Control c = (Control) Page.FindControl(controlName); if (c is DataGrid) { DataGrid dg = (DataGrid) Page.FindControl(controlName); if (dg.Items.Count == 0) { dg.Visible = false; } else { this.Controls.Clear(); } } else if (c is ListControl) { ListControl lc = (ListControl) Page.FindControl(controlName); if (lc.Items.Count == 0) { lc.Visible = false; } else { this.Controls.Clear(); } } else if (c is Repeater) { Repeater r = (Repeater) Page.FindControl(controlName); if (r.Items.Count == 0) { r.Visible = false; } else { this.Controls.Clear(); } } else { throw new ApplicationException(@" Control not a valid type. Must be a ListControl, DataGrid, or Repeater, or a type inherited from one of these."); } } catch (System.NullReferenceException nre) { throw new ApplicationException(@" EmptyRepeaterPanel unable to find a control named " + controlName + ".", nre); } } /// <summary> /// ID of the Repeater WebControl to hide if it's data source is empty /// </summary> public string Control { get { return control; } set { control = value; } } } }
This code can be compiled down into a DLL and referenced from any application we need to use it in. To make use of it, first we need to register it in our page like this:
<%@ Register TagPrefix="fluent" Namespace="Fluent.Format" Assembly="Format" %>
In this case our DataGrid is implemented as it normally would, but the .aspx page has an additional tag which modifies the behavior of the DataGrid.
<fluent:EmptyRepeaterPanel Runat="server" Control="TestGrid"> <asp:Label Runat="server" Font-Bold="True" ForeColor="red">Sorry, search returned 0 results.</asp:Label> </fluent:EmptyRepeaterPanel> <asp:DataGrid Runat="server" ID="TestGrid" AutoGenerateColumns="False" ShowFooter="True"> <Columns> <asp:BoundColumn DataField="SSN" HeaderText="SSN" /> <asp:BoundColumn DataField="FNAME" HeaderText="FNAME" /> <asp:BoundColumn DataField="LNAME" HeaderText="LNAME" /> </Columns> </asp:DataGrid>
With this approach we've made it quite easy to modify the behavior of existing native .NET web controls. Aspect Oriented Components are a clean and simple way to add powerful new functionality to your code. In the coming weeks, we will be describing our approach in further detail and discussing promising potential uses.



