自定义视图引擎
创建自己的视图引擎
public class HoTMeaTViewEngine : VirtualPathProviderViewEngine
{
public HoTMeaTViewEngine()
{
base.ViewLocationFormats = new string[] { "~/Views/{1}/{0}.html" };
base.PartialViewLocationFormats = base.ViewLocationFormats;
}
protected override IView CreateView(ControllerContext controllerContext, string viewPath, string masterPath)
{
return new HoTMeaTView(viewPath, masterPath ?? "");
}
protected override IView CreatePartialView(ControllerContext controllerContext, string partialPath)
{
return new HoTMeaTView(partialPath, "");
}
}
创建自己的视图
public class HoTMeaTView : IView
{
static Dictionary<string, string> cachedFiles = new Dictionary<string, string>();
static Regex codeParser = new Regex(@"
(/<code(/s+(?<AttributeName>/w+)/s*=/s*""(?<AttributeValue>[^>""]*)"")*/s*//>)
|
(/<code(/s+(?<AttributeName>/w+)/s*=/s*""(?<AttributeValue>[^>""]*)"")*/s*/>/s*
(/s*/<(?<ParameterName>/w+)/>(?<ParameterValue>[/s/S]*?)/<//k<ParameterName>/>/s*)*
/s*/</code/>)
", RegexOptions.Compiled | RegexOptions.IgnoreCase | RegexOptions.IgnorePatternWhitespace);
static Regex controlsParser = new Regex(@"
/<(?<Control>(listView))(/s+(?<AttributeName>/w+)/s*=/s*""(?<AttributeValue>[^>""]*)"")*/s*/>/s*
(/s*/<(?<ParameterName>/w+)/>(?<ParameterValue>[/s/S]*?)/<//k<ParameterName>/>/s*)*
/s*/<//k<Control>/>", RegexOptions.Compiled | RegexOptions.IgnoreCase | RegexOptions.IgnorePatternWhitespace);
public HoTMeaTView(string viewPath, string masterPath)
{
this.ViewPath = viewPath;
this.MasterPath = masterPath;
}
public ViewContext CurrentViewContext { get; private set; }
public string ViewPath { get; private set; }
public string MasterPath { get; private set; }
public void Render(ViewContext viewContext, TextWriter writer)
{
this.CurrentViewContext = viewContext;
if (cachedFiles.ContainsKey(this.ViewPath) == false)
{
cachedFiles.Add(this.ViewPath, File.ReadAllText(viewContext.HttpContext.Server.MapPath(this.ViewPath)));
}
string sourceCode = cachedFiles[this.ViewPath];
sourceCode = controlsParser.Replace(sourceCode, delegate(Match currentMatch)
{
SettingsDicitonary settings = this.GetSettings(currentMatch);
return this.EvaluateControl(currentMatch.Groups["Control"].Value.ToLower(), settings.Attributes, settings.Parameters, currentMatch.Value);
});
sourceCode = codeParser.Replace(sourceCode, delegate(Match currentMatch)
{
SettingsDicitonary settings = this.GetSettings(currentMatch);
return this.EvaluateCode(settings.Attributes["class"], settings.Attributes["method"], settings.Parameters, currentMatch.Value);
});
writer.Write(sourceCode);
}
private string EvaluateCode(string className, string methodName, Dictionary<string, string> parameters, string originalSource)
{
Type targetType = Type.GetType(className, false, false);
if (targetType == null)
{
return originalSource;
}
// First attempt with a static method.
MethodInfo targetMethod = targetType.GetMethods(BindingFlags.Public | BindingFlags.Static)
.Where(method => method.Name == methodName)
.Where(method => method.GetParameters()
.Count(parameter => parameters.Keys.Contains(parameter.Name)) == parameters.Count)
.FirstOrDefault();
if (targetMethod != null)
{
return string.Format("{0}", targetMethod.Invoke(null, this.GetConvertedValues(parameters, targetMethod.GetParameters())));
}
// Second attempt with an instance method.
targetMethod = targetType.GetMethods(BindingFlags.Public | BindingFlags.Instance)
.Where(method => method.Name == methodName)
.Where(method => method.GetParameters()
.Count(parameter => parameters.Keys.Contains(parameter.Name)) == parameters.Count)
.FirstOrDefault();
if (targetMethod != null)
{
return string.Format("{0}", targetMethod.Invoke(Activator.CreateInstance(targetType), this.GetConvertedValues(parameters, targetMethod.GetParameters())));
}
return originalSource;
}
private string EvaluateControl(string controlName, Dictionary<string, string> attributes, Dictionary<string, string> parameters, string originalSource)
{
switch (controlName)
{
// Note... make this a generic alias for the "code" block??? that would be awesome!
case "listview":
{
return ListView.Render((IEnumerable)this.GetBoundValue(attributes["source"]), parameters["itemTemplate"]);
}
}
return originalSource;
}
private object GetBoundValue(string bindingString)
{
Binding binding = BindingHelper.Parse(bindingString);
if (binding != null)
{
switch (binding.Source.ToLower())
{
case "request":
{
return this.CurrentViewContext.HttpContext.Request[binding.Property];
}
case "routedata":
{
return this.CurrentViewContext.RouteData.Values[binding.Property];
}
case "viewdata":
{
return this.CurrentViewContext.ViewData[binding.Property];
}
}
}
return null;
}
private object[] GetConvertedValues(Dictionary<string, string> parameters, ParameterInfo[] parameterInfos)
{
object[] result = new object[parameterInfos.Length];
for (int i = 0; i < parameterInfos.Length; i++)
{
string currentValue = parameters.First(param => param.Key == parameterInfos[i].Name).Value;
object boundValue = this.GetBoundValue(currentValue);
if (boundValue != null)
{
result[i] = boundValue;
}
else
{
result[i] = Convert.ChangeType(currentValue, parameterInfos[i].ParameterType);
}
}
return result;
}
private SettingsDicitonary GetSettings(Match currentMatch)
{
SettingsDicitonary result = new SettingsDicitonary();
// { // Attributes
Group attributeNames = currentMatch.Groups["AttributeName"];
Group attributeValues = currentMatch.Groups["AttributeValue"];
for (int i = 0, length = attributeNames.Captures.Count; i < length; i++)
{
result.Attributes.Add(attributeNames.Captures[i].Value, attributeValues.Captures[i].Value);
}
// }
// { // Parameters
Group parameterNames = currentMatch.Groups["ParameterName"];
Group parameterValues = currentMatch.Groups["ParameterValue"];
for (int i = 0, length = parameterNames.Captures.Count; i < length; i++)
{
result.Parameters.Add(parameterNames.Captures[i].Value, parameterValues.Captures[i].Value);
}
// }
return result;
}
class SettingsDicitonary
{
public readonly Dictionary<string, string> Attributes = new Dictionary<string, string>();
public readonly Dictionary<string, string> Parameters = new Dictionary<string, string>();
}
}
创建BindingHelper类
public class Binding
{
public string Source { get; set; }
public string Property { get; set; }
}
public static class BindingHelper
{
private static Regex bindingParser = new Regex(@"^/{(?<Source>Binding|ViewData)/s+(?<Property>/w+)/}$", RegexOptions.Compiled | RegexOptions.IgnoreCase | RegexOptions.IgnorePatternWhitespace);
private static Regex propertyBindingParser = new Regex(@"/{Binding/s+(?<Property>/w+)/}", RegexOptions.Compiled | RegexOptions.IgnoreCase | RegexOptions.IgnorePatternWhitespace);
public static Binding Parse(string bindingString)
{
Match bindingMatch = bindingParser.Match(bindingString);
if (bindingMatch.Success)
{
return new Binding
{
Source = bindingMatch.Groups["Source"].Value,
Property = bindingMatch.Groups["Property"].Value,
};
}
else
{
return null;
}
}
public static string PerformBinding(string formatString, object item)
{
Type itemType = item.GetType();
string returnString=propertyBindingParser.Replace(formatString, delegate(Match binding)
{
string property=binding.Groups["Property"].Value;
PropertyInfo info=itemType.GetProperty(property);
string myString=string.Format("{0}",info.GetValue(item, null));
return myString;
});
return returnString;
}
}
创建ListView控件
public static class ListView
{
public static string Render(IEnumerable source, string itemTemplate)
{
StringBuilder resultBuilder = new StringBuilder();
foreach (object item in source)
{
resultBuilder.Append(BindingHelper.PerformBinding(itemTemplate, item));
}
return resultBuilder.ToString();
}
}
创建实用类
public class TimeHelper
{
public static DateTime GetCurrentTime()
{
return DateTime.Now;
}
public static string GetCurrentTime(string formatString)
{
return DateTime.Now.ToString(formatString);
}
}
使用方法
配置全局应用程序
protected void Application_Start()
{
RegisterRoutes(RouteTable.Routes);
ViewEngines.Engines.Clear();
ViewEngines.Engines.Add(new HoTMeaTViewEngine());
}
public ActionResult Index()
{
ViewData["People"] = new Person[]
{
new Person{FirstName="Timothy", LastName="Khouri"},
new Person{FirstName="Jonathan", LastName="Carter"},
new Person{FirstName="Travis", LastName="Sheppard"},
};
return View();
}
public class Person
{
public string FirstName { get; set; }
public string LastName { get; set; }
}
<html>
<head>
<title>Singing Eels - "HoT MeaT" View Engine!</title>
<style type="text/css">
p
{
margin: 0;
padding: 1em;
}
div.sample
{
margin-bottom: 1em;
border: solid 1px #369;
}
</style>
</head>
<body>
<h1>
Singing Eels - "HoT MeaT" View Engine!</h1>
<div class="sample">
<p>
The time on the server is:
<span style="color: #f00;">
<code class="Eels.TimeHelper" method="GetCurrentTime" />
</span>
</p>
</div>
<div class="sample">
<p>
The time on the server (formatted) is:
<span style="color: #f00;">
<code class="Eels.TimeHelper" method="GetCurrentTime">
<formatString>dddd, MMMM dd, yyyy</formatString>
</code>
</span>
</p>
</div>
<div class="sample">
<p>
Check out some data-binding in HoTMeaT:</p>
<ul>
<listView source="{ViewData People}">
<itemTemplate>
<li>{Binding LastName}, {Binding FirstName}</li>
</itemTemplate>
</listView>
</ul>
</div>
</body>
</html>