|
//------------------------------------------------------------------------------
// <copyright file="TemplateControl.cs" company="Microsoft">
// Copyright (c) Microsoft Corporation. All rights reserved.
// </copyright>
//------------------------------------------------------------------------------
/*
* Page class definition
*
* Copyright (c) 1998 Microsoft Corporation
*/
namespace System.Web.UI {
using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.ComponentModel;
using System.Configuration;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Globalization;
using System.IO;
using System.Reflection;
using System.Resources;
using System.Runtime.CompilerServices;
using System.Security.Permissions;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Web;
using System.Web.Caching;
using System.Web.Compilation;
using System.Web.Util;
using System.Xml;
using Debug = System.Web.Util.Debug;
/*
* Base class for Pages and UserControls
*/
/// <devdoc>
/// <para>Provides the <see cref='System.Web.UI.Page'/> class and the <see cref='System.Web.UI.UserControl'/> class with a base set of functionality.</para>
/// </devdoc>
public abstract class TemplateControl : Control, INamingContainer, IFilterResolutionService {
// Used for the literal string optimization (reading strings from resource)
private IntPtr _stringResourcePointer;
private int _maxResourceOffset;
private static object _lockObject = new object();
// Caches the list of auto-hookup methods for each compiled Type (Hashtable<Type,ListDictionary>).
// We use a Hashtable instead of the central Cache for optimal performance (VSWhidbey 479476)
private static Hashtable _eventListCache = new Hashtable();
private static object _emptyEventSingleton = new EventList();
private VirtualPath _virtualPath;
private IResourceProvider _resourceProvider;
private const string _pagePreInitEventName = "Page_PreInit";
private const string _pageInitEventName = "Page_Init";
private const string _pageInitCompleteEventName = "Page_InitComplete";
private const string _pageLoadEventName = "Page_Load";
private const string _pagePreLoadEventName = "Page_PreLoad";
private const string _pageLoadCompleteEventName = "Page_LoadComplete";
private const string _pagePreRenderCompleteEventName = "Page_PreRenderComplete";
private const string _pagePreRenderCompleteAsyncEventName = "Page_PreRenderCompleteAsync";
private const string _pageDataBindEventName = "Page_DataBind";
private const string _pagePreRenderEventName = "Page_PreRender";
private const string _pageSaveStateCompleteEventName = "Page_SaveStateComplete";
private const string _pageUnloadEventName = "Page_Unload";
private const string _pageErrorEventName = "Page_Error";
private const string _pageAbortTransactionEventName = "Page_AbortTransaction";
private const string _onTransactionAbortEventName = "OnTransactionAbort";
private const string _pageCommitTransactionEventName = "Page_CommitTransaction";
private const string _onTransactionCommitEventName = "OnTransactionCommit";
private static IDictionary _eventObjects;
// Used to implement no-compile pages/uc
private BuildResultNoCompileTemplateControl _noCompileBuildResult;
static TemplateControl() {
_eventObjects = new Hashtable(16);
_eventObjects.Add(_pagePreInitEventName, Page.EventPreInit);
_eventObjects.Add(_pageInitEventName, EventInit);
_eventObjects.Add(_pageInitCompleteEventName, Page.EventInitComplete);
_eventObjects.Add(_pageLoadEventName, EventLoad);
_eventObjects.Add(_pagePreLoadEventName, Page.EventPreLoad);
_eventObjects.Add(_pageLoadCompleteEventName, Page.EventLoadComplete);
_eventObjects.Add(_pagePreRenderCompleteEventName, Page.EventPreRenderComplete);
_eventObjects.Add(_pageDataBindEventName, EventDataBinding);
_eventObjects.Add(_pagePreRenderEventName, EventPreRender);
_eventObjects.Add(_pageSaveStateCompleteEventName, Page.EventSaveStateComplete);
_eventObjects.Add(_pageUnloadEventName, EventUnload);
_eventObjects.Add(_pageErrorEventName, EventError);
_eventObjects.Add(_pageAbortTransactionEventName, EventAbortTransaction);
_eventObjects.Add(_onTransactionAbortEventName, EventAbortTransaction);
_eventObjects.Add(_pageCommitTransactionEventName, EventCommitTransaction);
_eventObjects.Add(_onTransactionCommitEventName, EventCommitTransaction);
}
protected TemplateControl() {
Construct();
}
/// <devdoc>
/// <para>Do construction time logic (ASURT 66166)</para>
/// </devdoc>
protected virtual void Construct() {}
private static readonly object EventCommitTransaction = new object();
/// <devdoc>
/// <para>Occurs when a user initiates a transaction.</para>
/// </devdoc>
[
WebSysDescription(SR.Page_OnCommitTransaction)
]
public event EventHandler CommitTransaction {
add {
Events.AddHandler(EventCommitTransaction, value);
}
remove {
Events.RemoveHandler(EventCommitTransaction, value);
}
}
/// <devdoc>
/// <para>Gets and sets a value indicating whether theme is enabled.</para>
/// </devdoc>
[
Browsable(true)
]
public override bool EnableTheming {
get {
return base.EnableTheming;
}
set {
base.EnableTheming = value;
}
}
/// <devdoc>
/// <para>Raises the <see langword='CommitTransaction'/> event. You can use this method
/// for any transaction processing logic in which your page or user control
/// participates.</para>
/// </devdoc>
protected virtual void OnCommitTransaction(EventArgs e) {
EventHandler handler = (EventHandler)Events[EventCommitTransaction];
if (handler != null) handler(this, e);
}
private static readonly object EventAbortTransaction = new object();
/// <devdoc>
/// <para>Occurs when a user aborts a transaction.</para>
/// </devdoc>
[
WebSysDescription(SR.Page_OnAbortTransaction)
]
public event EventHandler AbortTransaction {
add {
Events.AddHandler(EventAbortTransaction, value);
}
remove {
Events.RemoveHandler(EventAbortTransaction, value);
}
}
/// <devdoc>
/// <para>Raises the <see langword='AbortTransaction'/> event.</para>
/// </devdoc>
protected virtual void OnAbortTransaction(EventArgs e) {
EventHandler handler = (EventHandler)Events[EventAbortTransaction];
if (handler != null) handler(this, e);
}
// Page_Error related events/methods
private static readonly object EventError = new object();
/// <devdoc>
/// <para>Occurs when an uncaught exception is thrown.</para>
/// </devdoc>
[
WebSysDescription(SR.Page_Error)
]
public event EventHandler Error {
add {
Events.AddHandler(EventError, value);
}
remove {
Events.RemoveHandler(EventError, value);
}
}
/// <devdoc>
/// <para>Raises the <see langword='Error'/> event.
/// </para>
/// </devdoc>
protected virtual void OnError(EventArgs e) {
EventHandler handler = (EventHandler)Events[EventError];
if (handler != null) handler(this, e);
}
/*
* Receive a no-compile build result that we call during FrameworkInitialize
*/
internal void SetNoCompileBuildResult(BuildResultNoCompileTemplateControl noCompileBuildResult) {
_noCompileBuildResult = noCompileBuildResult;
}
internal bool NoCompile {
get { return _noCompileBuildResult != null; }
}
/*
* Method sometime overidden by the generated sub classes. Users
* should not override.
*/
/// <internalonly/>
/// <devdoc>
/// <para>Initializes the requested page. While this is sometimes
/// overridden when the page is generated at runtime, you should not explicitly override this method.</para>
/// </devdoc>
[EditorBrowsable(EditorBrowsableState.Never)]
protected virtual void FrameworkInitialize() {
// If it's non-compiled, perform the FrameworkInitialize logic.
if (NoCompile) {
if (!HttpRuntime.DisableProcessRequestInApplicationTrust) {
// If PermitOnly was disabled in config, we call PermitOnly from here so that at least
// the control tree creation is protected, as it always is for compiled pages (VSWhidbey 449666)
if (HttpRuntime.NamedPermissionSet != null && !HttpRuntime.ProcessRequestInApplicationTrust) {
HttpRuntime.NamedPermissionSet.PermitOnly();
}
}
_noCompileBuildResult.FrameworkInitialize(this);
}
}
/*
* This property is overriden by the generated classes (hence it cannot be internal)
* If false, we don't do the HookUpAutomaticHandlers() magic.
*/
/// <internalonly/>
/// <devdoc>
/// <para>[To be supplied.]</para>
/// </devdoc>
[EditorBrowsable(EditorBrowsableState.Never)]
protected virtual bool SupportAutoEvents {
get { return true; }
}
/*
* Returns a pointer to the resource buffer, and the largest valid offset
* in the buffer (for security reason)
*/
internal IntPtr StringResourcePointer { get { return _stringResourcePointer; } }
internal int MaxResourceOffset { get { return _maxResourceOffset; } }
// This method is now obsolete. Ideally, we should get rid of it altogether, but that
// would be a breaking change (VSWhidbey 464430)
// We now use the parameter-less override, which is simpler
[EditorBrowsable(EditorBrowsableState.Never)]
public static object ReadStringResource(Type t) {
return StringResourceManager.ReadSafeStringResource(t);
}
[EditorBrowsable(EditorBrowsableState.Never)]
public object ReadStringResource() {
return StringResourceManager.ReadSafeStringResource(GetType());
}
/// <internalonly/>
/// <devdoc>
/// <para>This method is called by the generated classes (hence it cannot be internal)</para>
/// </devdoc>
protected LiteralControl CreateResourceBasedLiteralControl(int offset, int size, bool fAsciiOnly) {
return new ResourceBasedLiteralControl(this, offset, size, fAsciiOnly);
}
/// <internalonly/>
/// <devdoc>
/// <para>This method is called by the generated classes (hence it cannot be internal)</para>
/// </devdoc>
[EditorBrowsable(EditorBrowsableState.Never)]
protected void SetStringResourcePointer(object stringResourcePointer, int maxResourceOffset) {
// Ignore the passed in maxResourceOffset, which cannot be trusted. Instead, use
// the resource size that we obtained from the resource (ASURT 122759)
SafeStringResource ssr = (SafeStringResource) stringResourcePointer;
_stringResourcePointer = ssr.StringResourcePointer;
_maxResourceOffset = ssr.ResourceSize;
}
internal VirtualPath VirtualPath {
get {
return _virtualPath;
}
}
[
EditorBrowsable(EditorBrowsableState.Advanced),
Browsable(false)
]
public string AppRelativeVirtualPath {
get {
return VirtualPath.GetAppRelativeVirtualPathString(TemplateControlVirtualPath);
}
set {
// Set the TemplateSourceDirectory based on the VirtualPath
this.TemplateControlVirtualPath = VirtualPath.CreateNonRelative(value);
}
}
internal VirtualPath TemplateControlVirtualPath {
get {
return _virtualPath;
}
set {
_virtualPath = value;
// Set the TemplateSourceDirectory based on the VirtualPath
this.TemplateControlVirtualDirectory = _virtualPath.Parent;
}
}
/// <devdoc>
/// <para>Tests if a device filter applies to this request</para>
/// </devdoc>
public virtual bool TestDeviceFilter(string filterName) {
return(Context.Request.Browser.IsBrowser(filterName));
}
/// <internalonly/>
/// <devdoc>
/// <para>This method is called by the generated classes (hence it cannot be internal)</para>
/// </devdoc>
[EditorBrowsable(EditorBrowsableState.Never)]
protected void WriteUTF8ResourceString(HtmlTextWriter output, int offset, int size, bool fAsciiOnly) {
// Make sure we don't access invalid data
checked {
if (offset < 0 || size < 0 || offset + size > _maxResourceOffset)
throw new ArgumentOutOfRangeException("offset");
}
output.WriteUTF8ResourceString(StringResourcePointer, offset, size, fAsciiOnly);
}
/*
* This method is overriden by the generated classes (hence it cannot be internal)
*/
/// <internalonly/>
/// <devdoc>
/// </devdoc>
[EditorBrowsable(EditorBrowsableState.Never)]
[Obsolete("Use of this property is not recommended because it is no longer useful. http://go.microsoft.com/fwlink/?linkid=14202")]
protected virtual int AutoHandlers {
get { return 0;}
set {}
}
internal override TemplateControl GetTemplateControl() {
return this;
}
[SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Justification = "See comment on GetDelegateInformationWithAssert.")]
internal void HookUpAutomaticHandlers() {
// Do nothing if auto-events are not supported
if (!SupportAutoEvents) {
return;
}
// Get the event list for this Type from our cache, if possible
object o = _eventListCache[GetType()];
EventList eventList;
// Try to find what handlers are implemented if not tried before
if (o == null) {
lock (_lockObject) {
// Try the cache again, in case another thread took care of it
o = (EventList)_eventListCache[GetType()];
if (o == null) {
eventList = new EventList();
GetDelegateInformation(eventList);
// Cannot find any known handlers.
if (eventList.IsEmpty) {
o = _emptyEventSingleton;
}
else {
o = eventList;
}
// Cache it for next time
_eventListCache[GetType()] = o;
}
}
}
// Don't do any thing if no known handlers are found.
if (o == _emptyEventSingleton) {
return;
}
eventList = (EventList)o;
IDictionary<string, SyncEventMethodInfo> syncEvents = eventList.SyncEvents;
// Hook up synchronous events
foreach (var entry in syncEvents) {
string key = entry.Key;
SyncEventMethodInfo info = entry.Value;
Debug.Assert(_eventObjects[key] != null);
bool eventExists = false;
MethodInfo methodInfo = info.MethodInfo;
Delegate eventDelegates = Events[_eventObjects[key]];
if (eventDelegates != null) {
foreach (Delegate eventDelegate in eventDelegates.GetInvocationList()) {
// Ignore if this method is already added to the events list.
if (eventDelegate.Method.Equals(methodInfo)) {
eventExists = true;
break;
}
}
}
if (!eventExists) {
// Create a new Calli delegate proxy
IntPtr functionPtr = methodInfo.MethodHandle.GetFunctionPointer();
EventHandler handler = (new CalliEventHandlerDelegateProxy(this, functionPtr, info.IsArgless)).Handler;
// Adds the delegate to events list.
Events.AddHandler(_eventObjects[key], handler);
}
}
// Hook up asynchronous events
IDictionary<string, AsyncEventMethodInfo> asyncEvents = eventList.AsyncEvents;
AsyncEventMethodInfo preRenderCompleteAsyncEvent;
if (asyncEvents.TryGetValue(_pagePreRenderCompleteAsyncEventName, out preRenderCompleteAsyncEvent)) {
Page page = (Page)this; // this event handler only exists for the Page type
if (preRenderCompleteAsyncEvent.RequiresCancellationToken) {
var handler = FastDelegateCreator<Func<CancellationToken, Task>>.BindTo(this, preRenderCompleteAsyncEvent.MethodInfo);
page.RegisterAsyncTask(new PageAsyncTask(handler));
}
else {
var handler = FastDelegateCreator<Func<Task>>.BindTo(this, preRenderCompleteAsyncEvent.MethodInfo);
page.RegisterAsyncTask(new PageAsyncTask(handler));
}
}
}
private void GetDelegateInformation(EventList eventList) {
if (HttpRuntime.IsFullTrust) {
GetDelegateInformationWithNoAssert(eventList);
}
else {
GetDelegateInformationWithAssert(eventList);
}
}
// Make sure we have reflection permission to discover the handlers (ASURT 105965)
// Using this permission is bad practice; we should use RMA instead of full MemberAccess,
// or we should force the instance methods to be public. But this is a legacy behavior
// and we can't change it without breaking the world.
[ReflectionPermission(SecurityAction.Assert, Flags = ReflectionPermissionFlag.MemberAccess)]
[SuppressMessage("Microsoft.Security", "CA2106:SecureAsserts", Justification = "See comment above.")]
private void GetDelegateInformationWithAssert(EventList eventList) {
GetDelegateInformationWithNoAssert(eventList);
}
private void GetDelegateInformationWithNoAssert(EventList eventList) {
IDictionary<string, SyncEventMethodInfo> syncEventDictionary = eventList.SyncEvents;
IDictionary<string, AsyncEventMethodInfo> asyncEventDictionary = eventList.AsyncEvents;
if (this is Page) {
/* SYNCHRONOUS - Page */
GetDelegateInformationFromSyncMethod(_pagePreInitEventName, syncEventDictionary);
GetDelegateInformationFromSyncMethod(_pagePreLoadEventName, syncEventDictionary);
GetDelegateInformationFromSyncMethod(_pageLoadCompleteEventName, syncEventDictionary);
GetDelegateInformationFromSyncMethod(_pagePreRenderCompleteEventName, syncEventDictionary);
GetDelegateInformationFromSyncMethod(_pageInitCompleteEventName, syncEventDictionary);
GetDelegateInformationFromSyncMethod(_pageSaveStateCompleteEventName, syncEventDictionary);
/* ASYNCHRONOUS - Page */
GetDelegateInformationFromAsyncMethod(_pagePreRenderCompleteAsyncEventName, asyncEventDictionary);
}
/* SYNCHRONOUS - Control */
GetDelegateInformationFromSyncMethod(_pageInitEventName, syncEventDictionary);
GetDelegateInformationFromSyncMethod(_pageLoadEventName, syncEventDictionary);
GetDelegateInformationFromSyncMethod(_pageDataBindEventName, syncEventDictionary);
GetDelegateInformationFromSyncMethod(_pagePreRenderEventName, syncEventDictionary);
GetDelegateInformationFromSyncMethod(_pageUnloadEventName, syncEventDictionary);
GetDelegateInformationFromSyncMethod(_pageErrorEventName, syncEventDictionary);
if (!GetDelegateInformationFromSyncMethod(_pageAbortTransactionEventName, syncEventDictionary)) {
GetDelegateInformationFromSyncMethod(_onTransactionAbortEventName, syncEventDictionary);
}
if (!GetDelegateInformationFromSyncMethod(_pageCommitTransactionEventName, syncEventDictionary)) {
GetDelegateInformationFromSyncMethod(_onTransactionCommitEventName, syncEventDictionary);
}
/* ASYNCHRONOUS - Control */
}
private bool GetDelegateInformationFromAsyncMethod(string methodName, IDictionary<string, AsyncEventMethodInfo> dictionary) {
// First, try to get a delegate to the single-parameter handler
MethodInfo parameterfulMethod = GetInstanceMethodInfo(typeof(Func<CancellationToken, Task>), methodName);
if (parameterfulMethod != null) {
dictionary[methodName] = new AsyncEventMethodInfo(parameterfulMethod, requiresCancellationToken: true);
return true;
}
// If there isn't one, try the argless one
MethodInfo parameterlessMethod = GetInstanceMethodInfo(typeof(Func<Task>), methodName);
if (parameterlessMethod != null) {
dictionary[methodName] = new AsyncEventMethodInfo(parameterlessMethod, requiresCancellationToken: false);
return true;
}
return false;
}
private bool GetDelegateInformationFromSyncMethod(string methodName, IDictionary<string, SyncEventMethodInfo> dictionary) {
// First, try to get a delegate to the two parameter handler
MethodInfo parameterfulMethod = GetInstanceMethodInfo(typeof(EventHandler), methodName);
if (parameterfulMethod != null) {
dictionary[methodName] = new SyncEventMethodInfo(parameterfulMethod, isArgless: false);
return true;
}
// If there isn't one, try the argless one
MethodInfo parameterlessMethod = GetInstanceMethodInfo(typeof(VoidMethod), methodName);
if (parameterlessMethod != null) {
dictionary[methodName] = new SyncEventMethodInfo(parameterlessMethod, isArgless: true);
return true;
}
return false;
}
private MethodInfo GetInstanceMethodInfo(Type delegateType, string methodName) {
Delegate del = Delegate.CreateDelegate(
type: delegateType,
target: this,
method: methodName,
ignoreCase: true,
throwOnBindFailure: false);
return (del != null) ? del.Method : null;
}
/// <devdoc>
/// <para>Obtains a <see cref='System.Web.UI.UserControl'/> object from a user control file.</para>
/// </devdoc>
public Control LoadControl(string virtualPath) {
return LoadControl(VirtualPath.Create(virtualPath));
}
internal Control LoadControl(VirtualPath virtualPath) {
// If it's relative, make it *app* relative. Treat is as relative to this
// user control (ASURT 55513)
virtualPath = VirtualPath.Combine(this.TemplateControlVirtualDirectory, virtualPath);
// Process the user control and get its BuildResult
BuildResult result = BuildManager.GetVPathBuildResult(Context, virtualPath);
return LoadControl((IWebObjectFactory)result, virtualPath, null /*Type*/, null /*parameters*/);
}
// Make sure we have reflection permission to use GetMethod below (ASURT 106196)
[ReflectionPermission(SecurityAction.Assert, Flags=ReflectionPermissionFlag.MemberAccess)]
private void AddStackContextToHashCode(HashCodeCombiner combinedHashCode) {
StackTrace st = new StackTrace();
// First, skip all the stack frames that are in the TemplateControl class, as
// they are irrelevant to the hash. Start the search at 2 since we know for sure
// that this method and its caller are in TemplateControl.
int startingUserFrame = 2;
for (; ; startingUserFrame++) {
StackFrame f = st.GetFrame(startingUserFrame);
if (f.GetMethod().DeclaringType != typeof(TemplateControl)) {
break;
}
}
// Get a cache key based on the top two items of the caller's stack.
// It's not guaranteed unique, but for all common cases, it will be
for (int i = startingUserFrame; i < startingUserFrame + 2; i++) {
StackFrame f = st.GetFrame(i);
MethodBase m = f.GetMethod();
combinedHashCode.AddObject(m.DeclaringType.AssemblyQualifiedName);
combinedHashCode.AddObject(m.Name);
combinedHashCode.AddObject(f.GetNativeOffset());
}
}
public Control LoadControl(Type t, object[] parameters) {
return LoadControl(null /*IWebObjectFactory*/, null /*virtualPath*/, t, parameters);
}
private Control LoadControl(IWebObjectFactory objectFactory, VirtualPath virtualPath, Type t, object[] parameters) {
// Make sure we get an object factory or a type, but not both
Debug.Assert((objectFactory == null) != (t == null));
BuildResultCompiledType compiledUCResult = null;
BuildResultNoCompileUserControl noCompileUCResult = null;
if (objectFactory != null) {
// It can be a compiled or no-compile user control
compiledUCResult = objectFactory as BuildResultCompiledType;
if (compiledUCResult != null) {
t = compiledUCResult.ResultType;
Debug.Assert(t != null);
// Make sure it's a user control (VSWhidbey 428718)
Util.CheckAssignableType(typeof(UserControl), t);
}
else {
noCompileUCResult = (BuildResultNoCompileUserControl)objectFactory;
Debug.Assert(noCompileUCResult != null);
}
}
else {
// Make sure the type has the correct base class (ASURT 123677)
if (t != null)
Util.CheckAssignableType(typeof(Control), t);
}
PartialCachingAttribute cacheAttrib;
// Check if the user control has a PartialCachingAttribute attribute
if (t != null) {
cacheAttrib = (PartialCachingAttribute)
TypeDescriptor.GetAttributes(t)[typeof(PartialCachingAttribute)];
}
else {
cacheAttrib = noCompileUCResult.CachingAttribute;
}
if (cacheAttrib == null) {
// The control is not cached. Just create it.
Control c;
if (objectFactory != null) {
c = (Control) objectFactory.CreateInstance();
}
else {
c = (Control) HttpRuntime.CreatePublicInstance(t, parameters);
}
// If it's a user control, do some extra initialization
UserControl uc = c as UserControl;
if (uc != null) {
Debug.Assert(virtualPath != null);
if (virtualPath != null)
uc.TemplateControlVirtualPath = virtualPath;
uc.InitializeAsUserControl(Page);
}
return c;
}
HashCodeCombiner combinedHashCode = new HashCodeCombiner();
// Start by adding the type or object factory of the user control to the hash.
// This guarantees that two unrelated user controls don't share the same cached data.
if (objectFactory != null) {
combinedHashCode.AddObject(objectFactory);
}
else {
combinedHashCode.AddObject(t);
}
// If it's not shared, add some stack frames to the hash
if (!cacheAttrib.Shared) {
AddStackContextToHashCode(combinedHashCode);
}
string cacheKey = combinedHashCode.CombinedHashString;
// Wrap it to allow it to be cached
return new PartialCachingControl(objectFactory, t, cacheAttrib, "_" + cacheKey, parameters);
}
// Class that implements the templates returned by LoadTemplate (ASURT 94138)
internal class SimpleTemplate : ITemplate {
private IWebObjectFactory _objectFactory;
internal SimpleTemplate(ITypedWebObjectFactory objectFactory) {
// Make sure it's a user control (VSWhidbey 428718)
Util.CheckAssignableType(typeof(UserControl), objectFactory.InstantiatedType);
_objectFactory = objectFactory;
}
public virtual void InstantiateIn(Control control) {
UserControl uc = (UserControl)_objectFactory.CreateInstance();
uc.InitializeAsUserControl(control.Page);
control.Controls.Add(uc);
}
}
/// <devdoc>
/// <para>
/// Obtains an instance of the <see langword='ITemplate'/> interface from an
/// external file.
/// </para>
/// </devdoc>
public ITemplate LoadTemplate(string virtualPath) {
return LoadTemplate(VirtualPath.Create(virtualPath));
}
internal ITemplate LoadTemplate(VirtualPath virtualPath) {
// If it's relative, make it *app* relative. Treat is as relative to this
// user control (ASURT 55513)
virtualPath = VirtualPath.Combine(TemplateControlVirtualDirectory, virtualPath);
// Compile the declarative template and get its object factory
ITypedWebObjectFactory objectFactory = (ITypedWebObjectFactory)BuildManager.GetVPathBuildResult(
Context, virtualPath);
return new SimpleTemplate(objectFactory);
}
/// <devdoc>
/// <para> Parse the input string into a Control. Looks for the first control
/// in the input. Returns null if none is found.</para>
/// </devdoc>
public Control ParseControl(string content) {
return ParseControl(content, true);
}
public Control ParseControl(string content, bool ignoreParserFilter) {
return TemplateParser.ParseControl(content, VirtualPath.Create(AppRelativeVirtualPath), ignoreParserFilter);
}
#if NOTYET
/// <devdoc>
/// <para> Parse the input string into an ITemplate.</para>
/// </devdoc>
internal ITemplate ParseTemplate(string content) {
return TemplateParser.ParseTemplate(content, AppRelativeTemplateSourceDirectory);
}
#endif
/// <devdoc>
/// Used by simplified databinding methods to ensure they can only be called when the control is on a page.
/// </devdoc>
private void CheckPageExists() {
if (Page == null) {
throw new InvalidOperationException(SR.GetString(SR.TemplateControl_DataBindingRequiresPage));
}
}
/// <devdoc>
/// Simplified databinding Eval() method. This method uses the current data item to evaluate an expression using DataBinder.Eval().
/// The data item is retrieved using either the IDataItemContainer interface or by looking for a property called 'DataItem'.
/// If the data item is not found, an exception is thrown.
/// </devdoc>
protected internal object Eval(string expression) {
CheckPageExists();
return DataBinder.Eval(Page.GetDataItem(), expression);
}
/// <devdoc>
/// Simplified databinding Eval() method with a format expression. This method uses the current data item to evaluate an expression using DataBinder.Eval().
/// The data item is retrieved using either the IDataItemContainer interface or by looking for a property called 'DataItem'.
/// If the data item is not found, an exception is thrown.
/// </devdoc>
protected internal string Eval(string expression, string format) {
CheckPageExists();
return DataBinder.Eval(Page.GetDataItem(), expression, format);
}
/// <devdoc>
/// Simplified databinding XPath() method. This method uses the current data item to evaluate an XPath expression using XPathBinder.Eval().
/// The data item is retrieved using either the IDataItemContainer interface or by looking for a property called 'DataItem'.
/// If the data item is not found, an exception is thrown.
/// </devdoc>
protected internal object XPath(string xPathExpression) {
CheckPageExists();
return XPathBinder.Eval(Page.GetDataItem(), xPathExpression);
}
/// <devdoc>
/// Simplified databinding XPath() method. This method uses the current data item and a namespace resolver
/// to evaluate an XPath expression using XPathBinder.Eval().
/// The data item is retrieved using either the IDataItemContainer interface or by looking for a property called 'DataItem'.
/// If the data item is not found, an exception is thrown.
/// </devdoc>
protected internal object XPath(string xPathExpression, IXmlNamespaceResolver resolver) {
CheckPageExists();
return XPathBinder.Eval(Page.GetDataItem(), xPathExpression, resolver);
}
/// <devdoc>
/// Simplified databinding XPath() method with a format expression. This method uses the current data item to evaluate an XPath expression using XPathBinder.Eval().
/// The data item is retrieved using either the IDataItemContainer interface or by looking for a property called 'DataItem'.
/// If the data item is not found, an exception is thrown.
/// </devdoc>
protected internal string XPath(string xPathExpression, string format) {
CheckPageExists();
return XPathBinder.Eval(Page.GetDataItem(), xPathExpression, format);
}
/// <devdoc>
/// Simplified databinding XPath() method with a format expression. This method uses the current data item and a namespace resolver
/// to evaluate an XPath expression using XPathBinder.Eval().
/// The data item is retrieved using either the IDataItemContainer interface or by looking for a property called 'DataItem'.
/// If the data item is not found, an exception is thrown.
/// </devdoc>
protected internal string XPath(string xPathExpression, string format, IXmlNamespaceResolver resolver) {
CheckPageExists();
return XPathBinder.Eval(Page.GetDataItem(), xPathExpression, format, resolver);
}
/// <devdoc>
/// Simplified databinding XPathSelect() method. This method uses the current data item to evaluate an XPath expression that returns a node list using XPathBinder.Select().
/// The data item is retrieved using either the IDataItemContainer interface or by looking for a property called 'DataItem'.
/// If the data item is not found, an exception is thrown.
/// </devdoc>
protected internal IEnumerable XPathSelect(string xPathExpression) {
CheckPageExists();
return XPathBinder.Select(Page.GetDataItem(), xPathExpression);
}
/// <devdoc>
/// Simplified databinding XPathSelect() method. This method uses the current data item and a namespace resolver
/// to evaluate an XPath expression that returns a node list using XPathBinder.Select().
/// The data item is retrieved using either the IDataItemContainer interface or by looking for a property called 'DataItem'.
/// If the data item is not found, an exception is thrown.
/// </devdoc>
protected internal IEnumerable XPathSelect(string xPathExpression, IXmlNamespaceResolver resolver) {
CheckPageExists();
return XPathBinder.Select(Page.GetDataItem(), xPathExpression, resolver);
}
/// <devdoc>
/// Return a Page-level resource object
/// </devdoc>
protected object GetLocalResourceObject(string resourceKey) {
// Cache the resource provider in the template control, so that if a Page needs to call
// this multiple times, we don't need to call ResourceExpressionBuilder.GetLocalResourceProvider
// every time.
if (_resourceProvider == null)
_resourceProvider = ResourceExpressionBuilder.GetLocalResourceProvider(this);
return ResourceExpressionBuilder.GetResourceObject(_resourceProvider, resourceKey, null /*culture*/);
}
protected object GetLocalResourceObject(string resourceKey, Type objType, string propName) {
// Cache the resource provider in the template control, so that if a Page needs to call
// this multiple times, we don't need to call ResourceExpressionBuilder.GetLocalResourceProvider
// every time.
if (_resourceProvider == null)
_resourceProvider = ResourceExpressionBuilder.GetLocalResourceProvider(this);
return ResourceExpressionBuilder.GetResourceObject(_resourceProvider,
resourceKey, null /*culture*/, objType, propName);
}
/// <devdoc>
/// Return an App-level resource object
/// </devdoc>
protected object GetGlobalResourceObject(string className, string resourceKey) {
return ResourceExpressionBuilder.GetGlobalResourceObject(className, resourceKey, null, null, null);
}
protected object GetGlobalResourceObject(string className, string resourceKey, Type objType, string propName) {
return ResourceExpressionBuilder.GetGlobalResourceObject(className, resourceKey, objType, propName, null);
}
#region IFilterResolutionService
/// <internalonly/>
bool IFilterResolutionService.EvaluateFilter(string filterName) {
return TestDeviceFilter(filterName);
}
/// <internalonly/>
int IFilterResolutionService.CompareFilters(string filter1, string filter2) {
return BrowserCapabilitiesCompiler.BrowserCapabilitiesFactory.CompareFilters(filter1, filter2);
}
#endregion
private class EventList {
internal readonly IDictionary<string, AsyncEventMethodInfo> AsyncEvents = new Dictionary<string, AsyncEventMethodInfo>(StringComparer.Ordinal);
internal readonly IDictionary<string, SyncEventMethodInfo> SyncEvents = new Dictionary<string, SyncEventMethodInfo>(StringComparer.Ordinal);
internal bool IsEmpty {
get {
return (AsyncEvents.Count == 0 && SyncEvents.Count == 0);
}
}
}
// Internal helper class for storing the event info
private class SyncEventMethodInfo {
internal SyncEventMethodInfo(MethodInfo methodInfo, bool isArgless){
if (IsAsyncVoidMethod(methodInfo)) {
SynchronizationContextUtil.ValidateModeForPageAsyncVoidMethods();
}
MethodInfo = methodInfo;
IsArgless = isArgless;
}
internal bool IsArgless { get; private set; }
internal MethodInfo MethodInfo { get; private set; }
private static bool IsAsyncVoidMethod(MethodInfo methodInfo) {
// When the C# / VB compilers generate an 'async void' method, they'll put
// an [AsyncStateMachine] attribute on the entry point. This marker attribute
// can be used to detect these methods. It's not 100% reliable, since it's
// possible that a normal void method simply calls an async void method, and
// the 'outer' method won't contain this attribute. But the heuristic is
// good enough to help developers land in the pit of success re: async.
return methodInfo.IsDefined(typeof(AsyncStateMachineAttribute), inherit: false);
}
}
private class AsyncEventMethodInfo {
internal AsyncEventMethodInfo(MethodInfo methodInfo, bool requiresCancellationToken) {
MethodInfo = methodInfo;
RequiresCancellationToken = requiresCancellationToken;
}
internal MethodInfo MethodInfo { get; private set; }
internal bool RequiresCancellationToken { get; private set; }
}
}
}
|