Render partial from different folder (not shared) - c#

How can I have a view render a partial (user control) from a different folder?
With preview 3 I used to call RenderUserControl with the complete path, but whith upgrading to preview 5 this is not possible anymore.
Instead we got the RenderPartial method, but it's not offering me the functionality I'm looking for.

Just include the path to the view, with the file extension.
Razor:
#Html.Partial("~/Views/AnotherFolder/Messages.cshtml", ViewData.Model.Successes)
ASP.NET engine:
<% Html.RenderPartial("~/Views/AnotherFolder/Messages.ascx", ViewData.Model.Successes); %>
If that isn't your issue, could you please include your code that used to work with the RenderUserControl?

In my case I was using MvcMailer (https://github.com/smsohan/MvcMailer) and wanted to access a partial view from another folder, that wasn't in "Shared." The above solutions didn't work, but using a relative path did.
#Html.Partial("../MyViewFolder/Partials/_PartialView", Model.MyObject)

If you are using this other path a lot of the time you can fix this permanently without having to specify the path all of the time. By default, it is checking for partial views in the View folder and in the Shared folder. But say you want to add one.
Add a class to your Models folder:
public class NewViewEngine : RazorViewEngine {
private static readonly string[] NEW_PARTIAL_VIEW_FORMATS = new[] {
"~/Views/Foo/{0}.cshtml",
"~/Views/Shared/Bar/{0}.cshtml"
};
public NewViewEngine() {
// Keep existing locations in sync
base.PartialViewLocationFormats = base.PartialViewLocationFormats.Union(NEW_PARTIAL_VIEW_FORMATS).ToArray();
}
}
Then in your Global.asax.cs file, add the following line:
ViewEngines.Engines.Add(new NewViewEngine());

For readers using ASP.NET Core 2.1 or later and wanting to use Partial Tag Helper syntax, try this:
<partial name="~/Views/Folder/_PartialName.cshtml" />
The tilde (~) is optional.
The information at https://learn.microsoft.com/en-us/aspnet/core/mvc/views/partial?view=aspnetcore-3.1#partial-tag-helper is helpful too.

For a user control named myPartial.ascx located at Views/Account folder write like this:
<%Html.RenderPartial("~/Views/Account/myPartial.ascx");%>

I've created a workaround that seems to be working pretty well. I found the need to switch to the context of a different controller for action name lookup, view lookup, etc. To implement this, I created a new extension method for HtmlHelper:
public static IDisposable ControllerContextRegion(
this HtmlHelper html,
string controllerName)
{
return new ControllerContextRegion(html.ViewContext.RouteData, controllerName);
}
ControllerContextRegion is defined as:
internal class ControllerContextRegion : IDisposable
{
private readonly RouteData routeData;
private readonly string previousControllerName;
public ControllerContextRegion(RouteData routeData, string controllerName)
{
this.routeData = routeData;
this.previousControllerName = routeData.GetRequiredString("controller");
this.SetControllerName(controllerName);
}
public void Dispose()
{
this.SetControllerName(this.previousControllerName);
}
private void SetControllerName(string controllerName)
{
this.routeData.Values["controller"] = controllerName;
}
}
The way this is used within a view is as follows:
#using (Html.ControllerContextRegion("Foo")) {
// Html.Action, Html.Partial, etc. now looks things up as though
// FooController was our controller.
}
There may be unwanted side effects for this if your code requires the controller route component to not change, but in our code so far, there doesn't seem to be any negatives to this approach.

The VirtualPathProviderViewEngine, on which the WebFormsViewEngine is based, is supposed to support the "~" and "/" characters at the front of the path so your examples above should work.
I noticed your examples use the path "~/Account/myPartial.ascx", but you mentioned that your user control is in the Views/Account folder. Have you tried
<%Html.RenderPartial("~/Views/Account/myPartial.ascx");%>
or is that just a typo in your question?

you should try this
~/Views/Shared/parts/UMFview.ascx
place the ~/Views/ before your code

Create a Custom View Engine and have a method that returns a ViewEngineResult
In this example you just overwrite the _options.ViewLocationFormats and add your folder directory
:
public ViewEngineResult FindView(ActionContext context, string viewName, bool isMainPage)
{
var controllerName = context.GetNormalizedRouteValue(CONTROLLER_KEY);
var areaName = context.GetNormalizedRouteValue(AREA_KEY);
var checkedLocations = new List<string>();
foreach (var location in _options.ViewLocationFormats)
{
var view = string.Format(location, viewName, controllerName);
if (File.Exists(view))
{
return ViewEngineResult.Found("Default", new View(view, _ViewRendering));
}
checkedLocations.Add(view);
}
return ViewEngineResult.NotFound(viewName, checkedLocations);
}
Example: https://github.com/AspNetMonsters/pugzor

Try using RenderAction("myPartial","Account");

Related

ASP.NET MVC Modules

I need to create a nuget package which will contain shared views, controllers, js, and css files to be used across multiple other projects. Essentially a modular set of things like checkout or search pages that can be dropped into other site projects.
All the research I've done so far points to utilizing precompiled views with RazorGenerator but doesn't say much about the controllers, js, and css files.
Ideally a module's views and other files should be able to be overridden by the consuming host project but the files themselves should not be directly editable within the host project. Much like dlls referenced when other nuget packages are added.
The answers and posts about this type of subject I've found so far seem a bit dated.
Is there a cleaner, modern solution for creating an ASP.NET MVC module nuget package so that fully working pages are able to be shared across projects?
Controllers
Use area's and register those area's. Possibly this is not natively supported and you might need to overwrite some parts in mvc4. look at:
How do I register a controller that has been created in an AREA
http://netmvc.blogspot.be/2012/03/aspnet-mvc-4-webapi-support-areas-in.html
As long as the dll's are loaded in you can always register all classes that are subclasses of Controller with reflection (on application startup).
Razor
Precompiling is possible, but only really adviseable in dotnet core since it is a first class citizen there.
You could also add the views as content which is injected into the project.
Downside:
On update it overwrites the views (if you changed them you lost changes)
Pros:
On update you can merge both changes in git
Easily change the already existing razor pages
I found a solution that works for us. We have not yet fully implemented this so some unforeseen issues may still arise.
First, create an MVC project with the needed views, controllers, javascript, etc needed to match your package requirements. Each static file and view must be set as embedded resources in the project.
Then, add a class to serve these files on a virtual path provider. This will allow a consuming project to access the static files and views as if they were within the same project.
To enable custom routing an implementation of the RouteBase class will be required. This implementation needs to accept a string property for which virtual routes are based to allow the host to apply whichever route prefix is desired. For our example, the property will default to Booking with the associated architecture of views within our project to match.
Both the RouteBase implementation and the VirtualPath class will be instantiated within a setup method. This will allow a consuming project to call a single method to setup the booking engine. This method will take in the sites Route Collection and dynamic route property to append the custom routes. The method will also register the VirtualPathProvider to the HostingEnvironment object.
The consuming host can also override views and any other static file by simply having a file in a location within the host project that matches the path of the file or view in the booking engine.
Some Code Examples
RouteBase method which returns correct route values if an incoming route matches a virtual route.
public override RouteData GetRouteData(HttpContextBase httpContext)
{
RouteData result = null;
// Trim the leading slash
var path = httpContext.Request.Path.Substring(1);
// Get the page that matches.
var page = GetPageList(httpContext)
.Where(x => x.VirtualPath.Equals(path))
.FirstOrDefault();
if (page != null)
{
result = new RouteData(this, new MvcRouteHandler());
// Optional - make query string values into route values.
AddQueryStringParametersToRouteData(result, httpContext);
result.Values["controller"] = page.Controller;
result.Values["action"] = page.Action;
}
// IMPORTANT: Always return null if there is no match.
// This tells .NET routing to check the next route that is registered.
return result;
}
RouteBase Virtual Route to NuGet Package Route mapping. New PageInfo objects created with dynamic virtual path string and references to real controller and action names. These are then stored in the http context cache.
private IEnumerable<PageInfo> GetPageList(HttpContextBase httpContext)
{
string key = "__CustomPageList";
var pages = httpContext.Cache[key];
if (pages == null)
{
lock (synclock)
{
pages = httpContext.Cache[key];
if (pages == null)
{
pages = new List<PageInfo>()
{
new PageInfo()
{
VirtualPath = string.Format("{0}/Contact", BookingEngine.Route),
Controller = "Home",
Action = "Contact"
},
};
httpContext.Cache.Insert(
key: key,
value: pages,
dependencies: null,
absoluteExpiration: System.Web.Caching.Cache.NoAbsoluteExpiration,
slidingExpiration: TimeSpan.FromMinutes(1),
priority: System.Web.Caching.CacheItemPriority.NotRemovable,
onRemoveCallback: null);
}
}
}
return (IEnumerable<PageInfo>)pages;
}
Booking Engine class setup method which does all the instantiating needed for the assembly.
public class BookingEngine
{
public static string Route = "Booking";
public static void Setup(RouteCollection routes, string route)
{
Route = route;
HostingEnvironment.RegisterVirtualPathProvider(
new EmbeddedVirtualPathProvider());
routes.Add(
name: "CustomPage",
item: new CustomRouteController());
}
}
EmbeddedVirtualFile
public override CacheDependency GetCacheDependency(string virtualPath, virtualPathDependencies, DateTime utcStart)
{
string embedded = _GetEmbeddedPath(virtualPath);
// not embedded? fall back
if (string.IsNullOrEmpty(embedded))
return base.GetCacheDependency(virtualPath,
virtualPathDependencies, utcStart);
// there is no cache dependency for embedded resources
return null;
}
public override bool FileExists(string virtualPath)
{
string embedded = _GetEmbeddedPath(virtualPath);
// You can override the embed by placing a real file at the virtual path...
return base.FileExists(virtualPath) || !string.IsNullOrEmpty(embedded);
}
public override VirtualFile GetFile(string virtualPath)
{
// You can override the embed by placing a real file at the virtual path...
if (base.FileExists(virtualPath))
return base.GetFile(virtualPath);
string embedded = _GetEmbeddedPath(virtualPath);
if (string.IsNullOrEmpty(embedded))
return null;
return new EmbeddedVirtualFile(virtualPath, GetType().Assembly
.GetManifestResourceStream(embedded));
}
private string _GetEmbeddedPath(string path)
{
if (path.StartsWith("~/"))
path = path.Substring(1);
path = path.Replace(BookingEngine.Route, "/");
//path = path.ToLowerInvariant();
path = System.Reflection.Assembly.GetExecutingAssembly().GetName().Name + path.Replace('/', '.');
// this makes sure the "virtual path" exists as an embedded resource
return GetType().Assembly.GetManifestResourceNames()
.Where(o => o == path).FirstOrDefault();
}
Nested Virtual File Class
public class EmbeddedVirtualFile : VirtualFile
{
private Stream _stream;
public EmbeddedVirtualFile(string virtualPath,
Stream stream) : base(virtualPath)
{
if (null == stream)
throw new ArgumentNullException("stream");
_stream = stream;
}
public override Stream Open()
{
return _stream;
}
}
A lot of the code we are using comes from the following links
Embedded Files - https://www.ianmariano.com/2013/06/11/embedded-razor-views-in-mvc-4/
RouteBase implementation - Multiple levels in MVC custom routing

Correct way to reference a Session / Global user variable in MVC3

So I have a Session variable which is set during the first user login
Session["ClientID"]
Basically this is used for theming (so the ClientID sets the theme/brand to appear on a website). Looking at the code applying
(Guid)Session["ClientID"]
All over the place just seems dirty and error prone, what the best design pattern to use to get the variable globally recognized. So I can do
this.CurrentClientID
or something similar on all MVC Pages. In theory I could overload the Controller class with a Custom class providing this ID, but I'm not sure how I would expose this to the View as well.
Any pointers to the best solution would be gratefully received!
No idea what you mean globally, but an extension method to the ControllerBase class would render it accessible in all your controllers:
public static class ControllerExtensions
{
public static Guid GetCurrentClientID(this ControllerBase controller)
{
return (Guid)controller.ControllerContext.HttpContext.Session["ClientID"];
}
}
and now inside each controller you can access it:
public ActionResult Foo()
{
Guid id = this.GetCurrentClientID();
...
}
And if you want it to be even more globally available make it an extension method to the HttpContextBase class:
public static class ControllerExtensions
{
public static Guid GetCurrentClientID(this HttpContextBase context)
{
return (Guid)context.Session["ClientID"];
}
}
now everywhere you have access to the HttpContext (which is pretty much everywhere in an ASP.NET application) you simply use the extension method. For example inside a view:
#Html.ActionLink("foo link", "foo", new { clientid = Context.GetCurrentClientID() })

MVC3 Custom view engine

I have a custom View Engine that derives from WebFormViewEngine. There's a lot of stuff going on in here, mostly caching. I want to be able to use WebFormViewEngine AND RazorViewEngine at the same time, is this possible? Ideally I'd like to do;
ViewEngines.Add(new MyViewEngine<WebFormsViewEngine>());
ViewEngines.Add(new MyViewEngine<RazorViewEngine>());
if a .ascx/.aspx/.master file exists then use WebForms, otherwise use Razor is a .cshtml file exists.
EDIT: I should of worded my question better. As my custom view engine derives from WebFormViewEngine it obviously uses WebForms, I can't derive from two classes. I can derive from RazorViewEngine but then I'll loose WebForms. I can duplicate my code entirely, derive from RazorViewEngine and edit the views file extensions, etc. but as I said I've got a lot of custom code in my view engine and would be duplicating hundreds of lines.
WebFormViewEngine and RazorViewEngine derive from BuildManagerViewEngine which in turn implements IViewEngine. The problem with that is I have to implement methods CreatePartialView() and CreateView() but how would I know what to return (WebForms/Razor?) using generics?
It's possible, but not recommended way because it introduces some mess into your project. You can read good article on it: Using Multiple View Engines
It shouldn't matter.
By default, MVC3 includes both the WebFormsViewEngine and the RazorViewEngine, these are both registered automatically. I believe they are ordered as WebFormsViewEngine and then RazorViewEngine also, which means the web forms view will be checked first. If it finds a view, it will use it. If it cannot, it will use the next view engine, which is the Razor view engine.
Have you tried removing your custom view engine and just leaving the default configuration?
In the end I ended up with creating an abstract class: public abstract class MyViewEngine : BuildManagerViewEngine, IViewEngine { } and then implement/override the FindView and FindPartialView methods (which had my caching code in). I had an abstract method public abstract void SetSearchPaths(); that was called in my ctor.
I then created another class that derived from MyViewEngine for WebForms specifically:
public class MyViewEngineWebForms : MyViewEngine
{
public override void SetSearchPaths()
{
base.MasterLocationFormats = new string[] { "~/Skins/{2}/Views/{1}/{0}.master", "~/Skins/{2}/Views/Shared/{0}.master" };
base.ViewLocationFormats = new string[] { "~/Skins/{2}/Views/{1}/{0}.aspx", "~/Skins/{2}/Views/Shared/{0}.aspx", "~/Skins/Shared/Views/{0}.aspx" };
base.PartialViewLocationFormats = new string[] { "~/Skins/{2}/Views/{1}/{0}.ascx", "~/Skins/{2}/Views/Shared/{0}.ascx", "~/Skins/Shared/PartialViews/{0}.ascx" };
base.FileExtensions = new string[] { "aspx", "ascx", "master" };
}
protected override IView CreatePartialView(ControllerContext controllerContext, string partialPath)
{
return new WebFormView(controllerContext, partialPath, null, base.ViewPageActivator);
}
protected override IView CreateView(ControllerContext controllerContext, string viewPath, string masterPath)
{
return new WebFormView(controllerContext, viewPath, masterPath, base.ViewPageActivator);
}
}
and did exactly the same for Razor but change the file extensions to .cshtml/.vbhtml and change WebFormView to RazorView. Add them to the ViewEngine collection:
ViewEngines.Engines.Clear();
ViewEngines.Engines.Add(new SeeTicketsWebFormsViewEngine());
ViewEngines.Engines.Add(new SeeTicketsRazorViewEngine());
and now they work perfectly again, side by side with all of my custom caching logic.

ASP.NET MVC: Views using a model type that is loaded by MEF can't be found by the view engine

I'm attempting to create a framework for allowing controllers and views to be dynamically imported into an MVC application. Here's how it works so far:
I'm using .NET 4, ASP.NET MVC 3 RC and the Razor ViewEngine
Controllers are exported and imported using MEF per project - I call a set of controllers and views from a given project a "Module"
Assemblies discovered using MEF are dynamically referenced by the BuildManager using a pre-application start method and BuildManager.AddReferencedAssembly.
Binaries (from exporting project) and Views are copied into the target project's folder structure using a build event
Controllers are selected using a custom controller factory which inherits from DefaultControllerFactory and overrides GetControllerType()
Views are selected using a custom view engine which inherits from RazorViewEngine and overrides GetView() and GetPartialView() to allow it to look for views in Module-specific view directories
Everything works so far except for views using a strongly typed model. Views that use the dynamic model work fine, but when I specify a model type using #model, I get a YSOD that says "The view 'Index' or its master was not found".
When debugging my ViewEngine implementation, I can see that:
this.VirtualPathProvider.FileExists(String.Format(this.ViewLocationFormats[2], viewName, controllerContext.RouteData.GetRequiredString("controller"))) returns true, while
this.FileExists(controllerContext, String.Format(this.ViewLocationFormats[2], viewName, controllerContext.RouteData.GetRequiredString("controller"))) returns false.
Looking in Reflector, the RazorViewEngine implementation of FileExists() ultimately winds up doing this:
return (BuildManager.GetObjectFactory(virtualPath, false) != null);
However, I can't view BuildManager.GetObjectFactory() from Reflector because it's hidden somehow.
I'm suspecting that it has something to do with the fact that the model type is a type that is loaded from MEF, but since I'm already referencing the assemblies discovered by MEF from BuildManager, I'm out of leads. Can anyone provide a little more insight into what might be going on?
Update:
Turns out I was using an outdated version of Reflector from before .NET 4. I can see GetObjectFactory() now, but I can't really seem to find anything helpful. I've tried adding this into my FindView() overload:
try
{
var path = String.Format(this.ViewLocationFormats[2], viewName, controllerContext.RouteData.GetRequiredString("controller"));
var objFactory = System.Web.Compilation.BuildManager.GetObjectFactory(virtualPath: path, throwIfNotFound: true);
}
catch
{
}
Unfortunately, objFactory ends up null, and no exception gets thrown. All the bits that deal with compilation errors are part of private methods or types so I can't debug any of that, but it even seems like they'd end up throwing an exception, which doesn't seem to be happening. Looks like I'm at a dead end again. Help!
Update 2
I've discovered that at the point where FindView() is being called, if I call AppDomain.CurrentDomain.GetAssemblies(), the assembly that the model type is in is included. However, I cannot load the type using Type.GetType().
Update 3
Here's what I'm seeing:
Update 4
Here's the ViewEngine implementation:
using System;
using System.Linq;
using System.Web.Mvc;
using System.Web.Hosting;
using System.Web.Compilation;
namespace Site.Admin.Portal
{
public class ModuleViewEngine : RazorViewEngine
{
private static readonly String[] viewLocationFormats = new String[]
{
"~/Views/{0}/{{1}}/{{0}}.aspx",
"~/Views/{0}/{{1}}/{{0}}.ascx",
"~/Views/{0}/{{1}}/{{0}}.cshtml",
"~/Views/{0}/Shared/{{0}}.aspx",
"~/Views/{0}/Shared/{{0}}.ascx",
"~/Views/{0}/Shared/{{0}}.cshtml"
};
public ModuleViewEngine(IModule module)
{
this.Module = module;
var formats = viewLocationFormats.Select(f => String.Format(f, module.Name)).ToArray();
this.ViewLocationFormats = formats;
this.PartialViewLocationFormats = formats;
this.AreaViewLocationFormats = formats;
this.AreaPartialViewLocationFormats = formats;
this.AreaMasterLocationFormats = formats;
}
public IModule Module { get; private set; }
public override ViewEngineResult FindPartialView(ControllerContext controllerContext, String partialViewName, Boolean useCache)
{
var moduleName = controllerContext.RouteData.GetRequiredString("module");
if (moduleName.Equals(this.Module.Name, StringComparison.InvariantCultureIgnoreCase))
{
return base.FindPartialView(controllerContext, partialViewName, useCache);
}
else return new ViewEngineResult(new String[0]);
}
public override ViewEngineResult FindView(ControllerContext controllerContext, String viewName, String masterName, Boolean useCache)
{
var moduleName = controllerContext.RouteData.GetRequiredString("module");
if (moduleName.Equals(this.Module.Name, StringComparison.InvariantCultureIgnoreCase))
{
var baseResult = base.FindView(controllerContext, viewName, masterName, useCache);
return baseResult;
}
else return new ViewEngineResult(new String[0]);
}
}
}
Based on Update 2, I'm guessing what you've got is an explicitly loaded copy of your assembly (that is, it was loaded through some other method than Load, like LoadFrom). Explicitly loaded assemblies are set off aside into a special place, because they are not allowed to satisfy implicit type requirements. The rules for Fusion (the assembly loader) can be pretty arcane and hard to understand.
I agree with Matthew's assessment that, to get this to work, your DLL is going to have to be in /bin or else it will never be able to satisfy the implicit type requirement.
The imported libaries aren't in the /bin directory so aren't probed when trying to resolve references. I discovered a work around which I published in my MVC + MEF article (Part 2). Essentially you need to add your directories where your extensions sit to the probing path of the AppDomain.
Essentially where I am building my container:
/// <summary>
/// Creates the composition container.
/// </summary>
/// <returns></returns>
protected virtual CompositionContainer CreateCompositionContainer()
{
var catalog = new AggregateCatalog();
catalog.Catalogs.Add(new DirectoryCatalog(MapPath("~/bin")));
var config = CompositionConfigurationSection.GetInstance();
if (config != null && config.Catalogs != null) {
config.Catalogs
.Cast<CatalogConfigurationElement>()
.ForEach(c =>
{
if (!string.IsNullOrEmpty(c.Path)) {
string path = c.Path;
if (path.StartsWith("~"))
path = MapPath(path);
foreach (var directoryCatalog in GetDirectoryCatalogs(path)) {
// Register our path for probing.
RegisterPath(directoryCatalog.FullPath);
// Add the catalog.
catalog.Catalogs.Add(directoryCatalog);
}
}
});
}
var provider = new DynamicInstantiationExportProvider();
var container = new CompositionContainer(catalog, provider);
provider.SourceProvider = container;
return container;
}
I register all the directories of catalogs in the current domain:
/// <summary>
/// Registers the specified path for probing.
/// </summary>
/// <param name="path">The probable path.</param>
private void RegisterPath(string path)
{
AppDomain.CurrentDomain.AppendPrivatePath(path);
}
I believe the same should work for MVC3.
UPDATE: Correct me if I am wrong, but I don't believe that ViewEngines are instantiated once per request, you create a single instance that you register with MVC. Because of this only one IModule instance is ever used with your ViewEngine, so if a path doesn't match that first IModule.Name it won't be found? Does that make sense?

Reusing ViewPage/HtmlHelper in seperate project in ASP.NET MVC using C#

I want to use the ViewPage/HtmlHelper class in the System.Web.Mvc namespace in a seperate project. I imported the relevant libraries and then tried this:
using System.Web.Mvc;
using System.Web.Mvc.Resources;
using System.Web.Mvc.Html;
public static class Display
{
public static string CheckBox()
{
ViewPage viewPage = new ViewPage();
return viewPage.Html.CheckBox("Test");
}
}
Which I call like this in another class that includes my display class:
string Checkbox = Display.CheckBox():
This compiles just fine, however when I run it I get:
System.NullReferenceException: Object
reference not set to an instance of an
object.
I simply want to use the HtmlHelper's extension methods as is, e.g: page.Html.ActionLink(), page.Html.Radionbutton() etc. How can I resolve this problem?
Are you trying to call your custom CheckBox() from a different place than the view? Please don't do that. The philosophy behind ASP.NET MVC is that your controller should prepare all data for the view, then the view should decide on how to render it.
If you redesign your method to be an extension method, you could do that:
public static class Display // class name really don't matter for extension methods
{
public static string CheckBox(this HtmlHelper html)
{
return html.CheckBox("Test");
}
}
Within the view:
<%= Html.CheckBox() %>
Note that this may cause a naming conflict with existing extension methods. One way to avoid that is to design something like:
New code in view:
<%= Html.Display().CheckBox() %>
New extension code:
public static DisplayExtension
{
public static Display(this HtmlHelper html)
{
return new Display(html);
}
}
public class Display // no longer static
{
private readonly HtmlHelper html;
public string Display(HtmlHelper html)
{
this.html = html;
}
public string CheckBox()
{
return html.CheckBox("Test");
}
}
The Html helpers require that the ViewContext property of the ViewPage is set. Typically, this is not the case within a controller or other class code.
Could you package this CheckBox within an ASCX file and reference it there by other views with a Html.RenderPartial method call?

Categories