|
Prior to ASP.net 2.0 page developers were required to write code in the Page's Load event to initialize control properties when the value was dynamic. For example, to display some dates on the page, a developer would have to write this code: <%@ Page Language="C#" %> Untitled Page What this meant was that page developers now had to be familiar with the page lifecycle of ASP.net. Is Load the right event? Maybe Init? Perhaps something else? In ASP.net 2.0 there is a new feature called ExpressionBuilders that allow complex property values to be set in the persistence of a control. ASP.net ships with three expression builders: - ConnectionStrings
Allows access to the connection strings stored in section of the configuration file. - AppSettings
Allows access to the application settings stored in section of the configuration file. - Resources
Allows access to localized resources, typically stored in .resx files located in the App_GlobalResources and App_LocalResources folders. Expression builders actually generate dynamic code using System.CodeDom that will get executed when the page is run. The expression builder syntax is similar to the databinding syntax that many are familiar with, but instead of <%# expression %> they use <%$ prefix:value %>: To simplify the code at the top of the page, an expression builder that understands time is needed. It has to generate code that calls DateTime.Now and optionally adds a certain number of days to it. Here is the new TimeExpressionBuilder: namespace ControlBuilderFaq.Samples { using System; using System.CodeDom; using System.Web; using System.Web.UI; using System.Web.Compilation; [ExpressionPrefix("Time")] public class TimeExpressionBuilder : ExpressionBuilder { public override CodeExpression GetCodeExpression(BoundPropertyEntry entry, object parsedData, ExpressionBuilderContext context) { if (String.Equals(entry.Expression, "Today", StringComparison.OrdinalIgnoreCase)) { // System.DateTime.Now return new CodePropertyReferenceExpression( new CodeTypeReferenceExpression(typeof(DateTime)), "Now"); } else { if (String.Equals(entry.Expression, "Yesterday", StringComparison.OrdinalIgnoreCase)) { // System.DateTime.Now.AddDays(-1) return new CodeMethodInvokeExpression( new CodePropertyReferenceExpression( new CodeTypeReferenceExpression(typeof(DateTime)), "Now"), "AddDays", new CodePrimitiveExpression(-1)); } else { if (String.Equals(entry.Expression, "Tomorrow", StringComparison.OrdinalIgnoreCase)) { // System.DateTime.Now.AddDays(1) return new CodeMethodInvokeExpression( new CodePropertyReferenceExpression( new CodeTypeReferenceExpression(typeof(DateTime)), "Now"), "AddDays", new CodePrimitiveExpression(1)); } else { throw new InvalidOperationException("The expression value should be one of: Today, Yesterday, Tomorrow."); } } } } }} There is typically only one method that needs to be overridden, GetCodeExpression(). All this method has to do is look at entry.Expression, and then use CodeDom to generate some code that represents that expression. The next step is to register the new expression builder in web.config: The new page that uses this expression builder now looks a lot simpler. In fact, there is no more inline code at all! <%@ Page Language="C#" %> Untitled Page While ExpressionBuilders are runtime components, ExpressionEditors are the design-time counterpart to them. They allow for a better editing experience for page developers. Expression editors allow expressions to be edited in the Expressions Editor dialog using an ExpressionEditorSheet. The Expressions Editor dialog is accessible from the property grid of any control:  And this is the dialog that the users will see with the new expression builder and expression editor: Here's the additional code required for the expression editor and its associated editor sheet: namespace ControlBuilderFaq.Samples { using System; using System.CodeDom; using System.ComponentModel; using System.Web; using System.Web.UI; using System.Web.UI.Design; using System.Web.Compilation; [ExpressionPrefix("Time")] [ExpressionEditor(typeof(TimeExpressionEditor))] public class TimeExpressionBuilder : ExpressionBuilder { public override CodeExpression GetCodeExpression(BoundPropertyEntry entry, object parsedData, ExpressionBuilderContext context) { if (String.Equals(entry.Expression, "Today", StringComparison.OrdinalIgnoreCase)) { // System.DateTime.Now return new CodePropertyReferenceExpression( new CodeTypeReferenceExpression(typeof(DateTime)), "Now"); } else { if (String.Equals(entry.Expression, "Yesterday", StringComparison.OrdinalIgnoreCase)) { // System.DateTime.Now.AddDays(-1) return new CodeMethodInvokeExpression( new CodePropertyReferenceExpression( new CodeTypeReferenceExpression(typeof(DateTime)), "Now"), "AddDays", new CodePrimitiveExpression(-1)); } else { if (String.Equals(entry.Expression, "Tomorrow", StringComparison.OrdinalIgnoreCase)) { // System.DateTime.Now.AddDays(1) return new CodeMethodInvokeExpression( new CodePropertyReferenceExpression( new CodeTypeReferenceExpression(typeof(DateTime)), "Now"), "AddDays", new CodePrimitiveExpression(1)); } else { throw new InvalidOperationException("The expression value should be one of: Today, Yesterday, Tomorrow."); } } } } } public class TimeExpressionEditor : ExpressionEditor { // Evaluates an expression at design-time for preview purposes public override object EvaluateExpression(string expression, object parseTimeData, Type propertyType, IServiceProvider serviceProvider) { if (String.Equals(expression, "Today", StringComparison.OrdinalIgnoreCase)) { return System.DateTime.Now.ToString(); } else { if (String.Equals(expression, "Yesterday", StringComparison.OrdinalIgnoreCase)) { return System.DateTime.Now.AddDays(-1).ToString(); } else { if (String.Equals(expression, "Tomorrow", StringComparison.OrdinalIgnoreCase)) { return System.DateTime.Now.AddDays(1).ToString(); } else { throw new InvalidOperationException("The expression value should be one of: Today, Yesterday, Tomorrow."); } } } } public override ExpressionEditorSheet GetExpressionEditorSheet(string expression, IServiceProvider serviceProvider) { return new TimeExpressionEditorSheet(expression, serviceProvider); } private sealed class TimeExpressionEditorSheet : ExpressionEditorSheet { private string _day; public TimeExpressionEditorSheet(string day, IServiceProvider serviceProvider) : base(serviceProvider) { Day = day; } [TypeConverter(typeof(TimeConverter))] public string Day { get { return _day; } set { _day = value; } } public override string GetExpression() { return Day; } // TypeConverter to provide dropdown of valid values private sealed class TimeConverter : StringConverter { public override bool GetStandardValuesExclusive(ITypeDescriptorContext context) { return true; } public override bool GetStandardValuesSupported(ITypeDescriptorContext context) { return true; } public override TypeConverter.StandardValuesCollection GetStandardValues(ITypeDescriptorContext context) { string[] standardValues = new string[] { "Today", "Yesterday", "Tomorrow" }; return new StandardValuesCollection(standardValues); } } } }} Finally, here's a look at some of the source code that actually gets generated when the new expression builder is used @__ctrl.Text = System.Convert.ToString(System.DateTime.Now.AddDays(-1), System.Globalization.CultureInfo.CurrentCulture); - Eilon
|