1793 lines
95 KiB
C#
1793 lines
95 KiB
C#
|
//------------------------------------------------------------------------------
|
||
|
// <copyright file="BaseTemplateCodeDomTreeGenerator.cs" company="Microsoft">
|
||
|
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||
|
// </copyright>
|
||
|
//------------------------------------------------------------------------------
|
||
|
|
||
|
namespace System.Web.Compilation {
|
||
|
|
||
|
using System;
|
||
|
using System.CodeDom;
|
||
|
using System.CodeDom.Compiler;
|
||
|
using System.Collections;
|
||
|
using System.Collections.Specialized;
|
||
|
using System.ComponentModel;
|
||
|
using System.ComponentModel.Design;
|
||
|
using System.Diagnostics.CodeAnalysis;
|
||
|
using System.Globalization;
|
||
|
using System.IO;
|
||
|
using System.Reflection;
|
||
|
using System.Runtime.InteropServices;
|
||
|
using System.Web.Caching;
|
||
|
using System.Web.Configuration;
|
||
|
using System.Web.UI;
|
||
|
using System.Web.Util;
|
||
|
using Debug = System.Web.Util.Debug;
|
||
|
|
||
|
internal abstract class BaseTemplateCodeDomTreeGenerator : BaseCodeDomTreeGenerator {
|
||
|
|
||
|
protected static readonly string buildMethodPrefix = "__BuildControl";
|
||
|
protected static readonly string extractTemplateValuesMethodPrefix = "__ExtractValues";
|
||
|
protected static readonly string templateSourceDirectoryName = "AppRelativeTemplateSourceDirectory";
|
||
|
protected static readonly string applyStyleSheetMethodName = "ApplyStyleSheetSkin";
|
||
|
protected static readonly string pagePropertyName = "Page";
|
||
|
internal const string skinIDPropertyName = "SkinID";
|
||
|
private const string _localVariableRef = "__ctrl";
|
||
|
|
||
|
private TemplateParser _parser;
|
||
|
private int _controlCount;
|
||
|
|
||
|
// Minimum literal string length for it to be placed in the resource
|
||
|
private const int minLongLiteralStringLength = 256;
|
||
|
private const string renderMethodParameterName = "__w";
|
||
|
|
||
|
// Used in designer mode
|
||
|
internal const string tempObjectVariable = "__o";
|
||
|
|
||
|
|
||
|
/*
|
||
|
* Set some fields that are needed for code generation
|
||
|
*/
|
||
|
internal BaseTemplateCodeDomTreeGenerator(TemplateParser parser) : base(parser) {
|
||
|
_parser = parser;
|
||
|
}
|
||
|
|
||
|
private TemplateParser Parser {
|
||
|
get {
|
||
|
return _parser;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
private CodeStatement GetOutputWriteStatement(CodeExpression expr, bool encode) {
|
||
|
|
||
|
// Call HttpUtility.HtmlEncode on the expression if needed
|
||
|
if (encode) {
|
||
|
expr = new CodeMethodInvokeExpression(
|
||
|
new CodeMethodReferenceExpression(
|
||
|
new CodeTypeReferenceExpression(typeof(HttpUtility)),
|
||
|
"HtmlEncode"),
|
||
|
expr);
|
||
|
}
|
||
|
|
||
|
CodeMethodInvokeExpression methodInvoke = new CodeMethodInvokeExpression();
|
||
|
CodeExpressionStatement call = new CodeExpressionStatement(methodInvoke);
|
||
|
methodInvoke.Method.TargetObject = new CodeArgumentReferenceExpression(renderMethodParameterName);
|
||
|
methodInvoke.Method.MethodName = "Write";
|
||
|
|
||
|
methodInvoke.Parameters.Add(expr);
|
||
|
|
||
|
return call;
|
||
|
}
|
||
|
|
||
|
|
||
|
/// <devdoc>
|
||
|
/// Append an output.Write() statement to a Render method
|
||
|
/// </devdoc>
|
||
|
private void AddOutputWriteStatement(CodeStatementCollection methodStatements,
|
||
|
CodeExpression expr,
|
||
|
CodeLinePragma linePragma) {
|
||
|
|
||
|
CodeStatement outputWriteStmt = GetOutputWriteStatement(expr, false /*encode*/);
|
||
|
if (linePragma != null)
|
||
|
outputWriteStmt.LinePragma = linePragma;
|
||
|
|
||
|
methodStatements.Add(outputWriteStmt);
|
||
|
}
|
||
|
|
||
|
private void AddOutputWriteStringStatement(CodeStatementCollection methodStatements,
|
||
|
String s) {
|
||
|
|
||
|
if (!UseResourceLiteralString(s)) {
|
||
|
AddOutputWriteStatement(methodStatements, new CodePrimitiveExpression(s), null);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
// Add the string to the resource builder, and get back its offset/size
|
||
|
int offset, size;
|
||
|
bool fAsciiOnly;
|
||
|
_stringResourceBuilder.AddString(s, out offset, out size, out fAsciiOnly);
|
||
|
|
||
|
// e.g. WriteUTF8ResourceString(output, 314, 20);
|
||
|
CodeMethodInvokeExpression methodInvoke = new CodeMethodInvokeExpression();
|
||
|
CodeExpressionStatement call = new CodeExpressionStatement(methodInvoke);
|
||
|
methodInvoke.Method.TargetObject = new CodeThisReferenceExpression();
|
||
|
methodInvoke.Method.MethodName = "WriteUTF8ResourceString";
|
||
|
methodInvoke.Parameters.Add(new CodeArgumentReferenceExpression(renderMethodParameterName));
|
||
|
methodInvoke.Parameters.Add(new CodePrimitiveExpression(offset));
|
||
|
methodInvoke.Parameters.Add(new CodePrimitiveExpression(size));
|
||
|
methodInvoke.Parameters.Add(new CodePrimitiveExpression(fAsciiOnly));
|
||
|
methodStatements.Add(call);
|
||
|
}
|
||
|
|
||
|
private static void BuildAddParsedSubObjectStatement(
|
||
|
CodeStatementCollection statements, CodeExpression ctrlToAdd, CodeLinePragma linePragma, CodeExpression ctrlRefExpr, ref bool gotParserVariable) {
|
||
|
|
||
|
if (!gotParserVariable) {
|
||
|
// e.g. IParserAccessor __parser = ((IParserAccessor)__ctrl);
|
||
|
CodeVariableDeclarationStatement parserDeclaration = new CodeVariableDeclarationStatement();
|
||
|
parserDeclaration.Name = "__parser";
|
||
|
parserDeclaration.Type = new CodeTypeReference(typeof(IParserAccessor));
|
||
|
parserDeclaration.InitExpression = new CodeCastExpression(
|
||
|
typeof(IParserAccessor),
|
||
|
ctrlRefExpr);
|
||
|
statements.Add(parserDeclaration);
|
||
|
gotParserVariable = true;
|
||
|
}
|
||
|
|
||
|
// e.g. __parser.AddParsedSubObject({{controlName}});
|
||
|
CodeMethodInvokeExpression methCallExpression = new CodeMethodInvokeExpression(
|
||
|
new CodeVariableReferenceExpression("__parser"), "AddParsedSubObject");
|
||
|
methCallExpression.Parameters.Add(ctrlToAdd);
|
||
|
CodeExpressionStatement methCallStatement = new CodeExpressionStatement(methCallExpression);
|
||
|
methCallStatement.LinePragma = linePragma;
|
||
|
|
||
|
statements.Add(methCallStatement);
|
||
|
}
|
||
|
|
||
|
internal virtual CodeExpression BuildPagePropertyReferenceExpression() {
|
||
|
return new CodePropertyReferenceExpression(new CodeThisReferenceExpression(), pagePropertyName);
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Build the data tree for a control's build method
|
||
|
*/
|
||
|
protected CodeMemberMethod BuildBuildMethod(ControlBuilder builder, bool fTemplate,
|
||
|
bool fInTemplate, bool topLevelControlInTemplate, PropertyEntry pse, bool fControlSkin) {
|
||
|
|
||
|
Debug.Assert(builder.ServiceProvider == null);
|
||
|
|
||
|
ServiceContainer container = new ServiceContainer();
|
||
|
container.AddService(typeof(IFilterResolutionService), HttpCapabilitiesBase.EmptyHttpCapabilitiesBase);
|
||
|
|
||
|
try {
|
||
|
builder.SetServiceProvider(container);
|
||
|
builder.EnsureEntriesSorted();
|
||
|
}
|
||
|
finally {
|
||
|
builder.SetServiceProvider(null);
|
||
|
}
|
||
|
|
||
|
string methodName = GetMethodNameForBuilder(buildMethodPrefix, builder);
|
||
|
Type ctrlType = GetCtrlTypeForBuilder(builder, fTemplate);
|
||
|
bool fStandardControl = false;
|
||
|
bool fControlFieldDeclared = false;
|
||
|
|
||
|
CodeMemberMethod method = new CodeMemberMethod();
|
||
|
AddDebuggerNonUserCodeAttribute(method);
|
||
|
method.Name = methodName;
|
||
|
method.Attributes = MemberAttributes.Private | MemberAttributes.Final;
|
||
|
|
||
|
_sourceDataClass.Members.Add(method);
|
||
|
|
||
|
// If it's for a template or a r/o complex prop, pass a parameter of the control's type
|
||
|
ComplexPropertyEntry cpse = pse as ComplexPropertyEntry;
|
||
|
if (fTemplate || (cpse != null && cpse.ReadOnly)) {
|
||
|
if (builder is RootBuilder)
|
||
|
method.Parameters.Add(new CodeParameterDeclarationExpression(_sourceDataClass.Name, "__ctrl"));
|
||
|
else
|
||
|
method.Parameters.Add(new CodeParameterDeclarationExpression(ctrlType, "__ctrl"));
|
||
|
}
|
||
|
else {
|
||
|
// If it's a standard control, return it from the method
|
||
|
if (typeof(Control).IsAssignableFrom(builder.ControlType)) {
|
||
|
fStandardControl = true;
|
||
|
}
|
||
|
|
||
|
Debug.Assert(builder.ControlType != null);
|
||
|
if (builder.ControlType != null) {
|
||
|
if (fControlSkin) {
|
||
|
// ReturnType needs to be of type Control in a skin file to match
|
||
|
// the controlskin delegate.
|
||
|
if (fStandardControl) {
|
||
|
method.ReturnType = new CodeTypeReference(typeof(Control));
|
||
|
}
|
||
|
}
|
||
|
else {
|
||
|
PartialCachingAttribute cacheAttrib = (PartialCachingAttribute)
|
||
|
TypeDescriptor.GetAttributes(builder.ControlType)[typeof(PartialCachingAttribute)];
|
||
|
if (cacheAttrib != null) {
|
||
|
method.ReturnType = new CodeTypeReference(typeof(Control));
|
||
|
}
|
||
|
else {
|
||
|
// Otherwise the return type is always the actual component type.
|
||
|
method.ReturnType = CodeDomUtility.BuildGlobalCodeTypeReference(builder.ControlType);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// A control field declaration is required, this field will be returned
|
||
|
// in the method.
|
||
|
fControlFieldDeclared = true;
|
||
|
}
|
||
|
|
||
|
// Add a control parameter if it's a ControlSkin
|
||
|
if (fControlSkin) {
|
||
|
method.Parameters.Add(new CodeParameterDeclarationExpression(typeof(Control).FullName, "ctrl"));
|
||
|
}
|
||
|
|
||
|
BuildBuildMethodInternal(builder, builder.ControlType, fInTemplate, topLevelControlInTemplate, pse,
|
||
|
method.Statements, fStandardControl, fControlFieldDeclared, null, fControlSkin);
|
||
|
|
||
|
return method;
|
||
|
}
|
||
|
|
||
|
/* Helper method to generate the content of the control's build method
|
||
|
* Type _ctrl;
|
||
|
* _ctrl = new Type();
|
||
|
* ...
|
||
|
* return _ctrl;
|
||
|
*/
|
||
|
[SuppressMessage("Microsoft.Usage", "CA2303:FlagTypeGetHashCode", Justification = "This is used for caching - it's ok to supress.")]
|
||
|
private void BuildBuildMethodInternal(ControlBuilder builder, Type ctrlType, bool fInTemplate,
|
||
|
bool topLevelControlInTemplate, PropertyEntry pse, CodeStatementCollection statements,
|
||
|
bool fStandardControl, bool fControlFieldDeclared, string deviceFilter, bool fControlSkin) {
|
||
|
|
||
|
// Same linePragma in the entire build method
|
||
|
CodeLinePragma linePragma = CreateCodeLinePragma(builder);
|
||
|
|
||
|
CodeObjectCreateExpression newExpr;
|
||
|
CodeExpressionStatement methCallStatement;
|
||
|
CodeMethodInvokeExpression methCallExpression;
|
||
|
|
||
|
CodeExpression ctrlRefExpr;
|
||
|
|
||
|
if (fControlSkin) {
|
||
|
CodeCastExpression cast = new CodeCastExpression(builder.ControlType.FullName,
|
||
|
new CodeArgumentReferenceExpression("ctrl"));
|
||
|
statements.Add(new CodeVariableDeclarationStatement(builder.ControlType.FullName, "__ctrl", cast));
|
||
|
ctrlRefExpr = new CodeVariableReferenceExpression("__ctrl");
|
||
|
}
|
||
|
// Not a control. ie. it's for a template or a r/o complex prop,
|
||
|
else if (!fControlFieldDeclared) {
|
||
|
ctrlRefExpr = new CodeArgumentReferenceExpression("__ctrl");
|
||
|
}
|
||
|
else {
|
||
|
CodeTypeReference ctrlTypeRef = CodeDomUtility.BuildGlobalCodeTypeReference(ctrlType);
|
||
|
|
||
|
newExpr = new CodeObjectCreateExpression(ctrlTypeRef);
|
||
|
|
||
|
// If it has a ConstructorNeedsTagAttribute, it needs a tag name
|
||
|
ConstructorNeedsTagAttribute cnta = (ConstructorNeedsTagAttribute)
|
||
|
TypeDescriptor.GetAttributes(ctrlType)[typeof(ConstructorNeedsTagAttribute)];
|
||
|
|
||
|
if (cnta != null && cnta.NeedsTag) {
|
||
|
newExpr.Parameters.Add(new CodePrimitiveExpression(builder.TagName));
|
||
|
}
|
||
|
|
||
|
// If it's for a DataBoundLiteralControl, pass it the number of
|
||
|
// entries in the constructor
|
||
|
DataBoundLiteralControlBuilder dataBoundBuilder = builder as DataBoundLiteralControlBuilder;
|
||
|
if (dataBoundBuilder != null) {
|
||
|
newExpr.Parameters.Add(new CodePrimitiveExpression(
|
||
|
dataBoundBuilder.GetStaticLiteralsCount()));
|
||
|
newExpr.Parameters.Add(new CodePrimitiveExpression(
|
||
|
dataBoundBuilder.GetDataBoundLiteralCount()));
|
||
|
}
|
||
|
|
||
|
// e.g. {{controlTypeName}} __ctrl;
|
||
|
statements.Add(new CodeVariableDeclarationStatement(ctrlTypeRef, "__ctrl"));
|
||
|
ctrlRefExpr = new CodeVariableReferenceExpression("__ctrl");
|
||
|
|
||
|
// e.g. __ctrl = new {{controlTypeName}}();
|
||
|
CodeAssignStatement setCtl = new CodeAssignStatement(ctrlRefExpr, newExpr);
|
||
|
setCtl.LinePragma = linePragma;
|
||
|
statements.Add(setCtl);
|
||
|
|
||
|
if (!builder.IsGeneratedID) {
|
||
|
// Assign the local control reference to the global control variable
|
||
|
CodeFieldReferenceExpression ctrlNameExpr = new CodeFieldReferenceExpression(new CodeThisReferenceExpression(), builder.ID);
|
||
|
|
||
|
// e.g. {{controlName}} = __ctrl;
|
||
|
CodeAssignStatement underscoreCtlSet = new CodeAssignStatement(ctrlNameExpr, ctrlRefExpr);
|
||
|
statements.Add(underscoreCtlSet);
|
||
|
}
|
||
|
|
||
|
// Don't do this if the control is itself a TemplateControl, in which case it
|
||
|
// will point its TemplateControl property to itself (instead of its parent
|
||
|
// TemplateControl). VSWhidbey 214356.
|
||
|
if (topLevelControlInTemplate && !typeof(TemplateControl).IsAssignableFrom(ctrlType)) {
|
||
|
statements.Add(BuildTemplatePropertyStatement(ctrlRefExpr));
|
||
|
}
|
||
|
|
||
|
if (fStandardControl) {
|
||
|
// e.g. __ctrl.SkinID = {{skinID}};
|
||
|
if (builder.SkinID != null) {
|
||
|
CodeAssignStatement set = new CodeAssignStatement();
|
||
|
set.Left = new CodePropertyReferenceExpression(ctrlRefExpr, skinIDPropertyName);
|
||
|
set.Right = new CodePrimitiveExpression(builder.SkinID);
|
||
|
statements.Add(set);
|
||
|
}
|
||
|
|
||
|
// e.g. __ctrl.ApplyStyleSheetSkin(this);
|
||
|
if (ThemeableAttribute.IsTypeThemeable(ctrlType)) {
|
||
|
// e.g. __ctrl.ApplyStyleSheetSkin(this.Page);
|
||
|
CodeMethodInvokeExpression applyStyleSheetExpr = new CodeMethodInvokeExpression(ctrlRefExpr, applyStyleSheetMethodName);
|
||
|
applyStyleSheetExpr.Parameters.Add(BuildPagePropertyReferenceExpression());
|
||
|
|
||
|
statements.Add(applyStyleSheetExpr);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Process the templates
|
||
|
if (builder.TemplatePropertyEntries.Count > 0) {
|
||
|
|
||
|
// Used to deal with the device filter conditionals
|
||
|
CodeStatementCollection currentStmts;
|
||
|
CodeStatementCollection nextStmts = statements;
|
||
|
PropertyEntry previous = null;
|
||
|
|
||
|
foreach (TemplatePropertyEntry pseSub in builder.TemplatePropertyEntries) {
|
||
|
currentStmts = nextStmts;
|
||
|
|
||
|
HandleDeviceFilterConditional(ref previous, pseSub, statements, ref currentStmts, out nextStmts);
|
||
|
|
||
|
string controlName = pseSub.Builder.ID;
|
||
|
CodeDelegateCreateExpression newDelegate = new CodeDelegateCreateExpression();
|
||
|
newDelegate.DelegateType = new CodeTypeReference(typeof(BuildTemplateMethod));
|
||
|
newDelegate.TargetObject = new CodeThisReferenceExpression();
|
||
|
newDelegate.MethodName = buildMethodPrefix + controlName;
|
||
|
|
||
|
CodeAssignStatement set = new CodeAssignStatement();
|
||
|
if (pseSub.PropertyInfo != null) {
|
||
|
set.Left = new CodePropertyReferenceExpression(ctrlRefExpr, pseSub.Name);
|
||
|
}
|
||
|
else {
|
||
|
set.Left = new CodeFieldReferenceExpression(ctrlRefExpr, pseSub.Name);
|
||
|
}
|
||
|
|
||
|
if (pseSub.BindableTemplate) {
|
||
|
// e.g. __ctrl.{{templateName}} = new CompiledBindableTemplateBuilder(
|
||
|
// e.g. new BuildTemplateMethod(this.__BuildControl {{controlName}}),
|
||
|
// e.g. new ExtractTemplateValuesMethod(this.__ExtractValues {{controlName}}));
|
||
|
CodeExpression newExtractValuesDelegate;
|
||
|
if (pseSub.Builder.HasTwoWayBoundProperties) {
|
||
|
newExtractValuesDelegate = new CodeDelegateCreateExpression();
|
||
|
((CodeDelegateCreateExpression)newExtractValuesDelegate).DelegateType = new CodeTypeReference(typeof(ExtractTemplateValuesMethod));
|
||
|
((CodeDelegateCreateExpression)newExtractValuesDelegate).TargetObject = new CodeThisReferenceExpression();
|
||
|
((CodeDelegateCreateExpression)newExtractValuesDelegate).MethodName = extractTemplateValuesMethodPrefix + controlName;
|
||
|
}
|
||
|
else {
|
||
|
newExtractValuesDelegate = new CodePrimitiveExpression(null);
|
||
|
}
|
||
|
|
||
|
newExpr = new CodeObjectCreateExpression(typeof(CompiledBindableTemplateBuilder));
|
||
|
newExpr.Parameters.Add(newDelegate);
|
||
|
newExpr.Parameters.Add(newExtractValuesDelegate);
|
||
|
|
||
|
}
|
||
|
else {
|
||
|
// e.g. __ctrl.{{templateName}} = new CompiledTemplateBuilder(
|
||
|
// e.g. new BuildTemplateMethod(this.__BuildControl {{controlName}}));
|
||
|
newExpr = new CodeObjectCreateExpression(typeof(CompiledTemplateBuilder));
|
||
|
newExpr.Parameters.Add(newDelegate);
|
||
|
|
||
|
}
|
||
|
set.Right = newExpr;
|
||
|
set.LinePragma = CreateCodeLinePragma(pseSub.Builder);
|
||
|
currentStmts.Add(set);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Is this BuilderData for a declarative control? If so initialize it (75330)
|
||
|
// Only do this is the control field has been declared (i.e. not with templates)
|
||
|
if (typeof(UserControl).IsAssignableFrom(ctrlType) && fControlFieldDeclared && !fControlSkin) {
|
||
|
// e.g. _ctrl.InitializeAsUserControl(Context, Page);
|
||
|
methCallExpression = new CodeMethodInvokeExpression(ctrlRefExpr, "InitializeAsUserControl");
|
||
|
methCallExpression.Parameters.Add(new CodePropertyReferenceExpression(new CodeThisReferenceExpression(), pagePropertyName));
|
||
|
methCallStatement = new CodeExpressionStatement(methCallExpression);
|
||
|
methCallStatement.LinePragma = linePragma;
|
||
|
statements.Add(methCallStatement);
|
||
|
}
|
||
|
|
||
|
// Process the simple attributes
|
||
|
if (builder.SimplePropertyEntries.Count > 0) {
|
||
|
// Used to deal with the device filter conditionals
|
||
|
CodeStatementCollection currentStmts;
|
||
|
CodeStatementCollection nextStmts = statements;
|
||
|
PropertyEntry previous = null;
|
||
|
|
||
|
foreach (SimplePropertyEntry pseSub in builder.SimplePropertyEntries) {
|
||
|
currentStmts = nextStmts;
|
||
|
|
||
|
HandleDeviceFilterConditional(ref previous, pseSub, statements, ref currentStmts, out nextStmts);
|
||
|
|
||
|
CodeStatement statement = pseSub.GetCodeStatement(this, ctrlRefExpr);
|
||
|
statement.LinePragma = linePragma;
|
||
|
currentStmts.Add(statement);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Call the helper method for allowing page developers to customize culture settings
|
||
|
if (typeof(Page).IsAssignableFrom(ctrlType) && !fControlSkin) {
|
||
|
// e.g. this.InitializeCulture();
|
||
|
methCallExpression = new CodeMethodInvokeExpression(new CodeThisReferenceExpression(), "InitializeCulture");
|
||
|
methCallStatement = new CodeExpressionStatement(methCallExpression);
|
||
|
methCallStatement.LinePragma = linePragma;
|
||
|
statements.Add(methCallStatement);
|
||
|
}
|
||
|
|
||
|
// Automatic template support (i.e. <asp:template name=SomeTemplate/>)
|
||
|
CodeMethodInvokeExpression instantiateTemplateExpr = null;
|
||
|
CodeConditionStatement templateIfStmt = null;
|
||
|
CodeStatementCollection buildSubControlBlock = statements;
|
||
|
string autoTemplateName = null;
|
||
|
if (builder is System.Web.UI.WebControls.ContentPlaceHolderBuilder) {
|
||
|
|
||
|
string templateName = ((System.Web.UI.WebControls.ContentPlaceHolderBuilder)builder).Name;
|
||
|
autoTemplateName = MasterPageControlBuilder.AutoTemplatePrefix + templateName;
|
||
|
|
||
|
Debug.Assert(autoTemplateName != null && autoTemplateName.Length > 0, "Template Name is empty.");
|
||
|
|
||
|
// Generate a private field and public property for the ITemplate
|
||
|
string fieldName = "__"+ autoTemplateName;
|
||
|
|
||
|
Type containerType = builder.BindingContainerType;
|
||
|
// Use the base class or template type if INamingContainer cannot be found.
|
||
|
if (!typeof(INamingContainer).IsAssignableFrom(containerType)) {
|
||
|
if (typeof(INamingContainer).IsAssignableFrom(Parser.BaseType)) {
|
||
|
containerType = Parser.BaseType;
|
||
|
}
|
||
|
else {
|
||
|
// This should not occur as all base classes are namingcontainers.
|
||
|
Debug.Assert(false, "baseClassType is not an INamingContainer");
|
||
|
containerType = typeof(System.Web.UI.Control);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
CodeAttributeDeclarationCollection attrDeclarations = new CodeAttributeDeclarationCollection();
|
||
|
|
||
|
CodeAttributeDeclaration templateContainerAttrDeclaration = new CodeAttributeDeclaration(
|
||
|
"TemplateContainer",
|
||
|
new CodeAttributeArgument[] {
|
||
|
new CodeAttributeArgument(new CodeTypeOfExpression(containerType))});
|
||
|
|
||
|
attrDeclarations.Add(templateContainerAttrDeclaration);
|
||
|
|
||
|
// If the template control is in a template, assume its container allows multiple instances,
|
||
|
// otherwise set the TemplateInstanceAttribute
|
||
|
if (fInTemplate == false) {
|
||
|
CodeAttributeDeclaration templateInstanceAttrDeclaration = new CodeAttributeDeclaration(
|
||
|
"TemplateInstanceAttribute",
|
||
|
new CodeAttributeArgument[] {
|
||
|
new CodeAttributeArgument(
|
||
|
new CodeFieldReferenceExpression(new CodeTypeReferenceExpression(typeof(TemplateInstance)),
|
||
|
"Single"))});
|
||
|
attrDeclarations.Add(templateInstanceAttrDeclaration);
|
||
|
}
|
||
|
|
||
|
BuildFieldAndAccessorProperty(autoTemplateName, fieldName, typeof(ITemplate), false /*fStatic*/, attrDeclarations);
|
||
|
CodeExpression templateFieldRef = new CodeFieldReferenceExpression(
|
||
|
new CodeThisReferenceExpression(), fieldName);
|
||
|
|
||
|
if (builder is System.Web.UI.WebControls.ContentPlaceHolderBuilder) {
|
||
|
// We generate something like this:
|
||
|
// if (this.ContentTemplates != null) {
|
||
|
// this.__Template_TestTemplate = (ITemplate)this.ContentTemplates[{templateName}];
|
||
|
// }
|
||
|
CodePropertyReferenceExpression contentTemplatesFieldRef =
|
||
|
new CodePropertyReferenceExpression(new CodeThisReferenceExpression(), "ContentTemplates");
|
||
|
|
||
|
CodeAssignStatement setStatement = new CodeAssignStatement();
|
||
|
setStatement.Left = templateFieldRef;
|
||
|
setStatement.Right = new CodeCastExpression(typeof(ITemplate), new CodeIndexerExpression(contentTemplatesFieldRef,
|
||
|
new CodePrimitiveExpression(templateName)));
|
||
|
|
||
|
CodeConditionStatement contentTemplateIfStmt = new CodeConditionStatement();
|
||
|
|
||
|
CodeBinaryOperatorExpression contentNullCheckExpr = new CodeBinaryOperatorExpression(contentTemplatesFieldRef, CodeBinaryOperatorType.IdentityInequality, new CodePrimitiveExpression(null));
|
||
|
|
||
|
CodeMethodInvokeExpression removeExpr = new CodeMethodInvokeExpression(contentTemplatesFieldRef, "Remove");
|
||
|
removeExpr.Parameters.Add(new CodePrimitiveExpression(templateName));
|
||
|
|
||
|
contentTemplateIfStmt.Condition = contentNullCheckExpr;
|
||
|
contentTemplateIfStmt.TrueStatements.Add(setStatement);
|
||
|
|
||
|
statements.Add(contentTemplateIfStmt);
|
||
|
}
|
||
|
|
||
|
// We generate something like this:
|
||
|
// if ((this.__Template_TestTemplate != null)) {
|
||
|
// // For 2.0:
|
||
|
// this.__Template_TestTemplate.InstantiateIn(__ctrl);
|
||
|
// // For 4.0, use a new method. This is for fixing Dev10 bug 776195.
|
||
|
// this.InstantiateInContentPlaceHolder(__ctrl, this.__Template_TestTemplate);
|
||
|
// }
|
||
|
// else {
|
||
|
// // normal sub control building code
|
||
|
// }
|
||
|
if (MultiTargetingUtil.IsTargetFramework40OrAbove) {
|
||
|
instantiateTemplateExpr = new CodeMethodInvokeExpression(new CodeThisReferenceExpression(), "InstantiateInContentPlaceHolder");
|
||
|
instantiateTemplateExpr.Parameters.Add(ctrlRefExpr);
|
||
|
instantiateTemplateExpr.Parameters.Add(templateFieldRef);
|
||
|
}
|
||
|
else {
|
||
|
instantiateTemplateExpr = new CodeMethodInvokeExpression(templateFieldRef, "InstantiateIn");
|
||
|
instantiateTemplateExpr.Parameters.Add(ctrlRefExpr);
|
||
|
}
|
||
|
|
||
|
templateIfStmt = new CodeConditionStatement();
|
||
|
templateIfStmt.Condition = new CodeBinaryOperatorExpression(templateFieldRef, CodeBinaryOperatorType.IdentityInequality, new CodePrimitiveExpression(null));
|
||
|
templateIfStmt.TrueStatements.Add(new CodeExpressionStatement(instantiateTemplateExpr));
|
||
|
buildSubControlBlock = templateIfStmt.FalseStatements;
|
||
|
statements.Add(templateIfStmt);
|
||
|
}
|
||
|
|
||
|
ICollection contentBuilderEntries = null;
|
||
|
if (builder is FileLevelPageControlBuilder) {
|
||
|
contentBuilderEntries = ((FileLevelPageControlBuilder)builder).ContentBuilderEntries;
|
||
|
|
||
|
if (contentBuilderEntries != null) {
|
||
|
CodeStatementCollection currentStmts;
|
||
|
CodeStatementCollection nextStmts = statements;
|
||
|
PropertyEntry previous = null;
|
||
|
|
||
|
foreach (TemplatePropertyEntry entry in contentBuilderEntries) {
|
||
|
System.Web.UI.WebControls.ContentBuilderInternal child =
|
||
|
(System.Web.UI.WebControls.ContentBuilderInternal)entry.Builder;
|
||
|
|
||
|
currentStmts = nextStmts;
|
||
|
|
||
|
HandleDeviceFilterConditional(ref previous, entry, statements, ref currentStmts, out nextStmts);
|
||
|
|
||
|
string controlName = child.ID;
|
||
|
string contentPlaceHolderID = child.ContentPlaceHolder;
|
||
|
|
||
|
CodeDelegateCreateExpression newDelegate = new CodeDelegateCreateExpression();
|
||
|
newDelegate.DelegateType = new CodeTypeReference(typeof(BuildTemplateMethod));
|
||
|
newDelegate.TargetObject = new CodeThisReferenceExpression();
|
||
|
newDelegate.MethodName = buildMethodPrefix + controlName;
|
||
|
|
||
|
// e.g. this.AddContentTemplate(contentPlaceHolderID, new CompiledTemplateBuilder(
|
||
|
// e.g. new BuildTemplateMethod(this.__BuildControl {{controlName}}));
|
||
|
CodeObjectCreateExpression cocExpr = new CodeObjectCreateExpression(typeof(CompiledTemplateBuilder));
|
||
|
cocExpr.Parameters.Add(newDelegate);
|
||
|
|
||
|
CodeMethodInvokeExpression cmiExpression = new CodeMethodInvokeExpression(new CodeThisReferenceExpression(), "AddContentTemplate");
|
||
|
cmiExpression.Parameters.Add(new CodePrimitiveExpression(contentPlaceHolderID));
|
||
|
cmiExpression.Parameters.Add(cocExpr);
|
||
|
|
||
|
CodeExpressionStatement ceStatement = new CodeExpressionStatement(cmiExpression);
|
||
|
ceStatement.LinePragma = CreateCodeLinePragma((ControlBuilder)child);
|
||
|
|
||
|
currentStmts.Add(ceStatement);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (builder is DataBoundLiteralControlBuilder) {
|
||
|
|
||
|
// If it's a DataBoundLiteralControl, build it by calling SetStaticString
|
||
|
// on all the static literal strings.
|
||
|
int i = -1;
|
||
|
foreach (object child in builder.SubBuilders) {
|
||
|
i++;
|
||
|
|
||
|
// Ignore it if it's null
|
||
|
if (child == null)
|
||
|
continue;
|
||
|
|
||
|
// Only deal with the strings here, which have even index
|
||
|
if (i % 2 == 1) {
|
||
|
Debug.Assert(child is CodeBlockBuilder, "child is CodeBlockBuilder");
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
string s = (string) child;
|
||
|
|
||
|
// e.g. __ctrl.SetStaticString(3, "literal string");
|
||
|
methCallExpression = new CodeMethodInvokeExpression(ctrlRefExpr, "SetStaticString");
|
||
|
methCallExpression.Parameters.Add(new CodePrimitiveExpression(i/2));
|
||
|
methCallExpression.Parameters.Add(new CodePrimitiveExpression(s));
|
||
|
statements.Add(new CodeExpressionStatement(methCallExpression));
|
||
|
}
|
||
|
}
|
||
|
// Process the children
|
||
|
else if (builder.SubBuilders != null) {
|
||
|
|
||
|
bool gotParserVariable = false;
|
||
|
int localVarIndex = 1;
|
||
|
|
||
|
foreach (object child in builder.SubBuilders) {
|
||
|
if (child is ControlBuilder && !(child is CodeBlockBuilder) && !(child is CodeStatementBuilder) && !(child is System.Web.UI.WebControls.ContentBuilderInternal)) {
|
||
|
ControlBuilder ctrlBuilder = (ControlBuilder) child;
|
||
|
|
||
|
if (fControlSkin) {
|
||
|
throw new HttpParseException(SR.GetString(SR.ControlSkin_cannot_contain_controls),
|
||
|
null,
|
||
|
builder.VirtualPath, null, builder.Line);
|
||
|
}
|
||
|
|
||
|
PartialCachingAttribute cacheAttrib = (PartialCachingAttribute)
|
||
|
TypeDescriptor.GetAttributes(ctrlBuilder.ControlType)[typeof(PartialCachingAttribute)];
|
||
|
|
||
|
methCallExpression = new CodeMethodInvokeExpression(new CodeThisReferenceExpression(),
|
||
|
buildMethodPrefix + ctrlBuilder.ID);
|
||
|
methCallStatement = new CodeExpressionStatement(methCallExpression);
|
||
|
|
||
|
if (cacheAttrib == null) {
|
||
|
string localVariableRef = _localVariableRef + (localVarIndex++).ToString(CultureInfo.InvariantCulture);
|
||
|
|
||
|
// Variable reference to the local control variable
|
||
|
CodeVariableReferenceExpression childCtrlRefExpr = new CodeVariableReferenceExpression(localVariableRef);
|
||
|
|
||
|
// e.g. {{controlTypeName}} ctrl5;
|
||
|
CodeTypeReference ctrlTypeReference =
|
||
|
CodeDomUtility.BuildGlobalCodeTypeReference(ctrlBuilder.ControlType);
|
||
|
buildSubControlBlock.Add(new CodeVariableDeclarationStatement(ctrlTypeReference, localVariableRef));
|
||
|
|
||
|
// e.g. ctrl5 = __BuildControl__control6();
|
||
|
CodeAssignStatement setCtl = new CodeAssignStatement(childCtrlRefExpr, methCallExpression);
|
||
|
setCtl.LinePragma = linePragma;
|
||
|
buildSubControlBlock.Add(setCtl);
|
||
|
|
||
|
// If there is no caching on the control, just create it and add it
|
||
|
// e.g. __parser.AddParsedSubObject(ctrl5);
|
||
|
BuildAddParsedSubObjectStatement(
|
||
|
buildSubControlBlock,
|
||
|
childCtrlRefExpr,
|
||
|
linePragma,
|
||
|
ctrlRefExpr,
|
||
|
ref gotParserVariable);
|
||
|
}
|
||
|
else {
|
||
|
string providerName = null;
|
||
|
// Only use the providerName parameter when targeting 4.0 and above
|
||
|
bool useProviderName = MultiTargetingUtil.IsTargetFramework40OrAbove;
|
||
|
if (useProviderName) {
|
||
|
providerName = cacheAttrib.ProviderName;
|
||
|
if (providerName == OutputCache.ASPNET_INTERNAL_PROVIDER_NAME) {
|
||
|
providerName = null;
|
||
|
}
|
||
|
}
|
||
|
// The control's output is getting cached. Call
|
||
|
// StaticPartialCachingControl.BuildCachedControl to do the work.
|
||
|
|
||
|
// e.g. StaticPartialCachingControl.BuildCachedControl(__ctrl, Request, "e4192e6d-cbe0-4df5-b516-682c10415590", __pca, new System.Web.UI.BuildMethod(this.__BuildControlt1));
|
||
|
CodeMethodInvokeExpression call = new CodeMethodInvokeExpression();
|
||
|
call.Method.TargetObject = new CodeTypeReferenceExpression(typeof(System.Web.UI.StaticPartialCachingControl));
|
||
|
call.Method.MethodName = "BuildCachedControl";
|
||
|
call.Parameters.Add(ctrlRefExpr);
|
||
|
call.Parameters.Add(new CodePrimitiveExpression(ctrlBuilder.ID));
|
||
|
|
||
|
// If the caching is shared, use the type of the control as the key
|
||
|
// otherwise, generate a guid
|
||
|
if (cacheAttrib.Shared) {
|
||
|
call.Parameters.Add(new CodePrimitiveExpression(
|
||
|
ctrlBuilder.ControlType.GetHashCode().ToString(CultureInfo.InvariantCulture)));
|
||
|
}
|
||
|
else
|
||
|
call.Parameters.Add(new CodePrimitiveExpression(Guid.NewGuid().ToString()));
|
||
|
call.Parameters.Add(new CodePrimitiveExpression(cacheAttrib.Duration));
|
||
|
call.Parameters.Add(new CodePrimitiveExpression(cacheAttrib.VaryByParams));
|
||
|
call.Parameters.Add(new CodePrimitiveExpression(cacheAttrib.VaryByControls));
|
||
|
call.Parameters.Add(new CodePrimitiveExpression(cacheAttrib.VaryByCustom));
|
||
|
call.Parameters.Add(new CodePrimitiveExpression(cacheAttrib.SqlDependency));
|
||
|
CodeDelegateCreateExpression newDelegate = new CodeDelegateCreateExpression();
|
||
|
newDelegate.DelegateType = new CodeTypeReference(typeof(BuildMethod));
|
||
|
newDelegate.TargetObject = new CodeThisReferenceExpression();
|
||
|
newDelegate.MethodName = buildMethodPrefix + ctrlBuilder.ID;
|
||
|
call.Parameters.Add(newDelegate);
|
||
|
if (useProviderName) {
|
||
|
call.Parameters.Add(new CodePrimitiveExpression(providerName));
|
||
|
}
|
||
|
buildSubControlBlock.Add(new CodeExpressionStatement(call));
|
||
|
}
|
||
|
|
||
|
}
|
||
|
else if (child is string && !builder.HasAspCode) {
|
||
|
|
||
|
// VSWhidbey 276806: if the control cares about the inner text (builder does not allow whitespace literals)
|
||
|
// the inner literals should be added to the control.
|
||
|
if (!fControlSkin || !builder.AllowWhitespaceLiterals()) {
|
||
|
string s = (string) child;
|
||
|
CodeExpression expr;
|
||
|
|
||
|
if (!UseResourceLiteralString(s)) {
|
||
|
// e.g. ((IParserAccessor)__ctrl).AddParsedSubObject(new LiteralControl({{@QuoteCString(text)}}));
|
||
|
newExpr = new CodeObjectCreateExpression(typeof(LiteralControl));
|
||
|
newExpr.Parameters.Add(new CodePrimitiveExpression(s));
|
||
|
expr = newExpr;
|
||
|
}
|
||
|
else {
|
||
|
// Add the string to the resource builder, and get back its offset/size
|
||
|
int offset, size;
|
||
|
bool fAsciiOnly;
|
||
|
_stringResourceBuilder.AddString(s, out offset, out size, out fAsciiOnly);
|
||
|
|
||
|
methCallExpression = new CodeMethodInvokeExpression();
|
||
|
methCallExpression.Method.TargetObject = new CodeThisReferenceExpression();
|
||
|
methCallExpression.Method.MethodName = "CreateResourceBasedLiteralControl";
|
||
|
methCallExpression.Parameters.Add(new CodePrimitiveExpression(offset));
|
||
|
methCallExpression.Parameters.Add(new CodePrimitiveExpression(size));
|
||
|
methCallExpression.Parameters.Add(new CodePrimitiveExpression(fAsciiOnly));
|
||
|
expr = methCallExpression;
|
||
|
}
|
||
|
|
||
|
BuildAddParsedSubObjectStatement(buildSubControlBlock, expr, linePragma, ctrlRefExpr, ref gotParserVariable);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Process the complex attributes
|
||
|
if (builder.ComplexPropertyEntries.Count > 0) {
|
||
|
|
||
|
// Used to deal with the device filter conditionals
|
||
|
CodeStatementCollection currentStmts;
|
||
|
CodeStatementCollection nextStmts = statements;
|
||
|
PropertyEntry previous = null;
|
||
|
int localVarIndex = 1;
|
||
|
String localVariableRef = null;
|
||
|
|
||
|
foreach (ComplexPropertyEntry pseSub in builder.ComplexPropertyEntries) {
|
||
|
|
||
|
currentStmts = nextStmts;
|
||
|
|
||
|
HandleDeviceFilterConditional(ref previous, pseSub, statements, ref currentStmts, out nextStmts);
|
||
|
|
||
|
if (pseSub.Builder is StringPropertyBuilder) {
|
||
|
// If it's a string inner property, treat it like a simple property
|
||
|
CodeExpression leftExpr, rightExpr = null;
|
||
|
|
||
|
// __ctrl.{{_name}}
|
||
|
// In case of a string property, there should only be one property name (unlike complex properties)
|
||
|
Debug.Assert(pseSub.Name.IndexOf('.') < 0, "pseSub._name.IndexOf('.') < 0");
|
||
|
leftExpr = new CodePropertyReferenceExpression(ctrlRefExpr, pseSub.Name);
|
||
|
|
||
|
// We need to call BuildStringPropertyExpression so any additional processing can be done
|
||
|
rightExpr = BuildStringPropertyExpression(pseSub);
|
||
|
|
||
|
// Now that we have both side, add the assignment
|
||
|
CodeAssignStatement setStatment = new CodeAssignStatement(leftExpr, rightExpr);
|
||
|
setStatment.LinePragma = linePragma;
|
||
|
currentStmts.Add(setStatment);
|
||
|
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
if (pseSub.ReadOnly) {
|
||
|
|
||
|
if (fControlSkin && pseSub.Builder != null && pseSub.Builder is CollectionBuilder &&
|
||
|
pseSub.Builder.ComplexPropertyEntries.Count > 0) {
|
||
|
|
||
|
// If it's a collection on a control theme and the themed collection is not empty, clear it first.
|
||
|
// e.g. __ctrl.{{pse_name}}.Clear();
|
||
|
|
||
|
BindingFlags bindingFlags = BindingFlags.Public | BindingFlags.Instance;
|
||
|
// Look for the "Clear" method on the collection.
|
||
|
if (pseSub.Type.GetMethod("Clear", bindingFlags) != null) {
|
||
|
CodeMethodReferenceExpression refExpr = new CodeMethodReferenceExpression();
|
||
|
refExpr.MethodName = "Clear";
|
||
|
refExpr.TargetObject = new CodePropertyReferenceExpression(ctrlRefExpr, pseSub.Name);
|
||
|
CodeMethodInvokeExpression invokeClearExpr = new CodeMethodInvokeExpression();
|
||
|
invokeClearExpr.Method = refExpr;
|
||
|
currentStmts.Add(invokeClearExpr);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// If it's a readonly prop, pass it as a parameter to the
|
||
|
// build method.
|
||
|
// e.g. __BuildControl {{controlName}}(__ctrl.{{pse._name}});
|
||
|
methCallExpression = new CodeMethodInvokeExpression(new CodeThisReferenceExpression(),
|
||
|
buildMethodPrefix + pseSub.Builder.ID);
|
||
|
methCallExpression.Parameters.Add(new CodePropertyReferenceExpression(ctrlRefExpr, pseSub.Name));
|
||
|
methCallStatement = new CodeExpressionStatement(methCallExpression);
|
||
|
methCallStatement.LinePragma = linePragma;
|
||
|
currentStmts.Add(methCallStatement);
|
||
|
}
|
||
|
else {
|
||
|
localVariableRef = _localVariableRef + (localVarIndex++).ToString(CultureInfo.InvariantCulture);
|
||
|
|
||
|
// e.g. {{controlTypeName}} ctrl4;
|
||
|
CodeTypeReference ctrlTypeReference =
|
||
|
CodeDomUtility.BuildGlobalCodeTypeReference(pseSub.Builder.ControlType);
|
||
|
currentStmts.Add(new CodeVariableDeclarationStatement(ctrlTypeReference, localVariableRef));
|
||
|
|
||
|
// Variable reference to the local control variable.
|
||
|
CodeVariableReferenceExpression childCtrlRefExpr = new CodeVariableReferenceExpression(localVariableRef);
|
||
|
|
||
|
methCallExpression = new CodeMethodInvokeExpression(new CodeThisReferenceExpression(),
|
||
|
buildMethodPrefix + pseSub.Builder.ID);
|
||
|
methCallStatement = new CodeExpressionStatement(methCallExpression);
|
||
|
|
||
|
// e.g. ctrl4 = __BuildControl {{controlName}}();
|
||
|
CodeAssignStatement setCtl = new CodeAssignStatement(childCtrlRefExpr, methCallExpression);
|
||
|
setCtl.LinePragma = linePragma;
|
||
|
currentStmts.Add(setCtl);
|
||
|
|
||
|
if (pseSub.IsCollectionItem) {
|
||
|
// e.g. __ctrl.Add(ctrl4);
|
||
|
methCallExpression = new CodeMethodInvokeExpression(ctrlRefExpr, "Add");
|
||
|
methCallStatement = new CodeExpressionStatement(methCallExpression);
|
||
|
methCallStatement.LinePragma = linePragma;
|
||
|
currentStmts.Add(methCallStatement);
|
||
|
methCallExpression.Parameters.Add(childCtrlRefExpr);
|
||
|
}
|
||
|
else {
|
||
|
// e.g. __ctrl.{{pse._name}} = {{controlName}};
|
||
|
CodeAssignStatement set = new CodeAssignStatement();
|
||
|
set.Left = new CodePropertyReferenceExpression(ctrlRefExpr, pseSub.Name);
|
||
|
set.Right = childCtrlRefExpr;
|
||
|
set.LinePragma = linePragma;
|
||
|
currentStmts.Add(set);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// If there are bound properties, hook up the binding method
|
||
|
if (builder.BoundPropertyEntries.Count > 0) {
|
||
|
|
||
|
bool isBindableTemplateBuilder = builder is BindableTemplateBuilder;
|
||
|
bool hasDataBindingEntry = false;
|
||
|
|
||
|
// Used to deal with the device filter conditionals
|
||
|
CodeStatementCollection currentStmts;
|
||
|
CodeStatementCollection methodStatements = statements;
|
||
|
CodeStatementCollection nextStmts = statements;
|
||
|
PropertyEntry previous = null;
|
||
|
|
||
|
bool hasTempObject = false;
|
||
|
|
||
|
foreach (BoundPropertyEntry entry in builder.BoundPropertyEntries) {
|
||
|
|
||
|
// Skip two-way entries if it's a BindableTemplateBuilder or the two-way entry has no setter
|
||
|
if (entry.TwoWayBound && (isBindableTemplateBuilder || entry.ReadOnlyProperty))
|
||
|
continue;
|
||
|
|
||
|
if (entry.IsDataBindingEntry) {
|
||
|
hasDataBindingEntry = true;
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
currentStmts = nextStmts;
|
||
|
|
||
|
HandleDeviceFilterConditional(ref previous, entry, statements, ref currentStmts, out nextStmts);
|
||
|
|
||
|
ExpressionBuilder eb = entry.ExpressionBuilder;
|
||
|
Debug.Assert(eb != null, "Did not expect null expression builder");
|
||
|
eb.BuildExpression(entry, builder, ctrlRefExpr, methodStatements, currentStmts, null, ref hasTempObject);
|
||
|
}
|
||
|
|
||
|
if (hasDataBindingEntry) {
|
||
|
|
||
|
EventInfo eventInfo = DataBindingExpressionBuilder.Event;
|
||
|
|
||
|
// __ctrl.{EventName} += new EventHandler(this.{{bindingMethod}})
|
||
|
CodeDelegateCreateExpression newDelegate = new CodeDelegateCreateExpression();
|
||
|
CodeAttachEventStatement attachEvent = new CodeAttachEventStatement(ctrlRefExpr, eventInfo.Name, newDelegate);
|
||
|
attachEvent.LinePragma = linePragma;
|
||
|
newDelegate.DelegateType = new CodeTypeReference(typeof(EventHandler));
|
||
|
newDelegate.TargetObject = new CodeThisReferenceExpression();
|
||
|
newDelegate.MethodName = GetExpressionBuilderMethodName(eventInfo.Name, builder);
|
||
|
statements.Add(attachEvent);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (builder is DataBoundLiteralControlBuilder) {
|
||
|
|
||
|
// __ctrl.DataBinding += new EventHandler(this.{{bindingMethod}})
|
||
|
CodeDelegateCreateExpression newDelegate = new CodeDelegateCreateExpression();
|
||
|
CodeAttachEventStatement attachEvent = new CodeAttachEventStatement(ctrlRefExpr, "DataBinding", newDelegate);
|
||
|
attachEvent.LinePragma = linePragma;
|
||
|
newDelegate.DelegateType = new CodeTypeReference(typeof(EventHandler));
|
||
|
newDelegate.TargetObject = new CodeThisReferenceExpression();
|
||
|
newDelegate.MethodName = BindingMethodName(builder);
|
||
|
statements.Add(attachEvent);
|
||
|
}
|
||
|
|
||
|
// If there is any ASP code, set the render method delegate
|
||
|
if (builder.HasAspCode && !fControlSkin) {
|
||
|
// e.g. __ctrl.SetRenderMethodDelegate(new RenderMethod(this.__Render {{controlName}}));
|
||
|
CodeDelegateCreateExpression newDelegate = new CodeDelegateCreateExpression();
|
||
|
newDelegate.DelegateType = new CodeTypeReference(typeof(RenderMethod));
|
||
|
newDelegate.TargetObject = new CodeThisReferenceExpression();
|
||
|
newDelegate.MethodName = "__Render" + builder.ID;
|
||
|
|
||
|
methCallExpression = new CodeMethodInvokeExpression(ctrlRefExpr, "SetRenderMethodDelegate");
|
||
|
methCallExpression.Parameters.Add(newDelegate);
|
||
|
methCallStatement = new CodeExpressionStatement(methCallExpression);
|
||
|
|
||
|
// VSWhidbey 579101
|
||
|
// If this is a contentPlaceHolder, we need to check if there is any content defined.
|
||
|
// We set the render method only when there is no contentTemplate defined.
|
||
|
// if ((this.__Template_TestTemplate == null)) {
|
||
|
// __ctrl.SetRenderMethodDelegate(new RenderMethod(this.__Render {{controlName}}));
|
||
|
// }
|
||
|
if (builder is System.Web.UI.WebControls.ContentPlaceHolderBuilder) {
|
||
|
string templateName = ((System.Web.UI.WebControls.ContentPlaceHolderBuilder)builder).Name;
|
||
|
autoTemplateName = MasterPageControlBuilder.AutoTemplatePrefix + templateName;
|
||
|
string fieldName = "__" + autoTemplateName;
|
||
|
CodeExpression templateFieldRef = new CodeFieldReferenceExpression(new CodeThisReferenceExpression(), fieldName);
|
||
|
templateIfStmt = new CodeConditionStatement();
|
||
|
templateIfStmt.Condition = new CodeBinaryOperatorExpression(templateFieldRef, CodeBinaryOperatorType.IdentityEquality, new CodePrimitiveExpression(null));
|
||
|
templateIfStmt.TrueStatements.Add(methCallStatement);
|
||
|
statements.Add(templateIfStmt);
|
||
|
}
|
||
|
else {
|
||
|
statements.Add(methCallStatement);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Process the events
|
||
|
if (builder.EventEntries.Count > 0) {
|
||
|
|
||
|
foreach (EventEntry eventEntry in builder.EventEntries) {
|
||
|
|
||
|
// Attach the event. Detach it first to avoid duplicates (see ASURT 42603),
|
||
|
// but only if there is codebehind
|
||
|
//
|
||
|
|
||
|
|
||
|
// e.g. __ctrl.ServerClick -= new System.EventHandler(this.buttonClicked);
|
||
|
// e.g. __ctrl.ServerClick += new System.EventHandler(this.buttonClicked);
|
||
|
CodeDelegateCreateExpression newDelegate = new CodeDelegateCreateExpression();
|
||
|
newDelegate.DelegateType = new CodeTypeReference(eventEntry.HandlerType);
|
||
|
newDelegate.TargetObject = new CodeThisReferenceExpression();
|
||
|
newDelegate.MethodName = eventEntry.HandlerMethodName;
|
||
|
|
||
|
if (Parser.HasCodeBehind) {
|
||
|
CodeRemoveEventStatement detachEvent = new CodeRemoveEventStatement(ctrlRefExpr, eventEntry.Name, newDelegate);
|
||
|
detachEvent.LinePragma = linePragma;
|
||
|
statements.Add(detachEvent);
|
||
|
}
|
||
|
|
||
|
CodeAttachEventStatement attachEvent = new CodeAttachEventStatement(ctrlRefExpr, eventEntry.Name, newDelegate);
|
||
|
attachEvent.LinePragma = linePragma;
|
||
|
statements.Add(attachEvent);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// If a control field is declared, we need to return it at the end of the method.
|
||
|
if (fControlFieldDeclared)
|
||
|
statements.Add(new CodeMethodReturnStatement(ctrlRefExpr));
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Build the template's method to extract values from contained controls
|
||
|
*/
|
||
|
protected void BuildExtractMethod(ControlBuilder builder) {
|
||
|
BindableTemplateBuilder bindableTemplateBuilder = builder as BindableTemplateBuilder;
|
||
|
// This will get called if Bind is in a non-bindable template. We should just skip the Extract method.
|
||
|
if (bindableTemplateBuilder != null && bindableTemplateBuilder.HasTwoWayBoundProperties) {
|
||
|
// Get the name of the databinding method
|
||
|
string methodName = ExtractMethodName(builder);
|
||
|
const string tableVarName = "__table";
|
||
|
const string containerVarName = "__container";
|
||
|
|
||
|
// Same linePragma in the entire method
|
||
|
CodeLinePragma linePragma = CreateCodeLinePragma(builder);
|
||
|
|
||
|
CodeMemberMethod method = new CodeMemberMethod();
|
||
|
AddDebuggerNonUserCodeAttribute(method);
|
||
|
method.Name = methodName;
|
||
|
method.Attributes &= ~MemberAttributes.AccessMask;
|
||
|
method.Attributes |= MemberAttributes.Public;
|
||
|
method.ReturnType = new CodeTypeReference(typeof(IOrderedDictionary));
|
||
|
_sourceDataClass.Members.Add(method);
|
||
|
|
||
|
/// Variable declarations need to go at the top for CodeDom compliance.
|
||
|
CodeStatementCollection topLevelStatements = method.Statements;
|
||
|
CodeStatementCollection statements = new CodeStatementCollection();
|
||
|
|
||
|
// add a container control parameter
|
||
|
method.Parameters.Add(new CodeParameterDeclarationExpression(typeof(Control), containerVarName));
|
||
|
|
||
|
// OrderedDictionary table;
|
||
|
CodeVariableDeclarationStatement tableDecl = new CodeVariableDeclarationStatement(typeof(OrderedDictionary), tableVarName);
|
||
|
topLevelStatements.Add(tableDecl);
|
||
|
|
||
|
// table = new OrderedDictionary();
|
||
|
CodeObjectCreateExpression newTableExpression = new CodeObjectCreateExpression(typeof(OrderedDictionary));
|
||
|
CodeAssignStatement newTableAssign = new CodeAssignStatement(new CodeVariableReferenceExpression(tableVarName),
|
||
|
newTableExpression);
|
||
|
newTableAssign.LinePragma = linePragma;
|
||
|
statements.Add(newTableAssign);
|
||
|
|
||
|
BuildExtractStatementsRecursive(bindableTemplateBuilder.SubBuilders, statements, topLevelStatements, linePragma, tableVarName, containerVarName);
|
||
|
|
||
|
// return table;
|
||
|
CodeMethodReturnStatement returnStatement = new CodeMethodReturnStatement(new CodeVariableReferenceExpression(tableVarName));
|
||
|
statements.Add(returnStatement);
|
||
|
|
||
|
// add all the non-variable declaration statements to the bottom of the method
|
||
|
method.Statements.AddRange(statements);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
private void BuildExtractStatementsRecursive(ArrayList subBuilders, CodeStatementCollection statements, CodeStatementCollection topLevelStatements, CodeLinePragma linePragma, string tableVarName, string containerVarName) {
|
||
|
foreach (object subBuilderObject in subBuilders) {
|
||
|
ControlBuilder controlBuilder = subBuilderObject as ControlBuilder;
|
||
|
if (controlBuilder != null) {
|
||
|
// Used to deal with the device filter conditionals
|
||
|
CodeStatementCollection currentStatements = null;
|
||
|
CodeStatementCollection nextStatements = statements;
|
||
|
PropertyEntry previous = null;
|
||
|
string previousControlName = null;
|
||
|
bool newControl = true;
|
||
|
|
||
|
foreach (BoundPropertyEntry entry in controlBuilder.BoundPropertyEntries) {
|
||
|
|
||
|
// Skip all entries that are not two-way
|
||
|
if (!entry.TwoWayBound)
|
||
|
continue;
|
||
|
|
||
|
// Reset the "previous" Property Entry if we're not looking at the same control.
|
||
|
// If we don't do this, Two controls that have conditionals on the same named property will have
|
||
|
// their conditionals incorrectly merged.
|
||
|
if (String.Compare(previousControlName, entry.ControlID, StringComparison.Ordinal) != 0) {
|
||
|
previous = null;
|
||
|
newControl = true;
|
||
|
}
|
||
|
else {
|
||
|
newControl = false;
|
||
|
}
|
||
|
|
||
|
previousControlName = entry.ControlID;
|
||
|
currentStatements = nextStatements;
|
||
|
|
||
|
HandleDeviceFilterConditional(ref previous, entry, statements, ref currentStatements, out nextStatements);
|
||
|
|
||
|
// Only declare the variable if it hasn't already been declared by a previous filter
|
||
|
// or property binding on the same control.
|
||
|
if (newControl) {
|
||
|
// {{controlType}} {{controlID}};
|
||
|
// eg. TextBox t1;
|
||
|
CodeVariableDeclarationStatement controlDecl = new CodeVariableDeclarationStatement(entry.ControlType, entry.ControlID);
|
||
|
topLevelStatements.Add(controlDecl);
|
||
|
|
||
|
// {{controlID}} = ({{controlType}})container.FindControl("{{controlID}}");
|
||
|
// eg. t1 = (TextBox)container.FindControl("t1");
|
||
|
CodeMethodInvokeExpression findControlCallExpression = new CodeMethodInvokeExpression(
|
||
|
new CodeVariableReferenceExpression(containerVarName), "FindControl");
|
||
|
string findControlParameter = entry.ControlID;
|
||
|
findControlCallExpression.Parameters.Add(new CodePrimitiveExpression(findControlParameter));
|
||
|
CodeCastExpression castExpression = new CodeCastExpression(entry.ControlType, findControlCallExpression);
|
||
|
|
||
|
CodeAssignStatement findControlAssign = new CodeAssignStatement(new CodeVariableReferenceExpression(entry.ControlID),
|
||
|
castExpression);
|
||
|
findControlAssign.LinePragma = linePragma;
|
||
|
topLevelStatements.Add(findControlAssign);
|
||
|
}
|
||
|
|
||
|
// if ({{controlID}} != null)
|
||
|
// table["{{fieldName}}"] = {{controlID}}.{{propertyName}});
|
||
|
// eg. if (t1 != null)
|
||
|
// eg. table["field"] = t1.Text);
|
||
|
CodeConditionStatement ifStatement = new CodeConditionStatement();
|
||
|
CodeBinaryOperatorExpression ensureControlExpression = new CodeBinaryOperatorExpression();
|
||
|
ensureControlExpression.Operator = CodeBinaryOperatorType.IdentityInequality;
|
||
|
ensureControlExpression.Left = new CodeVariableReferenceExpression(entry.ControlID);
|
||
|
ensureControlExpression.Right = new CodePrimitiveExpression(null);
|
||
|
ifStatement.Condition = ensureControlExpression;
|
||
|
|
||
|
string fieldParameter = entry.FieldName;
|
||
|
CodeIndexerExpression tableIndexer = new CodeIndexerExpression(new CodeVariableReferenceExpression(tableVarName),
|
||
|
new CodePrimitiveExpression(fieldParameter));
|
||
|
|
||
|
// VJ# does not support automatic boxing of value types, so passing a simple type, say a bool, into a method
|
||
|
// expecting an object will give a compiler error. We are working around this issue by adding special code for
|
||
|
// VJ# that will cast the expression for boxing. When the VJ# team adds implicit boxing of value types, we
|
||
|
// should remove this code. VSWhidbey 269028
|
||
|
CodeExpression controlPropertyExpression = CodeDomUtility.BuildPropertyReferenceExpression(new CodeVariableReferenceExpression(entry.ControlID), entry.Name);
|
||
|
if (_usingVJSCompiler) {
|
||
|
controlPropertyExpression = CodeDomUtility.BuildJSharpCastExpression(entry.Type, controlPropertyExpression);
|
||
|
}
|
||
|
CodeAssignStatement tableIndexAssign = new CodeAssignStatement(tableIndexer, controlPropertyExpression);
|
||
|
|
||
|
ifStatement.TrueStatements.Add(tableIndexAssign);
|
||
|
ifStatement.LinePragma = linePragma;
|
||
|
currentStatements.Add(ifStatement);
|
||
|
}
|
||
|
|
||
|
if (controlBuilder.SubBuilders.Count > 0) {
|
||
|
BuildExtractStatementsRecursive(controlBuilder.SubBuilders, statements, topLevelStatements, linePragma, tableVarName, containerVarName);
|
||
|
}
|
||
|
|
||
|
// Dev10 bug 525267
|
||
|
// When a control defines a DefaultProperty in its ParseChildren attribute, its subBuilders are appended
|
||
|
// to the DefaultProperty's subbuilders, and the DefaultProperty itself is added
|
||
|
// as a ComplexProperty or a TemplateProperty (and not as a suBbuilder). Thus we
|
||
|
// also need to go through these properties as well.
|
||
|
ArrayList list = new ArrayList();
|
||
|
AddEntryBuildersToList(controlBuilder.ComplexPropertyEntries, list);
|
||
|
AddEntryBuildersToList(controlBuilder.TemplatePropertyEntries, list);
|
||
|
|
||
|
if (list.Count > 0) {
|
||
|
BuildExtractStatementsRecursive(list, statements, topLevelStatements, linePragma, tableVarName, containerVarName);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
private void AddEntryBuildersToList(ICollection entries, ArrayList list) {
|
||
|
if (entries == null || list == null) {
|
||
|
return;
|
||
|
}
|
||
|
foreach (BuilderPropertyEntry entry in entries) {
|
||
|
if (entry.Builder != null) {
|
||
|
TemplatePropertyEntry templatePropertyEntry = entry as TemplatePropertyEntry;
|
||
|
// Only add template entries that have TemplateInstance.Single
|
||
|
if (templatePropertyEntry != null && templatePropertyEntry.IsMultiple) {
|
||
|
continue;
|
||
|
}
|
||
|
list.Add(entry.Builder);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Build the member field's declaration for a control
|
||
|
*/
|
||
|
private void BuildFieldDeclaration(ControlBuilder builder) {
|
||
|
|
||
|
// Do not generate member field for content controls.
|
||
|
if (builder is System.Web.UI.WebControls.ContentBuilderInternal) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
bool hideExistingMember = false;
|
||
|
|
||
|
// If we're using a non-default base class
|
||
|
if (Parser.BaseType != null) {
|
||
|
// Check if it has a non-private field or property that has a name that
|
||
|
// matches the id of the control.
|
||
|
|
||
|
Type memberType = Util.GetNonPrivateFieldType(Parser.BaseType, builder.ID);
|
||
|
|
||
|
// Couldn't find a field, try a property (ASURT 45039)
|
||
|
//
|
||
|
if (memberType == null)
|
||
|
memberType = Util.GetNonPrivatePropertyType(Parser.BaseType, builder.ID);
|
||
|
|
||
|
if (memberType != null) {
|
||
|
|
||
|
if (!memberType.IsAssignableFrom(builder.ControlType)) {
|
||
|
|
||
|
if (!(typeof(Control)).IsAssignableFrom(memberType)) {
|
||
|
// If it's not a control, it's probably an unrelated member,
|
||
|
// and we should just hide it (VSWhidbey 217135)
|
||
|
hideExistingMember = true;
|
||
|
}
|
||
|
else {
|
||
|
throw new HttpParseException(SR.GetString(SR.Base_class_field_with_type_different_from_type_of_control,
|
||
|
builder.ID, memberType.FullName, builder.ControlType.FullName), null,
|
||
|
builder.VirtualPath, null, builder.Line);
|
||
|
}
|
||
|
}
|
||
|
else {
|
||
|
// Don't build the declaration, since the base class already declares it
|
||
|
return;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Add the field. Make it protected if the ID was declared, and private if it was generated
|
||
|
CodeMemberField field = new CodeMemberField(CodeDomUtility.BuildGlobalCodeTypeReference(
|
||
|
builder.DeclareType), builder.ID);
|
||
|
field.Attributes &= ~MemberAttributes.AccessMask;
|
||
|
|
||
|
// If we need to hide an existing member, use 'new' (VSWhidbey 217135)
|
||
|
if (hideExistingMember)
|
||
|
field.Attributes |= MemberAttributes.New;
|
||
|
|
||
|
field.LinePragma = CreateCodeLinePragma(builder);
|
||
|
|
||
|
field.Attributes |= MemberAttributes.Family;
|
||
|
|
||
|
// Set WithEvents in the UserData, so that the field will be
|
||
|
// declared as WithEvents in VB (VSWhidbey 156623).
|
||
|
// But only do this if it's a Control, otherwise it may not have
|
||
|
// any events (VSWhidbey 283274).
|
||
|
if (typeof(Control).IsAssignableFrom(builder.DeclareType)) {
|
||
|
field.UserData["WithEvents"] = true;
|
||
|
}
|
||
|
|
||
|
_intermediateClass.Members.Add(field);
|
||
|
}
|
||
|
|
||
|
private string GetExpressionBuilderMethodName(string eventName, ControlBuilder builder) {
|
||
|
return "__" + eventName + builder.ID;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Return the name of a databinding method
|
||
|
*/
|
||
|
private string BindingMethodName(ControlBuilder builder) {
|
||
|
return "__DataBind" + builder.ID;
|
||
|
}
|
||
|
|
||
|
protected CodeMemberMethod BuildPropertyBindingMethod(ControlBuilder builder, bool fControlSkin) {
|
||
|
// VSWhidbey 275175: Create the tempObjectVariable "__o" only when it's used.
|
||
|
bool tempObjectVariableDeclared = false;
|
||
|
|
||
|
if (builder is DataBoundLiteralControlBuilder) {
|
||
|
// Get the name of the databinding method
|
||
|
string methodName = BindingMethodName(builder);
|
||
|
|
||
|
// Same linePragma in the entire method
|
||
|
CodeLinePragma linePragma = CreateCodeLinePragma(builder);
|
||
|
|
||
|
CodeMemberMethod method = new CodeMemberMethod();
|
||
|
method.Name = methodName;
|
||
|
method.Attributes &= ~MemberAttributes.AccessMask;
|
||
|
method.Attributes |= MemberAttributes.Public;
|
||
|
|
||
|
method.Parameters.Add(new CodeParameterDeclarationExpression(typeof(object), "sender"));
|
||
|
method.Parameters.Add(new CodeParameterDeclarationExpression(typeof(EventArgs), "e"));
|
||
|
|
||
|
CodeStatementCollection topMethodStatements = new CodeStatementCollection();
|
||
|
CodeStatementCollection otherMethodStatements = new CodeStatementCollection();
|
||
|
|
||
|
// {{controlType}} target;
|
||
|
CodeVariableDeclarationStatement targetDecl = new CodeVariableDeclarationStatement(builder.ControlType, "target");
|
||
|
Type bindingContainerType = builder.BindingContainerType;
|
||
|
|
||
|
CodeVariableDeclarationStatement containerDecl = new CodeVariableDeclarationStatement(bindingContainerType, "Container");
|
||
|
|
||
|
topMethodStatements.Add(containerDecl);
|
||
|
topMethodStatements.Add(targetDecl);
|
||
|
|
||
|
// target = ({{controlType}}) sender;
|
||
|
CodeAssignStatement setTarget = new CodeAssignStatement(new CodeVariableReferenceExpression(targetDecl.Name),
|
||
|
new CodeCastExpression(builder.ControlType,
|
||
|
new CodeArgumentReferenceExpression("sender")));
|
||
|
setTarget.LinePragma = linePragma;
|
||
|
otherMethodStatements.Add(setTarget);
|
||
|
|
||
|
// {{containerType}} Container = ({{containerType}}) target.BindingContainer;
|
||
|
CodeAssignStatement setContainer = new CodeAssignStatement(new CodeVariableReferenceExpression(containerDecl.Name),
|
||
|
new CodeCastExpression(bindingContainerType,
|
||
|
new CodePropertyReferenceExpression(new CodeVariableReferenceExpression("target"),
|
||
|
"BindingContainer")));
|
||
|
setContainer.LinePragma = linePragma;
|
||
|
otherMethodStatements.Add(setContainer);
|
||
|
|
||
|
DataBindingExpressionBuilder.GenerateItemTypeExpressions(builder, topMethodStatements, otherMethodStatements, linePragma, "Item");
|
||
|
//Generate code for BindItem as well at design time in addition to Item for intellisense.
|
||
|
//When you are in designer and as you type to set an attribute value to a data binding expression, since the control's end tag is not yet
|
||
|
//typed in, we will still have a DataBoundLiteralControl (instead of a ControlBuilder) in that scenario and the below code takes care of that scenario.
|
||
|
if (_designerMode) {
|
||
|
DataBindingExpressionBuilder.GenerateItemTypeExpressions(builder, topMethodStatements, otherMethodStatements, linePragma, "BindItem");
|
||
|
}
|
||
|
|
||
|
// If it's a DataBoundLiteralControl, call SetDataBoundString for each
|
||
|
// of the databinding expressions
|
||
|
int i = -1;
|
||
|
foreach (object child in builder.SubBuilders) {
|
||
|
i++;
|
||
|
|
||
|
// Ignore it if it's null
|
||
|
if (child == null)
|
||
|
continue;
|
||
|
|
||
|
// Only deal with the databinding expressions here, which have odd index
|
||
|
if (i % 2 == 0) {
|
||
|
Debug.Assert(child is string, "child is string");
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
CodeBlockBuilder codeBlock = (CodeBlockBuilder) child;
|
||
|
Debug.Assert(codeBlock.BlockType == CodeBlockType.DataBinding);
|
||
|
|
||
|
// In designer mode, generate a much simpler assignment to make
|
||
|
// the code simpler (since it doesn't actually need to run).
|
||
|
if (_designerMode) {
|
||
|
tempObjectVariableDeclared = GenerateSimpleAssignmentAtDesignTime(tempObjectVariableDeclared, topMethodStatements, otherMethodStatements, codeBlock.Content, CreateCodeLinePragma(codeBlock));
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
CodeExpression expr = new CodeSnippetExpression(codeBlock.Content.Trim());
|
||
|
if (codeBlock.IsEncoded) {
|
||
|
// HttpUtility.HtmlEncode({{codeExpr}}));
|
||
|
expr = new CodeMethodInvokeExpression(
|
||
|
new CodeMethodReferenceExpression(
|
||
|
new CodeTypeReferenceExpression(typeof(HttpUtility)),
|
||
|
"HtmlEncode"),
|
||
|
expr);
|
||
|
}
|
||
|
else {
|
||
|
// System.Convert.ToString({{codeExpr}});
|
||
|
expr = CodeDomUtility.GenerateConvertToString(expr);
|
||
|
}
|
||
|
|
||
|
// target.SetDataBoundString(3, {{codeExpr}}); {{codeExpr}} is one of the above in if-else depending on IsEncoded property.
|
||
|
CodeMethodInvokeExpression methCallExpression = new CodeMethodInvokeExpression(
|
||
|
new CodeVariableReferenceExpression("target"), "SetDataBoundString");
|
||
|
methCallExpression.Parameters.Add(new CodePrimitiveExpression(i/2));
|
||
|
methCallExpression.Parameters.Add(expr);
|
||
|
|
||
|
CodeStatement setDataBoundStringCall = new CodeExpressionStatement(methCallExpression);
|
||
|
setDataBoundStringCall.LinePragma = CreateCodeLinePragma(codeBlock);
|
||
|
otherMethodStatements.Add(setDataBoundStringCall);
|
||
|
}
|
||
|
|
||
|
foreach (CodeStatement stmt in topMethodStatements) {
|
||
|
method.Statements.Add(stmt);
|
||
|
}
|
||
|
foreach (CodeStatement stmt in otherMethodStatements) {
|
||
|
method.Statements.Add(stmt);
|
||
|
}
|
||
|
_sourceDataClass.Members.Add(method);
|
||
|
return method;
|
||
|
}
|
||
|
else {
|
||
|
|
||
|
EventInfo eventInfo = DataBindingExpressionBuilder.Event;
|
||
|
|
||
|
// Same linePragma in the entire method
|
||
|
CodeLinePragma linePragma = CreateCodeLinePragma(builder);
|
||
|
|
||
|
CodeMemberMethod method = null;
|
||
|
CodeStatementCollection topStatements = null;
|
||
|
CodeStatementCollection otherStatements = null;
|
||
|
|
||
|
// Used to deal with the device filter conditionals
|
||
|
CodeStatementCollection currentStmts;
|
||
|
CodeStatementCollection nextStmts = null;
|
||
|
PropertyEntry previous = null;
|
||
|
|
||
|
bool isBindableTemplateBuilder = builder is BindableTemplateBuilder;
|
||
|
bool firstEntry = true;
|
||
|
|
||
|
bool hasTempObject = false;
|
||
|
|
||
|
foreach (BoundPropertyEntry entry in builder.BoundPropertyEntries) {
|
||
|
// Skip two-way entries if it's a BindableTemplateBuilder or the two way entry is read only
|
||
|
if (entry.TwoWayBound && (isBindableTemplateBuilder || entry.ReadOnlyProperty))
|
||
|
continue;
|
||
|
|
||
|
// We only care about databinding entries here
|
||
|
if (!entry.IsDataBindingEntry)
|
||
|
continue;
|
||
|
|
||
|
if (firstEntry) {
|
||
|
firstEntry = false;
|
||
|
|
||
|
method = new CodeMemberMethod();
|
||
|
topStatements = new CodeStatementCollection();
|
||
|
otherStatements = new CodeStatementCollection();
|
||
|
|
||
|
// Get the name of the databinding method
|
||
|
string methodName = GetExpressionBuilderMethodName(eventInfo.Name, builder);
|
||
|
method.Name = methodName;
|
||
|
method.Attributes &= ~MemberAttributes.AccessMask;
|
||
|
method.Attributes |= MemberAttributes.Public;
|
||
|
|
||
|
if (_designerMode) {
|
||
|
ApplyEditorBrowsableCustomAttribute(method);
|
||
|
}
|
||
|
|
||
|
Type eventHandlerType = eventInfo.EventHandlerType;
|
||
|
MethodInfo mi = eventHandlerType.GetMethod("Invoke");
|
||
|
ParameterInfo[] paramInfos = mi.GetParameters();
|
||
|
foreach (ParameterInfo pi in paramInfos) {
|
||
|
method.Parameters.Add(new CodeParameterDeclarationExpression(pi.ParameterType, pi.Name));
|
||
|
}
|
||
|
|
||
|
nextStmts = otherStatements;
|
||
|
|
||
|
DataBindingExpressionBuilder.BuildExpressionSetup(builder, topStatements, otherStatements, linePragma, entry.TwoWayBound, _designerMode);
|
||
|
|
||
|
_sourceDataClass.Members.Add(method);
|
||
|
}
|
||
|
|
||
|
currentStmts = nextStmts;
|
||
|
|
||
|
HandleDeviceFilterConditional(ref previous, entry, otherStatements, ref currentStmts, out nextStmts);
|
||
|
|
||
|
// In designer mode, generate a much simpler assignment to make
|
||
|
// the code simpler (since it doesn't actually need to run).
|
||
|
if (_designerMode) {
|
||
|
int generatedColumn = tempObjectVariable.Length + BaseCodeDomTreeGenerator.GetGeneratedColumnOffset(_codeDomProvider);
|
||
|
CodeLinePragma codeLinePragma = CreateCodeLinePragma(virtualPath: builder.PageVirtualPath, lineNumber: entry.Line, column: entry.Column, generatedColumn: generatedColumn, codeLength: entry.Expression.Length);
|
||
|
tempObjectVariableDeclared = GenerateSimpleAssignmentAtDesignTime(tempObjectVariableDeclared, topStatements, otherStatements, entry.Expression, codeLinePragma);
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
if (entry.TwoWayBound) {
|
||
|
Debug.Assert(!entry.ReadOnlyProperty, "We should not attempt to build a data binding handler if the two way entry is read only.");
|
||
|
Debug.Assert(!entry.UseSetAttribute, "Two-way binding is not supported on expandos - this should have been prevented in ControlBuilder");
|
||
|
DataBindingExpressionBuilder.BuildEvalExpression(entry.FieldName, entry.FormatString,
|
||
|
entry.Name, entry.Type, builder, topStatements, currentStmts, linePragma, entry.IsEncoded, ref hasTempObject);
|
||
|
}
|
||
|
else {
|
||
|
DataBindingExpressionBuilder.BuildExpressionStatic(entry, builder, null, topStatements, currentStmts, linePragma, entry.IsEncoded, ref hasTempObject);
|
||
|
}
|
||
|
}
|
||
|
if (topStatements != null) {
|
||
|
foreach (CodeStatement stmt in topStatements) {
|
||
|
method.Statements.Add(stmt);
|
||
|
}
|
||
|
}
|
||
|
if (otherStatements != null) {
|
||
|
foreach (CodeStatement stmt in otherStatements) {
|
||
|
method.Statements.Add(stmt);
|
||
|
}
|
||
|
}
|
||
|
return method;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Build the data tree for a control's render method
|
||
|
*/
|
||
|
internal void BuildRenderMethod(ControlBuilder builder, bool fTemplate) {
|
||
|
|
||
|
CodeMemberMethod method = new CodeMemberMethod();
|
||
|
method.Attributes = MemberAttributes.Private | MemberAttributes.Final;
|
||
|
method.Name = "__Render" + builder.ID;
|
||
|
|
||
|
if (_designerMode) {
|
||
|
ApplyEditorBrowsableCustomAttribute(method);
|
||
|
}
|
||
|
|
||
|
method.Parameters.Add(new CodeParameterDeclarationExpression(typeof(HtmlTextWriter), renderMethodParameterName));
|
||
|
method.Parameters.Add(new CodeParameterDeclarationExpression(typeof(Control), "parameterContainer"));
|
||
|
|
||
|
_sourceDataClass.Members.Add(method);
|
||
|
|
||
|
// VSWhidbey 275175: Create the tempObjectVariable "__o" only when it's used.
|
||
|
bool tempObjectVariableDeclared = false;
|
||
|
|
||
|
// Process the children if any
|
||
|
if (builder.SubBuilders != null) {
|
||
|
IEnumerator en = builder.SubBuilders.GetEnumerator();
|
||
|
|
||
|
// Index that the control will have in its parent's Controls
|
||
|
// collection.
|
||
|
//
|
||
|
|
||
|
|
||
|
|
||
|
int controlIndex = 0;
|
||
|
|
||
|
for (int i=0; en.MoveNext(); i++) {
|
||
|
object child = en.Current;
|
||
|
|
||
|
CodeLinePragma linePragma = null;
|
||
|
|
||
|
if (child is ControlBuilder) {
|
||
|
linePragma = CreateCodeLinePragma((ControlBuilder)child);
|
||
|
}
|
||
|
|
||
|
if (child is string) {
|
||
|
if (_designerMode) continue;
|
||
|
|
||
|
AddOutputWriteStringStatement(method.Statements, (string)child);
|
||
|
}
|
||
|
else if (child is CodeBlockBuilder) {
|
||
|
CodeBlockBuilder codeBlockBuilder = (CodeBlockBuilder)child;
|
||
|
|
||
|
if (codeBlockBuilder.BlockType == CodeBlockType.Expression || codeBlockBuilder.BlockType == CodeBlockType.EncodedExpression) {
|
||
|
|
||
|
string codeExpression = codeBlockBuilder.Content;
|
||
|
|
||
|
// In designer mode, generate a much simpler assignment to make
|
||
|
// the code simpler (since it doesn't actually need to run).
|
||
|
if (_designerMode) {
|
||
|
tempObjectVariableDeclared = GenerateSimpleAssignmentAtDesignTime(tempObjectVariableDeclared, method.Statements, method.Statements, codeExpression, linePragma);
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
// The purpose of the following logic is to improve the debugging experience.
|
||
|
// Basically, we gain control on the formatting of the generated line
|
||
|
// that calls output.Write, in order to try to make the call line up
|
||
|
// with the <%= ... %> block. It's not always perfect, but it does a decent job
|
||
|
// and is always better than the v1 behavior.
|
||
|
|
||
|
// Get the Write() statement codedom tree
|
||
|
CodeStatement outputWrite = GetOutputWriteStatement(
|
||
|
new CodeSnippetExpression(codeExpression),
|
||
|
codeBlockBuilder.BlockType == CodeBlockType.EncodedExpression /*encode*/);
|
||
|
|
||
|
// Use codedom to generate the statement as a string in the target language
|
||
|
TextWriter w = new StringWriter(CultureInfo.InvariantCulture);
|
||
|
_codeDomProvider.GenerateCodeFromStatement(outputWrite, w, null /*CodeGeneratorOptions*/);
|
||
|
string outputWriteString = w.ToString();
|
||
|
|
||
|
// The '+3' is used to make sure the generated code is positioned properly to match
|
||
|
// the location of user code (due to the <%= %> separators).
|
||
|
outputWriteString = outputWriteString.PadLeft(
|
||
|
codeBlockBuilder.Column + codeExpression.Length + 3);
|
||
|
|
||
|
// We can then use this string as a snippet statement
|
||
|
CodeSnippetStatement lit = new CodeSnippetStatement(outputWriteString);
|
||
|
lit.LinePragma = linePragma;
|
||
|
method.Statements.Add(lit);
|
||
|
}
|
||
|
else {
|
||
|
// It's a <% ... %> block
|
||
|
Debug.Assert(codeBlockBuilder.BlockType == CodeBlockType.Code);
|
||
|
|
||
|
// Pad the code block so its generated offset matches the aspx
|
||
|
string code = codeBlockBuilder.Content;
|
||
|
code = code.PadLeft(code.Length + codeBlockBuilder.Column - 1);
|
||
|
|
||
|
CodeSnippetStatement lit = new CodeSnippetStatement(code);
|
||
|
lit.LinePragma = linePragma;
|
||
|
method.Statements.Add(lit);
|
||
|
}
|
||
|
}
|
||
|
else if (child is CodeStatementBuilder) {
|
||
|
|
||
|
if (_designerMode) continue;
|
||
|
|
||
|
CodeStatementBuilder statementBuilder = (CodeStatementBuilder)child;
|
||
|
|
||
|
CodeStatement statement = statementBuilder.BuildStatement(new CodeArgumentReferenceExpression(renderMethodParameterName));
|
||
|
|
||
|
method.Statements.Add(statement);
|
||
|
}
|
||
|
else if (child is ControlBuilder) {
|
||
|
|
||
|
if (_designerMode) continue;
|
||
|
|
||
|
// parameterContainer.Controls['controlIndex++'].RenderControl(output)
|
||
|
CodeMethodInvokeExpression methodInvoke = new CodeMethodInvokeExpression();
|
||
|
CodeExpressionStatement methodCall = new CodeExpressionStatement(methodInvoke);
|
||
|
methodInvoke.Method.TargetObject = new CodeIndexerExpression(new CodePropertyReferenceExpression(new CodeArgumentReferenceExpression("parameterContainer"),
|
||
|
"Controls"),
|
||
|
new CodeExpression[] {
|
||
|
new CodePrimitiveExpression(controlIndex++),
|
||
|
});
|
||
|
methodInvoke.Method.MethodName = "RenderControl";
|
||
|
|
||
|
// Don't generate a line pragma on the RenderControl call, as it degrades the
|
||
|
// debugging experience (VSWhidbey 482416)
|
||
|
|
||
|
methodInvoke.Parameters.Add(new CodeArgumentReferenceExpression(renderMethodParameterName));
|
||
|
method.Statements.Add(methodCall);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
private bool GenerateSimpleAssignmentAtDesignTime(bool tempObjectVariableDeclared, CodeStatementCollection topMethodStatements, CodeStatementCollection otherMethodStatements, string content, CodeLinePragma linePragma) {
|
||
|
// In designer mode, add an object variable used for simplified code expression generation
|
||
|
if (!tempObjectVariableDeclared) {
|
||
|
tempObjectVariableDeclared = true;
|
||
|
// object __o;
|
||
|
topMethodStatements.Add(new CodeVariableDeclarationStatement(
|
||
|
typeof(object), tempObjectVariable));
|
||
|
}
|
||
|
|
||
|
// e.g. __o = <user expression>;
|
||
|
CodeStatement simpleAssignment = new CodeAssignStatement(
|
||
|
new CodeVariableReferenceExpression(tempObjectVariable),
|
||
|
new CodeSnippetExpression(content));
|
||
|
simpleAssignment.LinePragma = linePragma;
|
||
|
otherMethodStatements.Add(simpleAssignment);
|
||
|
return tempObjectVariableDeclared;
|
||
|
}
|
||
|
|
||
|
protected virtual void BuildSourceDataTreeFromBuilder(ControlBuilder builder,
|
||
|
bool fInTemplate, bool topLevelControlInTemplate,
|
||
|
PropertyEntry pse) {
|
||
|
|
||
|
// Don't do anything for Code blocks
|
||
|
if (builder is CodeBlockBuilder || builder is CodeStatementBuilder)
|
||
|
return;
|
||
|
|
||
|
// Is the current builder for a template?
|
||
|
bool fTemplate = (builder is TemplateBuilder);
|
||
|
|
||
|
// For the control name in the compiled code, we use the
|
||
|
// ID if one is available (but don't use the ID inside a template)
|
||
|
// Otherwise, we generate a unique name.
|
||
|
if (builder.ID == null || fInTemplate) {
|
||
|
// Increase the control count to generate unique ID's
|
||
|
_controlCount++;
|
||
|
|
||
|
builder.ID = "__control" + _controlCount.ToString(NumberFormatInfo.InvariantInfo);
|
||
|
builder.IsGeneratedID = true;
|
||
|
}
|
||
|
|
||
|
// Process the children
|
||
|
if (builder.SubBuilders != null) {
|
||
|
foreach (object child in builder.SubBuilders) {
|
||
|
if (child is ControlBuilder) {
|
||
|
// Do not treat it as top level control in template if the control is at top-level of a file.
|
||
|
bool isTopLevelCtrlInTemplate =
|
||
|
fTemplate && typeof(Control).IsAssignableFrom(((ControlBuilder)child).ControlType) && !(builder is RootBuilder);
|
||
|
BuildSourceDataTreeFromBuilder((ControlBuilder)child, fInTemplate, isTopLevelCtrlInTemplate, null);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
foreach (TemplatePropertyEntry entry in builder.TemplatePropertyEntries) {
|
||
|
bool inTemplate = true;
|
||
|
|
||
|
// If the template container does not allow multiple instances,
|
||
|
// treat the controls as if not in templates.
|
||
|
if (entry.PropertyInfo != null) {
|
||
|
inTemplate = entry.IsMultiple;
|
||
|
}
|
||
|
|
||
|
BuildSourceDataTreeFromBuilder(((TemplatePropertyEntry)entry).Builder, inTemplate, false /*topLevelControlInTemplate*/, entry);
|
||
|
}
|
||
|
|
||
|
foreach (ComplexPropertyEntry entry in builder.ComplexPropertyEntries) {
|
||
|
// Don't create a build method for inner property strings
|
||
|
if (!(entry.Builder is StringPropertyBuilder)) {
|
||
|
BuildSourceDataTreeFromBuilder(((ComplexPropertyEntry)entry).Builder, fInTemplate, false /*topLevelControlInTemplate*/, entry);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Build a field declaration for the control if ID is defined on the control.
|
||
|
// (Not a generated ID)
|
||
|
if (!builder.IsGeneratedID)
|
||
|
BuildFieldDeclaration(builder);
|
||
|
|
||
|
CodeMemberMethod buildMethod = null;
|
||
|
CodeMemberMethod dataBindingMethod = null;
|
||
|
|
||
|
// Skip the rest if we're only generating the intermediate class
|
||
|
if (_sourceDataClass != null) {
|
||
|
|
||
|
if (!_designerMode) {
|
||
|
// Build a Build method for the control
|
||
|
buildMethod = BuildBuildMethod(builder, fTemplate, fInTemplate, topLevelControlInTemplate, pse, false);
|
||
|
}
|
||
|
|
||
|
// Build a Render method for the control, unless it has no code
|
||
|
if (builder.HasAspCode) {
|
||
|
BuildRenderMethod(builder, fTemplate);
|
||
|
}
|
||
|
|
||
|
// Build a method to extract values from the template
|
||
|
BuildExtractMethod(builder);
|
||
|
|
||
|
// Build a property binding method for the control
|
||
|
dataBindingMethod = BuildPropertyBindingMethod(builder, false);
|
||
|
}
|
||
|
|
||
|
// Give the ControlBuilder a chance to look at and modify the tree
|
||
|
builder.ProcessGeneratedCode(_codeCompileUnit, _intermediateClass,
|
||
|
_sourceDataClass, buildMethod, dataBindingMethod);
|
||
|
|
||
|
if (Parser.ControlBuilderInterceptor != null) {
|
||
|
Parser.ControlBuilderInterceptor.OnProcessGeneratedCode(builder, _codeCompileUnit,
|
||
|
_intermediateClass, _sourceDataClass, buildMethod, dataBindingMethod, builder.AdditionalState);
|
||
|
}
|
||
|
|
||
|
// Give the ParseRecorder a chance to look at and modify the tree
|
||
|
Parser.ParseRecorders.ProcessGeneratedCode(builder, _codeCompileUnit,
|
||
|
_intermediateClass, _sourceDataClass, buildMethod, dataBindingMethod);
|
||
|
}
|
||
|
|
||
|
internal virtual CodeExpression BuildStringPropertyExpression(PropertyEntry pse) {
|
||
|
string value = String.Empty;
|
||
|
if (pse is SimplePropertyEntry) {
|
||
|
value = (string)((SimplePropertyEntry)pse).Value;
|
||
|
}
|
||
|
else {
|
||
|
Debug.Assert(pse is ComplexPropertyEntry);
|
||
|
ComplexPropertyEntry cpe = (ComplexPropertyEntry)pse;
|
||
|
value = (string)((StringPropertyBuilder)cpe.Builder).BuildObject();
|
||
|
}
|
||
|
|
||
|
return CodeDomUtility.GenerateExpressionForValue(pse.PropertyInfo, value, typeof(string));
|
||
|
}
|
||
|
|
||
|
protected virtual CodeAssignStatement BuildTemplatePropertyStatement(CodeExpression ctrlRefExpr) {
|
||
|
|
||
|
// e.g. __ctrl.TemplateControl = this;
|
||
|
CodeAssignStatement assign = new CodeAssignStatement();
|
||
|
assign.Left = new CodePropertyReferenceExpression(ctrlRefExpr, "TemplateControl");
|
||
|
assign.Right = new CodeThisReferenceExpression();
|
||
|
return assign;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Return the name of an extract method
|
||
|
*/
|
||
|
private string ExtractMethodName(ControlBuilder builder) {
|
||
|
return extractTemplateValuesMethodPrefix + builder.ID;
|
||
|
}
|
||
|
|
||
|
|
||
|
private Type GetCtrlTypeForBuilder(ControlBuilder builder, bool fTemplate) {
|
||
|
|
||
|
if (builder is RootBuilder && builder.ControlType != null)
|
||
|
return builder.ControlType;
|
||
|
|
||
|
if (fTemplate)
|
||
|
return typeof(Control);
|
||
|
|
||
|
return builder.ControlType;
|
||
|
}
|
||
|
|
||
|
protected string GetMethodNameForBuilder(string prefix, ControlBuilder builder) {
|
||
|
if (builder is RootBuilder) {
|
||
|
return prefix + "Tree";
|
||
|
}
|
||
|
else {
|
||
|
return prefix + builder.ID;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Helper method to generate the device filter conditionals. e.g.
|
||
|
* if (this.TestDeviceFilter("FilterName")) {
|
||
|
* // ...
|
||
|
* }
|
||
|
* else {
|
||
|
* // ...
|
||
|
* }
|
||
|
*/
|
||
|
private void HandleDeviceFilterConditional(
|
||
|
ref PropertyEntry previous, PropertyEntry current,
|
||
|
CodeStatementCollection topStmts,
|
||
|
ref CodeStatementCollection currentStmts,
|
||
|
out CodeStatementCollection nextStmts) {
|
||
|
|
||
|
bool sameAsPrevious = (previous != null) && StringUtil.EqualsIgnoreCase(previous.Name, current.Name);
|
||
|
|
||
|
if (current.Filter.Length != 0) {
|
||
|
if (!sameAsPrevious) {
|
||
|
// If the current property entry is not the same as the previous entries,
|
||
|
// we need to start a new block of code
|
||
|
currentStmts = topStmts;
|
||
|
previous = null;
|
||
|
}
|
||
|
|
||
|
CodeConditionStatement ifStmt = new CodeConditionStatement();
|
||
|
CodeMethodInvokeExpression methCallExpression = new CodeMethodInvokeExpression(
|
||
|
new CodeThisReferenceExpression(), "TestDeviceFilter");
|
||
|
methCallExpression.Parameters.Add(new CodePrimitiveExpression(current.Filter));
|
||
|
ifStmt.Condition = methCallExpression;
|
||
|
currentStmts.Add(ifStmt);
|
||
|
|
||
|
// The current entry needs to go in the 'if' clause
|
||
|
currentStmts = ifStmt.TrueStatements;
|
||
|
|
||
|
// The next entry will tentatively go in the 'else' clause, unless it is
|
||
|
// for a different property (which we would catch next time around)
|
||
|
nextStmts = ifStmt.FalseStatements;
|
||
|
|
||
|
previous = current;
|
||
|
}
|
||
|
else {
|
||
|
// If we're switching to a new property, we need to add to the top-level statements (not the false block of an if)
|
||
|
if (!sameAsPrevious) {
|
||
|
currentStmts = topStmts;
|
||
|
}
|
||
|
nextStmts = topStmts;
|
||
|
previous = null;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
protected virtual bool UseResourceLiteralString(string s) {
|
||
|
|
||
|
// If the string is long enough, and the compiler supports it, use a UTF8 resource
|
||
|
// string for performance
|
||
|
return PageParser.EnableLongStringsAsResources &&
|
||
|
s.Length >= minLongLiteralStringLength &&
|
||
|
_codeDomProvider.Supports(GeneratorSupport.Win32Resources);
|
||
|
}
|
||
|
}
|
||
|
}
|