ฉันสามารถระบุตำแหน่งที่กำหนดเองเพื่อ "ค้นหามุมมอง" ใน ASP.NET MVC ได้หรือไม่


105

ฉันมีเค้าโครงต่อไปนี้สำหรับโครงการ mvc ของฉัน:

  • / ตัวควบคุม
    • /การสาธิต
    • / สาธิต / DemoArea1Controller
    • / สาธิต / DemoArea2Controller
    • ฯลฯ ...
  • / ชม
    • /การสาธิต
    • /Demo/DemoArea1/Index.aspx
    • /Demo/DemoArea2/Index.aspx

อย่างไรก็ตามเมื่อฉันมีสิ่งนี้สำหรับDemoArea1Controller:

public class DemoArea1Controller : Controller
{
    public ActionResult Index()
    {
        return View();
    }
}

ฉันได้รับข้อผิดพลาด "The view" index "หรือไม่พบต้นแบบ" พร้อมตำแหน่งการค้นหาตามปกติ

ฉันจะระบุคอนโทรลเลอร์ในการค้นหาเนมสเปซ "สาธิต" ในโฟลเดอร์ย่อยมุมมอง "สาธิต" ได้อย่างไร


นี่คืออีกตัวอย่างหนึ่งของ ViewEngine ง่ายๆจากแอป MVC Commerce ของ Rob Connery: View Engine Codeและโค้ด Global.asax.cs เพื่อตั้งค่า ViewEngine: Global.asax.csหวังว่านี่จะช่วยได้
Robert Dean

คำตอบ:


122

คุณสามารถขยาย WebFormViewEngine เพื่อระบุตำแหน่งทั้งหมดที่คุณต้องการค้นหาได้อย่างง่ายดาย:

public class CustomViewEngine : WebFormViewEngine
{
    public CustomViewEngine()
    {
        var viewLocations =  new[] {  
            "~/Views/{1}/{0}.aspx",  
            "~/Views/{1}/{0}.ascx",  
            "~/Views/Shared/{0}.aspx",  
            "~/Views/Shared/{0}.ascx",  
            "~/AnotherPath/Views/{0}.ascx"
            // etc
        };

        this.PartialViewLocationFormats = viewLocations;
        this.ViewLocationFormats = viewLocations;
    }
}

อย่าลืมลงทะเบียน view engine โดยแก้ไขเมธอด Application_Start ใน Global.asax.cs ของคุณ

protected void Application_Start()
{
    ViewEngines.Engines.Clear();
    ViewEngines.Engines.Add(new CustomViewEngine());
}

คุณจะเข้าถึงเส้นทางของ Master Page จากเพจ Nested Master ได้อย่างไร เช่นเดียวกับในการตั้งค่าเค้าโครงหน้าต้นแบบที่ซ้อนกันเพื่อค้นหาภายในเส้นทางของ
CustomViewEngine

6
จะไม่ดีกว่าหรือถ้าเราข้ามการล้างเอนจิ้นที่ลงทะเบียนไปแล้วเพียงแค่เพิ่มอันใหม่และ viewLocations จะมี แต่อันใหม่
Prasanna

3
การใช้งานโดยไม่ใช้ ViewEngines.Engines.Clear (); ทั้งหมดทำงานได้ดี หากคุณต้องการใช้ * .cshtml คุณต้องสืบทอดจาก RazorViewEngine
KregHEk

มีวิธีใดบ้างที่เราสามารถเชื่อมโยงตัวเลือก "เพิ่มมุมมอง" และ "ไปที่มุมมอง" จากตัวควบคุมไปยังตำแหน่งมุมมองใหม่ ฉันใช้ Visual Studio 2012
Neville Nazerane

ดังที่กล่าวโดย @Prasanna ไม่จำเป็นต้องล้างเครื่องยนต์ที่มีอยู่เพื่อเพิ่มสถานที่ใหม่ดูคำตอบนี้สำหรับรายละเอียดเพิ่มเติม
Hooman Bahreini

45

ตอนนี้ใน MVC 6 คุณสามารถใช้IViewLocationExpanderอินเทอร์เฟซได้โดยไม่ต้องยุ่งกับเอ็นจิ้นมุมมอง:

public class MyViewLocationExpander : IViewLocationExpander
{
    public void PopulateValues(ViewLocationExpanderContext context) {}

    public IEnumerable<string> ExpandViewLocations(ViewLocationExpanderContext context, IEnumerable<string> viewLocations)
    {
        return new[]
        {
            "/AnotherPath/Views/{1}/{0}.cshtml",
            "/AnotherPath/Views/Shared/{0}.cshtml"
        }; // add `.Union(viewLocations)` to add default locations
    }
}

{0}ชื่อมุมมองเป้าหมายอยู่ที่ไหน{1}- ชื่อคอนโทรลเลอร์และ{2}- ชื่อพื้นที่

คุณสามารถส่งคืนรายการสถานที่ของคุณเองรวมกับค่าเริ่มต้นviewLocations( .Union(viewLocations)) หรือเพียงแค่เปลี่ยน (viewLocations.Select(path => "/AnotherPath" + path) )

ในการลงทะเบียนตัวขยายตำแหน่งมุมมองที่กำหนดเองของคุณใน MVC ให้เพิ่มบรรทัดถัดไปConfigureServicesในวิธีการในStartup.csไฟล์:

public void ConfigureServices(IServiceCollection services)
{
    services.Configure<RazorViewEngineOptions>(options =>
    {
        options.ViewLocationExpanders.Add(new MyViewLocationExpander());
    });
}

3
ฉันหวังว่าฉันจะโหวตได้ถึง 10 โหวตนี้ คือสิ่งที่ต้องการใน Asp.net 5 / MVC 6 สวยงาม มีประโยชน์มากในกรณีของฉัน (และอื่น ๆ ) เมื่อคุณต้องการจัดกลุ่มพื้นที่เป็นพื้นที่พิเศษสำหรับไซต์ขนาดใหญ่หรือการจัดกลุ่มเชิงตรรกะ
drewid

ส่วน Startup.cs ควรเป็น: services.Configure <RazorViewEngineOptions> จะใช้วิธีนี้: โมฆะสาธารณะ ConfigureServices (บริการ IServiceCollection)
OrangeKing89

42

มีวิธีที่ง่ายกว่าการเข้ารหัสเส้นทางลงในตัวสร้างของคุณอย่างหนัก ด้านล่างนี้เป็นตัวอย่างของการขยาย Razor engine เพื่อเพิ่มเส้นทางใหม่ สิ่งหนึ่งที่ฉันไม่แน่ใจทั้งหมดคือเส้นทางที่คุณเพิ่มที่นี่จะถูกแคชหรือไม่:

public class ExtendedRazorViewEngine : RazorViewEngine
{
    public void AddViewLocationFormat(string paths)
    {
        List<string> existingPaths = new List<string>(ViewLocationFormats);
        existingPaths.Add(paths);

        ViewLocationFormats = existingPaths.ToArray();
    }

    public void AddPartialViewLocationFormat(string paths)
    {
        List<string> existingPaths = new List<string>(PartialViewLocationFormats);
        existingPaths.Add(paths);

        PartialViewLocationFormats = existingPaths.ToArray();
    }
}

และ Global.asax.cs ของคุณ

protected void Application_Start()
{
    ViewEngines.Engines.Clear();

    ExtendedRazorViewEngine engine = new ExtendedRazorViewEngine();
    engine.AddViewLocationFormat("~/MyThemes/{1}/{0}.cshtml");
    engine.AddViewLocationFormat("~/MyThemes/{1}/{0}.vbhtml");

    // Add a shared location too, as the lines above are controller specific
    engine.AddPartialViewLocationFormat("~/MyThemes/{0}.cshtml");
    engine.AddPartialViewLocationFormat("~/MyThemes/{0}.vbhtml");

    ViewEngines.Engines.Add(engine);

    AreaRegistration.RegisterAllAreas();
    RegisterRoutes(RouteTable.Routes);
}

สิ่งหนึ่งที่ควรทราบ: ตำแหน่งที่กำหนดเองของคุณจะต้องมีไฟล์ ViewStart.cshtml ในรูท


23

หากคุณต้องการเพิ่มเส้นทางใหม่คุณสามารถเพิ่มไปยังเอ็นจิ้นมุมมองเริ่มต้นและสำรองโค้ดบางบรรทัด:

ViewEngines.Engines.Clear();
var razorEngine = new RazorViewEngine();
razorEngine.MasterLocationFormats = razorEngine.MasterLocationFormats
      .Concat(new[] { 
          "~/custom/path/{0}.cshtml" 
      }).ToArray();

razorEngine.PartialViewLocationFormats = razorEngine.PartialViewLocationFormats
      .Concat(new[] { 
          "~/custom/path/{1}/{0}.cshtml",   // {1} = controller name
          "~/custom/path/Shared/{0}.cshtml" 
      }).ToArray();

ViewEngines.Engines.Add(razorEngine);

เช่นเดียวกับ WebFormEngine


2
สำหรับมุมมอง: ใช้ razorEngine.ViewLocationFormats
Aldentev

13

แทนที่จะใช้คลาสย่อย RazorViewEngine หรือแทนที่ทันทีคุณสามารถเปลี่ยนคุณสมบัติ PartialViewLocationFormats ของ RazorViewEngine ที่มีอยู่ได้ รหัสนี้ไปใน Application_Start:

System.Web.Mvc.RazorViewEngine rve = (RazorViewEngine)ViewEngines.Engines
  .Where(e=>e.GetType()==typeof(RazorViewEngine))
  .FirstOrDefault();

string[] additionalPartialViewLocations = new[] { 
  "~/Views/[YourCustomPathHere]"
};

if(rve!=null)
{
  rve.PartialViewLocationFormats = rve.PartialViewLocationFormats
    .Union( additionalPartialViewLocations )
    .ToArray();
}

2
สิ่งนี้ใช้ได้ผลสำหรับฉันยกเว้นว่าประเภทของเครื่องมือมีดโกนคือ 'FixedRazorViewEngine' แทนที่จะเป็น 'RazorViewEngine' นอกจากนี้ฉันยังโยนข้อยกเว้นหากไม่พบเครื่องยนต์เนื่องจากมันป้องกันไม่ให้แอปพลิเคชันของฉันเริ่มต้นสำเร็จ
ปล้น

3

ล่าสุดฉันตรวจสอบสิ่งนี้ต้องการให้คุณสร้าง ViewEngine ของคุณเอง ฉันไม่รู้ว่าพวกเขาทำให้มันง่ายขึ้นใน RC1 หรือไม่

วิธีการพื้นฐานที่ฉันใช้ก่อน RC แรกคือใน ViewEngine ของฉันเองเพื่อแยกเนมสเปซของคอนโทรลเลอร์และค้นหาโฟลเดอร์ที่ตรงกับส่วนต่างๆ

แก้ไข:

เดินกลับไปและพบรหัส นี่คือแนวคิดทั่วไป

public override ViewEngineResult FindView(ControllerContext controllerContext, string viewName, string masterName)
{
    string ns = controllerContext.Controller.GetType().Namespace;
    string controller = controllerContext.Controller.GetType().Name.Replace("Controller", "");

    //try to find the view
    string rel = "~/Views/" +
        (
            ns == baseControllerNamespace ? "" :
            ns.Substring(baseControllerNamespace.Length + 1).Replace(".", "/") + "/"
        )
        + controller;
    string[] pathsToSearch = new string[]{
        rel+"/"+viewName+".aspx",
        rel+"/"+viewName+".ascx"
    };

    string viewPath = null;
    foreach (var path in pathsToSearch)
    {
        if (this.VirtualPathProvider.FileExists(path))
        {
            viewPath = path;
            break;
        }
    }

    if (viewPath != null)
    {
        string masterPath = null;

        //try find the master
        if (!string.IsNullOrEmpty(masterName))
        {

            string[] masterPathsToSearch = new string[]{
                rel+"/"+masterName+".master",
                "~/Views/"+ controller +"/"+ masterName+".master",
                "~/Views/Shared/"+ masterName+".master"
            };


            foreach (var path in masterPathsToSearch)
            {
                if (this.VirtualPathProvider.FileExists(path))
                {
                    masterPath = path;
                    break;
                }
            }
        }

        if (string.IsNullOrEmpty(masterName) || masterPath != null)
        {
            return new ViewEngineResult(
                this.CreateView(controllerContext, viewPath, masterPath), this);
        }
    }

    //try default implementation
    var result = base.FindView(controllerContext, viewName, masterName);
    if (result.View == null)
    {
        //add the location searched
        return new ViewEngineResult(pathsToSearch);
    }
    return result;
}

1
มันง่ายกว่ามาก Subclass WebFormsViewEngine จากนั้นเพิ่มไปยังอาร์เรย์ของเส้นทางที่ค้นหาในตัวสร้างของคุณแล้ว
Craig Stuntz

ดีแล้วที่รู้. ครั้งสุดท้ายที่ฉันต้องการแก้ไขคอลเลกชันนั้นเป็นไปไม่ได้ในลักษณะนั้น
Joel

ควรค่าแก่การกล่าวถึงว่าคุณต้องตั้งค่าตัวแปร "baseControllerNamespace" เป็นเนมสเปซตัวควบคุมพื้นฐานของคุณ (เช่น "Project.Controllers") แต่อย่างอื่นก็ทำตามที่ฉันต้องการ 7 ปีหลังจากโพสต์
ต้นแบบ 14

3

ลองทำสิ่งนี้:

private static void RegisterViewEngines(ICollection<IViewEngine> engines)
{
    engines.Add(new WebFormViewEngine
    {
        MasterLocationFormats = new[] {"~/App/Views/Admin/{0}.master"},
        PartialViewLocationFormats = new[] {"~/App/Views/Admin//{1}/{0}.ascx"},
        ViewLocationFormats = new[] {"~/App/Views/Admin//{1}/{0}.aspx"}
    });
}

protected void Application_Start()
{
    RegisterViewEngines(ViewEngines.Engines);
}

3

หมายเหตุ: สำหรับ ASP.NET MVC 2 จะมีเส้นทางตำแหน่งเพิ่มเติมที่คุณจะต้องตั้งค่าสำหรับมุมมองใน 'พื้นที่'

 AreaViewLocationFormats
 AreaPartialViewLocationFormats
 AreaMasterLocationFormats

การสร้างเครื่องมือมุมมองสำหรับพื้นที่มีการอธิบายไว้ในบล็อกของฟิล

หมายเหตุ: นี่เป็นรุ่นตัวอย่าง 1 ดังนั้นอาจมีการเปลี่ยนแปลงได้


1

คำตอบส่วนใหญ่ที่นี่ให้ล้างสถานที่ที่มีอยู่โดยการโทรViewEngines.Engines.Clear()แล้วเพิ่มกลับเข้าไปอีกครั้ง ... ไม่จำเป็นต้องทำเช่นนี้

เราสามารถเพิ่มสถานที่ตั้งใหม่ให้กับสถานที่ที่มีอยู่ดังที่แสดงด้านล่าง:

// note that the base class is RazorViewEngine, NOT WebFormViewEngine
public class ExpandedViewEngine : RazorViewEngine
{
    public ExpandedViewEngine()
    {
        var customViewSubfolders = new[] 
        {
            // {1} is conroller name, {0} is action name
            "~/Areas/AreaName/Views/Subfolder1/{1}/{0}.cshtml",
            "~/Areas/AreaName/Views/Subfolder1/Shared/{0}.cshtml"
        };

        var customPartialViewSubfolders = new[] 
        {
            "~/Areas/MyAreaName/Views/Subfolder1/{1}/Partials/{0}.cshtml",
            "~/Areas/MyAreaName/Views/Subfolder1/Shared/Partials/{0}.cshtml"
        };

        ViewLocationFormats = ViewLocationFormats.Union(customViewSubfolders).ToArray();
        PartialViewLocationFormats = PartialViewLocationFormats.Union(customPartialViewSubfolders).ToArray();

        // use the following if you want to extend the master locations
        // MasterLocationFormats = MasterLocationFormats.Union(new[] { "new master location" }).ToArray();   
    }
}

ตอนนี้คุณสามารถกำหนดค่าโครงการของคุณเพื่อใช้ข้างต้นRazorViewEngineใน Global.asax:

protected void Application_Start()
{
    ViewEngines.Engines.Add(new ExpandedViewEngine());
    // more configurations
}

ดูtutoral นี้สำหรับข้อมูลเพิ่มเติม


0

ฉันทำแบบนี้ใน MVC 5 ฉันไม่ต้องการล้างตำแหน่งเริ่มต้น

ระดับตัวช่วย:

namespace ConKit.Helpers
{
    public static class AppStartHelper
    {
        public static void AddConKitViewLocations()
        {
            // get engine
            RazorViewEngine engine = ViewEngines.Engines.OfType<RazorViewEngine>().FirstOrDefault();
            if (engine == null)
            {
                return;
            }

            // extend view locations
            engine.ViewLocationFormats =
                engine.ViewLocationFormats.Concat(new string[] {
                    "~/Views/ConKit/{1}/{0}.cshtml",
                    "~/Views/ConKit/{0}.cshtml"
                }).ToArray();

            // extend partial view locations
            engine.PartialViewLocationFormats =
                engine.PartialViewLocationFormats.Concat(new string[] {
                    "~/Views/ConKit/{0}.cshtml"
                }).ToArray();
        }
    }
}

จากนั้นใน Application_Start:

// Add ConKit View locations
ConKit.Helpers.AppStartHelper.AddConKitViewLocations();
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.