Share ASP.NET MVC UserControls in a ClassLibrary

Share ASP.NET MVC UserControls in a ClassLibrary is not an obvious task to do. We need to make some registration in the HostingEnvironment and create some classes to search for the file in the shared assembly. This post will explain how to do it step by step, this question was asked in the StackOverflow and you can see my answer here.

The first step is to create a class that will get the content from the files embedded in a given assembly. See the AspNetVirtualFile class below.

public class AspNetVirtualFile : System.Web.Hosting.VirtualFile
{
    private string path;

    public AspNetVirtualFile(string virtualPath)
        : base(virtualPath)
    {
        path = System.Web.VirtualPathUtility.ToAppRelative(virtualPath);
    }

    public override System.IO.Stream Open()
    {
        var parts = path.Split(new char[] { '/' }, StringSplitOptions.RemoveEmptyEntries);

        var assemblyName = parts[2];
        var resourceName = parts[3];

        assemblyName = System.IO.Path.Combine(System.Web.HttpRuntime.BinDirectory, assemblyName);
        var assembly = System.Reflection.Assembly.LoadFile(assemblyName + ".dll");

        if (assembly != null)
        {
            return assembly.GetManifestResourceStream(resourceName);
        }

        return null;
    }
}

Now we need to create the virtual file provider that will check if the path is an embedded resource and use the virtual file. You can see that I will always look for a directory UserControls inside the assembly, change it if you want.

public class AspNetVirtualProvider : System.Web.Hosting.VirtualPathProvider
{
    public const string StartPath = "~/UserControls/";
    public const string Path = "~/UserControls/{0}/{1}";

    public AspNetVirtualProvider() { }

    private bool IsEmbeddedResourcePath(string virtualPath)
    {
        try
        {
            var checkPath = System.Web.VirtualPathUtility.ToAppRelative(virtualPath);
            return checkPath.StartsWith(StartPath, StringComparison.InvariantCultureIgnoreCase);
        }
        catch
        {

        }
        return false;
    }

    public override bool FileExists(string virtualPath)
    {
        return IsEmbeddedResourcePath(virtualPath) || base.FileExists(virtualPath);
    }

    public override System.Web.Hosting.VirtualFile GetFile(string virtualPath)
    {
        if (IsEmbeddedResourcePath(virtualPath))
        {
            return new AspNetVirtualFile(virtualPath);
        }
        else
        {
            return base.GetFile(virtualPath);
        }
    }

    public override System.Web.Caching.CacheDependency GetCacheDependency(string virtualPath, System.Collections.IEnumerable virtualPathDependencies, DateTime utcStart)
    {
        if (IsEmbeddedResourcePath(virtualPath))
        {
            return null;
        }
        else
        {
            return base.GetCacheDependency(virtualPath, virtualPathDependencies, utcStart);
        }
    }
}

The next step is to register the new file provider in the HostingEnvironment. Open the Global.asax and in the Application_Start write the code below.

System.Web.Hosting.HostingEnvironment.RegisterVirtualPathProvider(new AspNetVirtualProvider());

Now we will create and HtmlHelper extension, just to help when using the shared UserControl.

using System.Web.Mvc.Html;
public static class HtmlHelperExtensions
{
    public static void SharedRenderPartial(this System.Web.Mvc.HtmlHelper h, string assemblyName, string fileName, object model)
    {
        h.RenderPartial(String.Format(AspNetVirtualProvider.Path, assemblyName, fileName), model);
    }
}

OK, now it´s time to create our UserControl. I have a ClassLibrary project called SharedUserControls. In this project I´ll create a folder called UserControls.

Project Structure

Now I´ll create a cshtml file inside it and change the build action to Embedded Resource.

UserControl File

Below the contents for the UserControl file, as you can see we need to reference manually the namespaces adding using and when the file is edited, is necessary to build the project again to reflect in the browser.

@inherits System.Web.Mvc.WebViewPage<string>
@using System.Web.Mvc
@using System.Web.WebPages
@using System.Web.Mvc.Html

<input type="text" name="test" id="test" value="@Model" />

To use our new UserControl write this line in your view.

@{ Html.SharedRenderPartial("SharedUserControls", String.Format("SharedUserControls.UserControls.{0}", "SomeUserControl.cshtml"), "value"); }
Share ASP.NET MVC UserControls in a ClassLibrary

Deixe um comentário

Preencha os seus dados abaixo ou clique em um ícone para log in:

Logotipo do WordPress.com

Você está comentando utilizando sua conta WordPress.com. Sair /  Alterar )

Foto do Google

Você está comentando utilizando sua conta Google. Sair /  Alterar )

Imagem do Twitter

Você está comentando utilizando sua conta Twitter. Sair /  Alterar )

Foto do Facebook

Você está comentando utilizando sua conta Facebook. Sair /  Alterar )

Conectando a %s