Adding view engines

This commit is contained in:
Pranav K 2014-01-15 18:02:59 -08:00
parent 1ba47fa480
commit b918cb8170
26 changed files with 694 additions and 4 deletions

View File

@ -0,0 +1,18 @@

namespace Microsoft.AspNet.Mvc.Razor
{
public class HtmlString
{
private readonly string _input;
public HtmlString(string input)
{
_input = input;
}
public override string ToString()
{
return _input;
}
}
}

View File

@ -0,0 +1,10 @@

using System.Threading.Tasks;
namespace Microsoft.AspNet.Mvc.Razor
{
public interface IVirtualPathFactory
{
Task<object> CreateInstance(string virtualPath);
}
}

View File

@ -0,0 +1,49 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Threading.Tasks;
namespace Microsoft.AspNet.Mvc.Razor
{
public class MetadataVirtualPathProvider : IVirtualPathFactory
{
private readonly Dictionary<string, Type> _viewMetadata;
public MetadataVirtualPathProvider(Assembly assembly)
{
var metadataType = assembly.GetType("ViewMetadata");
if (metadataType != null)
{
object metadata = metadataType.GetProperty("Metadata", BindingFlags.Static | BindingFlags.Public)
.GetValue(obj: null);
_viewMetadata = new Dictionary<string, Type>((Dictionary<string, Type>)metadata, StringComparer.OrdinalIgnoreCase);
}
else
{
// Code to support precompiled views generated via RazorGenerator
_viewMetadata = assembly.GetExportedTypes()
.Where(type => typeof(RazorView).IsAssignableFrom(type))
.ToDictionary(type => GetVirtualPath(type), StringComparer.OrdinalIgnoreCase);
}
}
public Task<object> CreateInstance(string virtualPath)
{
Type type;
object view = null;
if (_viewMetadata.TryGetValue(virtualPath, out type))
{
view = (RazorView)Activator.CreateInstance(type);
}
return Task.FromResult(view);
}
private static string GetVirtualPath(Type type)
{
VirtualPathAttribute attribute = type.GetCustomAttribute<VirtualPathAttribute>();
return attribute.VirtualPath;
}
}
}

View File

@ -0,0 +1,79 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="12.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{224A14D0-ECA7-441C-AE89-B6E66A57EF9B}</ProjectGuid>
<OutputType>Library</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>Microsoft.AspNet.Mvc.Razor</RootNamespace>
<AssemblyName>Microsoft.AspNet.Mvc.Razor</AssemblyName>
<TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<ItemGroup>
<Reference Include="Microsoft.Owin, Version=2.0.2.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\packages\Microsoft.Owin.2.0.2\lib\net45\Microsoft.Owin.dll</HintPath>
</Reference>
<Reference Include="Owin, Version=1.0.0.0, Culture=neutral, PublicKeyToken=f0ebd12fd5e55cc5, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\packages\Owin.1.0\lib\net40\Owin.dll</HintPath>
</Reference>
<Reference Include="System" />
<Reference Include="System.Core" />
<Reference Include="System.Xml.Linq" />
<Reference Include="System.Data.DataSetExtensions" />
<Reference Include="Microsoft.CSharp" />
<Reference Include="System.Data" />
<Reference Include="System.Xml" />
</ItemGroup>
<ItemGroup>
<Compile Include="HtmlString.cs" />
<Compile Include="IVirtualPathFactory.cs" />
<Compile Include="MetadataVirtualPathProvider.cs" />
<Compile Include="RazorView.cs" />
<Compile Include="RazorViewEngine.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="VirtualPathAttribute.cs" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Microsoft.AspNet.CoreServices\Microsoft.AspNet.CoreServices.csproj">
<Project>{ec38534c-a2d1-413f-97d1-55eef5d2fb71}</Project>
<Name>Microsoft.AspNet.CoreServices</Name>
</ProjectReference>
<ProjectReference Include="..\Microsoft.AspNet.Mvc\Microsoft.AspNet.Mvc.csproj">
<Project>{2a0c26f1-0240-4ae1-ae00-4691c291b122}</Project>
<Name>Microsoft.AspNet.Mvc</Name>
</ProjectReference>
</ItemGroup>
<ItemGroup>
<None Include="packages.config" />
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.
<Target Name="BeforeBuild">
</Target>
<Target Name="AfterBuild">
</Target>
-->
</Project>

View File

@ -0,0 +1,36 @@
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly: AssemblyTitle("Microsoft.AspNet.Mvc.Razor")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("Microsoft.AspNet.Mvc.Razor")]
[assembly: AssemblyCopyright("Copyright © 2014")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
// Setting ComVisible to false makes the types in this assembly not visible
// to COM components. If you need to access a type in this assembly from
// COM, set the ComVisible attribute to true on that type.
[assembly: ComVisible(false)]
// The following GUID is for the ID of the typelib if this project is exposed to COM
[assembly: Guid("af993404-738b-4c26-94af-38bec7c4e487")]
// Version information for an assembly consists of the following four values:
//
// Major Version
// Minor Version
// Build Number
// Revision
//
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]

View File

@ -0,0 +1,104 @@
using System;
using System.Globalization;
using System.IO;
using System.Net;
using System.Text;
using System.Threading.Tasks;
using Microsoft.AspNet.CoreServices;
using Microsoft.Owin;
namespace Microsoft.AspNet.Mvc.Razor
{
public abstract class RazorView : IView
{
public IOwinContext Context { get; set; }
public object Model { get; set; }
public string Layout { get; set; }
protected TextWriter Output { get; set; }
private string BodyContent { get; set; }
public async Task RenderAsync(ViewContext context, TextWriter writer)
{
Model = context.Model;
var contentBuilder = new StringBuilder(1024);
using (var bodyWriter = new StringWriter(contentBuilder))
{
Output = bodyWriter;
Execute();
}
string bodyContent = contentBuilder.ToString();
if (!String.IsNullOrEmpty(Layout))
{
await RenderLayoutAsync(context, writer, bodyContent);
}
else
{
await writer.WriteAsync(bodyContent);
}
}
private async Task RenderLayoutAsync(ViewContext context, TextWriter writer, string bodyContent)
{
var virtualPathFactory = context.ServiceProvider.GetService<IVirtualPathFactory>();
RazorView razorView = (RazorView)(await virtualPathFactory.CreateInstance(Layout));
if (razorView == null)
{
string message = String.Format(CultureInfo.CurrentCulture, "The layout view '{0}' could not be located.", Layout);
throw new InvalidOperationException(message);
}
razorView.BodyContent = bodyContent;
await razorView.RenderAsync(context, writer);
}
protected abstract void Execute();
public virtual void Write(object value)
{
WriteTo(Output, value);
}
public virtual void WriteTo(TextWriter writer, object content)
{
if (content != null)
{
var htmlString = content as HtmlString;
if (htmlString != null)
{
writer.Write(content.ToString());
}
else
{
WebUtility.HtmlEncode(content.ToString(), writer);
}
}
}
public void WriteLiteral(object value)
{
WriteLiteralTo(Output, value);
}
public virtual void WriteLiteralTo(TextWriter writer, object text)
{
if (text != null)
{
writer.Write(text.ToString());
}
}
protected virtual void RenderBody()
{
if (BodyContent != null)
{
WriteLiteral(BodyContent);
}
}
}
}

View File

@ -0,0 +1,59 @@
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Threading.Tasks;
namespace Microsoft.AspNet.Mvc.Razor
{
public class RazorViewEngine : IViewEngine
{
private static readonly string[] _viewLocationFormats = new[]
{
"~/Views/{1}/{0}.cshtml",
"~/Views/Shared/{0}.cshtml",
};
private readonly IActionDescriptorProvider _actionDescriptorProvider;
private readonly IVirtualPathFactory _virtualPathFactory;
public RazorViewEngine(IActionDescriptorProvider actionDescriptorProvider,
IVirtualPathFactory virtualPathFactory)
{
_actionDescriptorProvider = actionDescriptorProvider;
_virtualPathFactory = virtualPathFactory;
}
public IEnumerable<string> ViewLocationFormats
{
get { return _viewLocationFormats; }
}
public async Task<ViewEngineResult> FindView(RequestContext requestContext, string viewName)
{
var actionDescriptor = _actionDescriptorProvider.CreateDescriptor(requestContext) as ControllerBasedActionDescriptor;
if (actionDescriptor == null)
{
return null;
}
if (String.IsNullOrEmpty(viewName))
{
viewName = actionDescriptor.ActionName;
}
string controllerName = actionDescriptor.ControllerName;
var searchedLocations = new List<string>(_viewLocationFormats.Length);
for (int i = 0; i < _viewLocationFormats.Length; i++)
{
string path = String.Format(CultureInfo.InvariantCulture, _viewLocationFormats[i], viewName, controllerName);
RazorView view = (RazorView)(await _virtualPathFactory.CreateInstance(path));
if (view != null)
{
return ViewEngineResult.Found(view);
}
searchedLocations.Add(path);
}
return ViewEngineResult.NotFound(searchedLocations);
}
}
}

View File

@ -0,0 +1,15 @@
using System;
namespace Microsoft.AspNet.Mvc.Razor
{
[AttributeUsage(AttributeTargets.Class, Inherited = false, AllowMultiple = false)]
public class VirtualPathAttribute : Attribute
{
public VirtualPathAttribute(string virtualPath)
{
VirtualPath = virtualPath;
}
public string VirtualPath { get; private set; }
}
}

View File

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="Microsoft.Owin" version="2.0.2" targetFramework="net45" />
<package id="Owin" version="1.0" targetFramework="net45" />
</packages>

View File

@ -9,6 +9,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MvcSample", "MvcSample\MvcS
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.AspNet.CoreServices", "Microsoft.AspNet.CoreServices\Microsoft.AspNet.CoreServices.csproj", "{EC38534C-A2D1-413F-97D1-55EEF5D2FB71}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.AspNet.Mvc.Razor", "Microsoft.AspNet.Mvc.Razor\Microsoft.AspNet.Mvc.Razor.csproj", "{224A14D0-ECA7-441C-AE89-B6E66A57EF9B}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@ -27,6 +29,10 @@ Global
{EC38534C-A2D1-413F-97D1-55EEF5D2FB71}.Debug|Any CPU.Build.0 = Debug|Any CPU
{EC38534C-A2D1-413F-97D1-55EEF5D2FB71}.Release|Any CPU.ActiveCfg = Release|Any CPU
{EC38534C-A2D1-413F-97D1-55EEF5D2FB71}.Release|Any CPU.Build.0 = Release|Any CPU
{224A14D0-ECA7-441C-AE89-B6E66A57EF9B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{224A14D0-ECA7-441C-AE89-B6E66A57EF9B}.Debug|Any CPU.Build.0 = Debug|Any CPU
{224A14D0-ECA7-441C-AE89-B6E66A57EF9B}.Release|Any CPU.ActiveCfg = Release|Any CPU
{224A14D0-ECA7-441C-AE89-B6E66A57EF9B}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE

View File

@ -1,9 +1,19 @@
using System;
using Microsoft.AspNet.CoreServices;
namespace Microsoft.AspNet.Mvc
{
public class ActionResultHelper : IActionResultHelper
{
private readonly IServiceProvider _serviceProvider;
private readonly IViewEngine _viewEngine;
public ActionResultHelper(IServiceProvider serviceProvider, IViewEngine viewEngine)
{
_serviceProvider = serviceProvider;
_viewEngine = viewEngine;
}
public IActionResult Content(string value)
{
return new ContentResult
@ -28,7 +38,11 @@ namespace Microsoft.AspNet.Mvc
public IActionResult View()
{
throw new NotImplementedException();
return new ViewResult(_serviceProvider, _viewEngine)
{
ViewName = null,
Model = null
};
}
}
}

View File

@ -61,6 +61,9 @@
<Compile Include="DefaultContentNegotiator.cs" />
<Compile Include="Extensions\IEnumerableExtensions.cs" />
<Compile Include="Formatters\JQeryMvcForUrlEncodedFormatter.cs" />
<Compile Include="View\CompositeViewEngine.cs" />
<Compile Include="View\IView.cs" />
<Compile Include="View\IViewEngine.cs" />
<Compile Include="FormattingUtilities.cs" />
<Compile Include="Extensions\TypeExtensions.cs" />
<Compile Include="IOwinContentNegotiator.cs" />
@ -84,6 +87,9 @@
<Compile Include="MvcServices.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="Routing\IRouteData.cs" />
<Compile Include="View\ViewContext.cs" />
<Compile Include="View\ViewEngineResult.cs" />
<Compile Include="View\ViewResult.cs" />
</ItemGroup>
<ItemGroup>
<None Include="packages.config" />

View File

@ -20,6 +20,11 @@ namespace Microsoft.AspNet.Mvc
_serviceProvider = serviceProvider ?? MvcServices.Create();
}
//public ServiceProvider ServiceProvider
//{
// get { return (ServiceProvider)_serviceProvider; }
//}
public Task ExecuteAsync(IOwinContext context, IRouteData routeData)
{
var requestContext = new RequestContext(context, routeData);

View File

@ -6,7 +6,7 @@ namespace Microsoft.AspNet.Mvc
{
public static class MvcServices
{
public static IServiceProvider Create()
public static ServiceProvider Create()
{
var services = new ServiceProvider();
DoCallback((service, implementation) => services.Add(service, implementation));
@ -21,6 +21,7 @@ namespace Microsoft.AspNet.Mvc
callback(typeof(IActionResultFactory), typeof(ActionResultFactory));
callback(typeof(IContentNegotiator), typeof(DefaultContentNegotiator));
// TODO: Should be many
callback(typeof(IActionDescriptorProvider), typeof(ActionDescriptorProvider));
callback(typeof(IActionInvokerProvider), typeof(ActionInvokerProvider));

View File

@ -0,0 +1,56 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace Microsoft.AspNet.Mvc.View
{
public class CompositeViewEngine : IViewEngine
{
private readonly List<IViewEngine> _viewEngines;
public CompositeViewEngine()
: this(Enumerable.Empty<IViewEngine>())
{
}
public CompositeViewEngine(IEnumerable<IViewEngine> viewEngines)
{
_viewEngines = viewEngines.ToList();
}
public void Insert(int index, IViewEngine viewEngine)
{
_viewEngines.Insert(index, viewEngine);
}
public void Add(IViewEngine viewEngine)
{
_viewEngines.Add(viewEngine);
}
public async Task<ViewEngineResult> FindView(RequestContext controllerContext, string viewName)
{
if (_viewEngines.Count == 0)
{
return ViewEngineResult.NotFound(Enumerable.Empty<string>());
}
var searchedPaths = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
for (int i = 0; i < _viewEngines.Count; i++)
{
ViewEngineResult result = await _viewEngines[i].FindView(controllerContext, viewName);
if (result.Success)
{
return result;
}
foreach (string location in result.SearchedLocations)
{
searchedPaths.Add(location);
}
}
return ViewEngineResult.NotFound(searchedPaths);
}
}
}

View File

@ -0,0 +1,10 @@
using System.IO;
using System.Threading.Tasks;
namespace Microsoft.AspNet.Mvc
{
public interface IView
{
Task RenderAsync(ViewContext context, TextWriter writer);
}
}

View File

@ -0,0 +1,9 @@
using System.Threading.Tasks;
namespace Microsoft.AspNet.Mvc
{
public interface IViewEngine
{
Task<ViewEngineResult> FindView(RequestContext requestContext, string viewName);
}
}

View File

@ -0,0 +1,19 @@
using System;
using Microsoft.AspNet.Mvc.Routing;
using Microsoft.Owin;
namespace Microsoft.AspNet.Mvc
{
public class ViewContext : RequestContext
{
public ViewContext(IOwinContext context, IRouteData routeData, object model) :
base(context, routeData)
{
Model = model;
}
public object Model { get; private set; }
public IServiceProvider ServiceProvider { get; set; }
}
}

View File

@ -0,0 +1,47 @@
using System;
using System.Collections.Generic;
namespace Microsoft.AspNet.Mvc
{
public class ViewEngineResult
{
private ViewEngineResult()
{
}
public IEnumerable<string> SearchedLocations { get; private set; }
public IView View { get; private set; }
public bool Success
{
get { return View != null; }
}
public static ViewEngineResult NotFound(IEnumerable<string> searchedLocations)
{
if (searchedLocations == null)
{
throw new ArgumentNullException("searchedLocations");
}
return new ViewEngineResult
{
SearchedLocations = searchedLocations
};
}
public static ViewEngineResult Found(IView view)
{
if (view == null)
{
throw new ArgumentNullException("view");
}
return new ViewEngineResult
{
View = view
};
}
}
}

View File

@ -0,0 +1,61 @@
using System;
using System.Globalization;
using System.IO;
using System.Text;
using System.Threading.Tasks;
using Microsoft.AspNet.CoreServices;
namespace Microsoft.AspNet.Mvc
{
public class ViewResult : IActionResult
{
private readonly IServiceProvider _serviceProvider;
private readonly IViewEngine _viewEngine;
public ViewResult(IServiceProvider serviceProvider, IViewEngine viewEngine)
{
_serviceProvider = serviceProvider;
_viewEngine = viewEngine;
}
public string ViewName {get; set; }
public object Model { get; set; }
public async Task ExecuteResultAsync(RequestContext context)
{
if (context == null)
{
throw new ArgumentNullException("context");
}
IView view = await FindView(context, ViewName);
using (view as IDisposable)
{
context.HttpContext.Response.ContentType = "text/html";
using (var writer = new StreamWriter(context.HttpContext.Response.Body, Encoding.UTF8, 1024, leaveOpen: true))
{
var viewContext = new ViewContext(context.HttpContext, context.RouteData, Model)
{
ServiceProvider = _serviceProvider
};
await view.RenderAsync(viewContext, writer);
}
}
}
private async Task<IView> FindView(RequestContext requestContext, string viewName)
{
ViewEngineResult result = await _viewEngine.FindView(requestContext, viewName);
if (!result.Success)
{
string locationsText = String.Join(Environment.NewLine, result.SearchedLocations);
const string message = @"The view &apos;{0}&apos; was not found. The following locations were searched:{1}.";
throw new InvalidOperationException(String.Format(CultureInfo.CurrentCulture, message, viewName, locationsText));
}
return result.View;
}
}
}

View File

@ -47,5 +47,10 @@ namespace MvcSample
return user;
}
public IActionResult MyView()
{
return Result.View();
}
}
}

View File

@ -70,8 +70,18 @@
<Compile Include="Models\Class1.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="Startup.cs" />
<Compile Include="Views\Layout.cs" />
<Compile Include="Views\MyView.cs" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Microsoft.AspNet.CoreServices\Microsoft.AspNet.CoreServices.csproj">
<Project>{ec38534c-a2d1-413f-97d1-55eef5d2fb71}</Project>
<Name>Microsoft.AspNet.CoreServices</Name>
</ProjectReference>
<ProjectReference Include="..\Microsoft.AspNet.Mvc.Razor\Microsoft.AspNet.Mvc.Razor.csproj">
<Project>{224a14d0-eca7-441c-ae89-b6e66a57ef9b}</Project>
<Name>Microsoft.AspNet.Mvc.Razor</Name>
</ProjectReference>
<ProjectReference Include="..\Microsoft.AspNet.Mvc\Microsoft.AspNet.Mvc.csproj">
<Project>{2a0c26f1-0240-4ae1-ae00-4691c291b122}</Project>
<Name>Microsoft.AspNet.Mvc</Name>

View File

@ -1,6 +1,8 @@
using System;
using System.Threading.Tasks;
using Microsoft.AspNet.CoreServices;
using Microsoft.AspNet.Mvc;
using Microsoft.AspNet.Mvc.Razor;
using Microsoft.AspNet.Mvc.Routing;
using Microsoft.Owin;
using Owin;
@ -15,8 +17,12 @@ namespace MvcSample
{
app.UseErrorPage();
var handler = new MvcHandler();
var serviceProvider = MvcServices.Create();
serviceProvider.AddInstance<IVirtualPathFactory>(new MetadataVirtualPathProvider(GetType().Assembly));
serviceProvider.Add<IViewEngine, RazorViewEngine>();
var handler = new MvcHandler(serviceProvider);
app.Run(async context =>
{
// Pretending to be routing

23
MvcSample/ViewMetadata.cs Normal file
View File

@ -0,0 +1,23 @@
using System;
using System.Collections.Generic;
public class ViewMetadata
{
public static Dictionary<string, Type> Metadata
{
get
{
return new Dictionary<string, Type>
{
{
"~/Views/Home/MyView.cshtml",
typeof(MvcSample.Views.MyView)
},
{
"~/Views/Shared/_Layout.cshtml",
typeof(MvcSample.Views.Layout)
}
};
}
}
}

20
MvcSample/Views/Layout.cs Normal file
View File

@ -0,0 +1,20 @@
using System;
using Microsoft.AspNet.Mvc.Razor;
namespace MvcSample.Views
{
[VirtualPath("~/Views/Shared/_Layout.cshtml")]
public class Layout : RazorView
{
protected override void Execute()
{
WriteLiteral("<html>");
WriteLiteral("<body>");
WriteLiteral("<h1>Hello world</h1>");
WriteLiteral("<div id=\"main\"");
RenderBody();
WriteLiteral("</div>");
WriteLiteral("</body></html>");
}
}
}

17
MvcSample/Views/MyView.cs Normal file
View File

@ -0,0 +1,17 @@
using System;
using Microsoft.AspNet.Mvc.Razor;
namespace MvcSample.Views
{
[VirtualPath("~/Views/Home/MyView.cshtml")]
public class MyView : RazorView
{
protected override void Execute()
{
Layout = "~/Views/Shared/_Layout.cshtml";
WriteLiteral("<div style=\"border: 1px solid black\">The time is now");
Write(new HtmlString(DateTime.UtcNow.ToString()));
WriteLiteral("</div>");
}
}
}