2008年12月16日,星期二

使用.Net Expression Builders设置控件属性

在上一篇文章中,我介绍了我的 多语言SharePoint网站的语言存储解决方案,并展示了它的两种使用方式:

在标准.Net程序代码中:

string sButtonText = LanguageStore.GetValue("Search", "SearchGoButtonText");

在HTML中以声明方式:


<asp:Button runat="server" id="btnSearch" Text="<%$ SPLang:Search|SearchGoButtonText %>" />


这种声明性语法非常有用,因为它意味着开发人员不必仅将代码隐藏文件弄乱,只需调用该方法来检索值,然后将其分配给各种控件的'Text'属性即可。我也已经改装了这个 Config Store解决方案 (以及其他一些增强功能),它将很快在Codeplex上可用。您可能会注意到它的语法与 SPUrl令牌 可以在母版页/页面布局中使用以获取图像或CSS文件的相对路径,这是因为我使用的是相同的.Net技术。由于我必须进行一些挖掘来确定如何完成此操作,所以我猜(这里可能是错误的!)许多其他开发人员也没有遇到过此问题,因此这是完成的过程。

实现表达式构建器类

表达式生成器本质上是一个派生自System.Web.Compilation.ExpressionBuilder的类,并且包含在页面解析时评估表达式的逻辑。 “秘密”是ASP.Net页解析引擎了解,只要遇到适当形式的表达式,它就需要调用类的方法。这些是将这个微型框架结合在一起的东西:

  • 从ExpressionBuilder派生的类,该类重写EvaluateExpression()和GetCodeExpression()方法

  • web.config中的声明将您的前缀(在我的情况下为“ SPLang”)与表达式构建器类相关联

  • 类对设计师的支持对ExpressionPrefix属性的可选使用

  • 声明性HTML中的表达式(按照上面的示例)

逐步进行操作,这是我班级的样子:


[ExpressionPrefix("SPLangStore")]
public class LangStoreExpressionBuilder : ExpressionBuilder
{
private static TraceSwitch traceSwitch = new TraceSwitch("COB.SharePoint.Utilities.LanguageStore",
"Trace switch for 语言商店");

private static LangStoreTraceHelper trace = new LangStoreTraceHelper("COB.SharePoint.Utilities.LangStoreExpressionBuilder");

public static 目的 GetEvalData(string expression, Type target, string entry)
{
trace.WriteLineIf(traceSwitch.TraceVerbose, TraceLevel.Verbose, "GetEvalData(): Entered with expression '{0}'.",
expression);

string[] aExpressionParts = expression.Split('|');
string sCategory = aExpressionParts[0];
string sTitle = aExpressionParts[1];

if ((aExpressionParts.Length != 2) || (string.IsNullOrEmpty(sCategory) || string.IsNullOrEmpty(sTitle)))
{
trace.WriteLineIf(traceSwitch.TraceError, TraceLevel.Error, "GetEvalData(): Unable to parse expression '{0}' into " +
"format 'Category|Title' - throwing exception.",
expression);

throw new LanguageStoreConfigurationException("Token passed to 语言商店 expression builder was in the wrong format - " +
"expressions should be in form 语言商店 Category|Item Title e.g. Search|SearchGoButtonText");
}

string sValue = LanguageStore.GetValue(sCategory, sTitle);

trace.WriteLineIf(traceSwitch.TraceInfo, TraceLevel.Info, "GetEvalData(): Retrieved '{0}' from 语言商店.",
sValue);

trace.WriteLineIf(traceSwitch.TraceVerbose, TraceLevel.Verbose, "GetEvalData(): Returning '{0}'.",
sValue);

return sValue;
}

public override 目的 EvaluateExpression(目的 target, BoundPropertyEntry entry,
目的 parsedData, ExpressionBuilderContext context)
{
return GetEvalData(entry.Expression, target.GetType(), entry.Name);
}

public override CodeExpression GetCodeExpression(BoundPropertyEntry entry,
目的 parsedData, ExpressionBuilderContext context)
{
Type type1 = entry.DeclaringType;
PropertyDescriptor descriptor1 = TypeDescriptor.GetProperties(type1)[entry.PropertyInfo.Name];
CodeExpression[] expressionArray1 = new CodeExpression[3];
expressionArray1[0] = new CodePrimitiveExpression(entry.Expression.Trim());
expressionArray1[1] = new CodeTypeOfExpression(type1);
expressionArray1[2] = new CodePrimitiveExpression(entry.Name);
return new CodeCastExpression(descriptor1.PropertyType, new CodeMethodInvokeExpression(new
CodeTypeReferenceExpression(base.GetType()), "GetEvalData", expressionArray1));
}

public override bool SupportsEvaluate
{
get { return true; }
}
}


如果您想知道为什么需要两种方法,那是因为在页面已编译的地方使用了GetCodeExpression(),而在纯解析页面时使用了EvaluateExpression()。我的代码遵循支持两种模式的MSDN模式,并使用两种都调用的第三种帮助器方法(GetEvalData())。正是这个GetEvalData()方法完成了对传递的表达式的解析,然后使用它来获取值的方法-在我的例子中,表达式是要从Language Store中获取的项的“类别”和“标题”。注意,实际上,所有这些关键行都是GetEvalData()中的一行,该行调用了我现有的LanguageStore.GetValue()方法-因此,有效地,我的表达式生成器只是该方法的包装。

我的web.config条目如下所示:


<add expressionPrefix="SPLang" type="COB.SharePoint.Utilities.LangStoreExpressionBuilder, COB.SharePoint.Utilities.LanguageStore, Version=1.0.0.0, Culture=neutral, PublicKeyToken=23afbf06fd91fa64" />

最后是如何使用表达式的组成部分:

ExpressionBuilder语法

对于SharePoint解决方案,假设我们将代码部署为功能/解决方案,我们通常希望通过SPWebConfigModification类自动添加web.config条目。您可以在Codeplex的源代码中找到执行此操作的代码 语言商店 解决方案(在功能接收器中)。

最后,如果您要构建一个表达式生成器,而该信息并不能完全解决问题, ExpressionBuilder的MSDN文档 该课程还有一些其他详细信息。

使用自定义表达式编辑器增强设计时体验

这是我还没有看过的东西,但是看起来非常酷!如果标准表达式生成器的内容对您来说不够方便,则可以通过提供自定义的“ ExpressionEditor”以在Visual Studio中使用来进一步扩展功能。如果我正确理解了可能性,则可以通过两种方式在VS属性网格中提供更好的体验:

  • 自定义编辑器表单(例如,用于输入语言商店项目的“类别”和“标题”的对话框-假设分别输入了“搜索”和“ SearchGoButtonText”,这将在正确的定界“搜索”中“构建”字符串|需要SearchGoButtonText'表单)

  • 使用 表达方式 集合-(我认为)可以用来查询“语言商店”列表并显示所有项目,因此只需单击几下即可简单地选择要显示其译文的项目,无需键入任何内容!

我绝对喜欢为语言存储/配置存储实现此功能-因此以后可能会再次使用它!

结论

表达式构建器提供了一种强大,干净的方法来将方法调用注入到您的标记中。在大多数情况下,我们习惯于像我的语言存储/配置存储实现中那样看到它们返回字符串,并且请注意,.Net框架中还存在以下实现:

但是,要记住的最后一件事是该方法的签名返回一个 目的 -因此,从理论上讲,应该有可能做很多其他事情,其中​​处理过程返回一个更复杂的对象,该对象被分配给控件属性。一个例子是数据绑定方案,其中您的方法返回实现IEnumerable / IList的东西-然后可以将其分配给控件的DataSource属性。 声明性地。您可能还会想到其他可能性,但希望这是值得深思的;-)

没意见: