📄 scriptobjectbuilder.cs
字号:
/// <returns></returns>
public static IEnumerable<ScriptReference> GetScriptReferences(Type type)
{
return GetScriptReferences(type, false);
}
/// <summary>
/// Gets the ScriptReferences for a Type
/// </summary>
/// <param name="type">Type for which references are to be gotten</param>
/// <param name="ignoreStartingTypeReferences">true if the ClientScriptResource for the starting type is to be ignored</param>
/// <returns>list of ScriptReferences for the Type</returns>
public static IEnumerable<ScriptReference> GetScriptReferences(Type type, bool ignoreStartingTypeReferences)
{
List<ResourceEntry> entries = GetScriptReferencesInternal(type, new Stack<Type>(), ignoreStartingTypeReferences);
return ScriptReferencesFromResourceEntries(entries);
}
/// <summary>
/// Gets the embedded css file references for a type
/// </summary>
/// <param name="type"></param>
/// <returns></returns>
public static IEnumerable<string> GetCssReferences(Control control)
{
return GetCssReferences(control, control.GetType(), new Stack<Type>());
}
/// <summary>
/// Register's the Css references for this control
/// </summary>
/// <param name="control"></param>
public static void RegisterCssReferences(Control control)
{
// Add the link to the page header instead of inside the body which is not xhtml compliant
HtmlHead header = control.Page.Header;
foreach (string styleSheet in ScriptObjectBuilder.GetCssReferences(control))
{
if (null == header)
{
// It would be nice to add the required header here, but it's too late in the page
// lifecycle to be modifying the Page.Controls collection - throw an informative
// exception instead and let the page author make the simple change.
throw new NotSupportedException("This page is missing a HtmlHead control which is required for the CSS stylesheet link that is being added. Please add <head runat=\"server\" />.");
}
bool addIt = true; // looking for this stylesheet already in the header
foreach (Control c in header.Controls)
{
HtmlLink l = c as HtmlLink;
if (null != l && styleSheet.Equals(l.Href, StringComparison.OrdinalIgnoreCase))
{
addIt = false;
break;
}
}
if (addIt)
{
HtmlLink link = new HtmlLink();
link.Href = styleSheet;
link.Attributes.Add("type", "text/css");
link.Attributes.Add("rel", "stylesheet");
header.Controls.Add(link);
// ASP.NET AJAX doesn't currently send a new head element down during an async postback,
// so we do the same thing on the client by registering the appropriate script for after
// the update. A HiddenField is used to prevent multiple registrations.
ScriptManager scriptManager = ScriptManager.GetCurrent(control.Page);
if (null == scriptManager)
{
throw new InvalidOperationException(Resources.E_NoScriptManager);
}
if (scriptManager.IsInAsyncPostBack &&
(null == control.Page.Request.Form["__AjaxControlToolkitCalendarCssLoaded"]))
{
ScriptManager.RegisterClientScriptBlock(control, control.GetType(), "RegisterCssReferences",
"var head = document.getElementsByTagName('HEAD')[0];" +
"if (head) {" +
"var linkElement = document.createElement('link');" +
"linkElement.type = 'text/css';" +
"linkElement.rel = 'stylesheet';" +
"linkElement.href = '" + styleSheet + "';" +
"head.appendChild(linkElement);" +
"}"
, true);
ScriptManager.RegisterHiddenField(control, "__AjaxControlToolkitCalendarCssLoaded", "");
}
}
}
}
/// <summary>
/// Executes a callback capable method on a control
/// </summary>
/// <param name="control"></param>
/// <param name="callbackArgument"></param>
/// <returns></returns>
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Justification="Deliberate attempt to catch and pass-on all exceptions")]
public static string ExecuteCallbackMethod(Control control, string callbackArgument)
{
Type controlType = control.GetType();
// Deserialize the callback JSON into CLR objects
JavaScriptSerializer js = new JavaScriptSerializer();
Dictionary<string, object> callInfo = js.DeserializeObject(callbackArgument) as Dictionary<string, object>;
// Get the call information
string methodName = (string)callInfo["name"];
object[] args = (object[])callInfo["args"];
string clientState = (string)callInfo["state"];
// Attempt to load the client state
IClientStateManager csm = control as IClientStateManager;
if (csm != null && csm.SupportsClientState)
{
csm.LoadClientState(clientState);
}
// call the method
object result = null;
string error = null;
try
{
// Find a matching static or instance method. Only public methods can be invoked
MethodInfo mi = controlType.GetMethod(methodName, BindingFlags.Public | BindingFlags.Static | BindingFlags.Instance);
if (mi == null)
{
throw new MissingMethodException(controlType.FullName, methodName);
}
// Verify that the method has the corrent number of parameters as well as the ExtenderControlMethodAttribute
ParameterInfo[] methodParams = mi.GetParameters();
ExtenderControlMethodAttribute methAttr = (ExtenderControlMethodAttribute)Attribute.GetCustomAttribute(mi, typeof(ExtenderControlMethodAttribute));
if (methAttr == null || !methAttr.IsScriptMethod || args.Length != methodParams.Length)
{
throw new MissingMethodException(controlType.FullName, methodName);
}
// Convert each argument to the parameter type if possible
// NOTE: I'd rather have the ObjectConverter from within System.Web.Script.Serialization namespace for this
object[] targetArgs = new object[args.Length];
for (int i = 0; i < targetArgs.Length; i++)
{
if (args[i] == null)
continue;
targetArgs[i] = Convert.ChangeType(args[i], methodParams[i].ParameterType, CultureInfo.InvariantCulture);
}
result = mi.Invoke(control, targetArgs);
}
catch (Exception ex)
{
// Catch the exception information to relay back to the client
if (ex is TargetInvocationException)
{
ex = ex.InnerException;
}
error = ex.GetType().FullName + ":" + ex.Message;
}
// return the result
Dictionary<string, object> resultInfo = new Dictionary<string, object>();
if (error == null)
{
resultInfo["result"] = result;
if (csm != null && csm.SupportsClientState)
{
resultInfo["state"] = csm.SaveClientState();
}
}
else
{
resultInfo["error"] = error;
}
// Serialize the result info into JSON
return js.Serialize(resultInfo);
}
/// <summary>
/// ScriptReference objects aren't immutable. The AJAX core adds context to them, so we cant' reuse them.
/// Therefore, we track only ReferenceEntries internally and then convert them to NEW ScriptReference objects on-demand.
/// </summary>
/// <param name="entries"></param>
/// <returns></returns>
private static IEnumerable<ScriptReference> ScriptReferencesFromResourceEntries(IList<ResourceEntry> entries)
{
IList<ScriptReference> referenceList = new List<ScriptReference>(entries.Count);
foreach (ResourceEntry re in entries)
{
referenceList.Add(re.ToScriptReference());
}
return referenceList;
}
/// <summary>
/// Gets the ScriptReferences for a Type and walks the Type's dependencies with circular-reference checking
/// </summary>
/// <param name="type">Type for which references are to be gotten</param>
/// <param name="typeReferenceStack">Stack of Types to track processed types</param>
/// <param name="ignoreStartingTypeReferences">true if the ClientScriptResource for the starting type is to be ignored</param>
/// <returns>list of ScriptReferences for the Type</returns>
private static List<ResourceEntry> GetScriptReferencesInternal(Type type, Stack<Type> typeReferenceStack, bool ignoreStartingTypeReferences)
{
// Verify no circular references
if (typeReferenceStack.Contains(type))
{
throw new InvalidOperationException("Circular reference detected.");
}
// Look for a cached set of references outside of the lock for perf.
//
List<ResourceEntry> entries;
// Don't check the cache for "unusual" responses
if (!ignoreStartingTypeReferences && _cache.TryGetValue(type, out entries))
{
return entries;
}
// Track this type to prevent circular references
typeReferenceStack.Push(type);
try
{
lock (_sync)
{
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -