Tôi có thể chỉ định một vị trí tùy chỉnh để “tìm kiếm lượt xem” trong ASP.NET MVC không?


105

Tôi có bố cục sau cho dự án mvc của mình:

  • / Bộ điều khiển
    • /Bản giới thiệu
    • / Demo / DemoArea1Controller
    • / Demo / DemoArea2Controller
    • Vân vân...
  • /Lượt xem
    • /Bản giới thiệu
    • /Demo/DemoArea1/Index.aspx
    • /Demo/DemoArea2/Index.aspx

Tuy nhiên, khi tôi có điều này cho DemoArea1Controller:

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

Tôi gặp lỗi "Không thể tìm thấy chỉ mục" chế độ xem "hoặc không thể tìm thấy chỉ mục chính" với các vị trí tìm kiếm thông thường.

Làm cách nào để chỉ định bộ điều khiển đó trong tìm kiếm không gian tên "Demo" trong thư mục con dạng xem "Demo"?


Đây là một mẫu ViewEngine đơn giản khác từ ứng dụng MVC Commerce của Rob Connery: Mã View Engine Và mã Global.asax.cs để đặt ViewEngine: Global.asax.cs Hy vọng điều này sẽ hữu ích.
Robert Dean,

Câu trả lời:


121

Bạn có thể dễ dàng mở rộng WebFormViewEngine để chỉ định tất cả các vị trí bạn muốn xem:

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;
    }
}

Đảm bảo rằng bạn nhớ đăng ký công cụ xem bằng cách sửa đổi phương thức Application_Start trong Global.asax.cs của bạn

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

Làm cách nào để bạn có thể truy cập đường dẫn của Trang chính từ trang Chính lồng nhau? Như trong việc thiết bố trí trang chủ lồng nhau để tìm kiếm trong các đường dẫn của CustomViewEngine
Drahcir

6
Sẽ không tốt hơn nếu chúng ta bỏ qua Xóa các công cụ đã đăng ký và chỉ thêm một công cụ mới và viewLocations sẽ chỉ có những công cụ mới?
Prasanna

3
Triển khai không có ViewEngines.Engines.Clear (); Tất cả đều hoạt động tốt. Nếu bạn muốn sử dụng * .cshtml bạn phải kế thừa từ RazorViewEngine
KregHEk

có cách nào để chúng tôi có thể liên kết các tùy chọn "thêm chế độ xem" và "chuyển đến chế độ xem" từ bộ điều khiển với các vị trí chế độ xem mới không? tôi đang sử dụng Visual Studio 2012
Neville Nazerane

Như đã đề cập bởi @Prasanna, không cần phải xóa các công cụ hiện có để thêm các vị trí mới, hãy xem câu trả lời này để biết thêm chi tiết.
Hooman Bahreini

44

Bây giờ trong MVC 6, bạn có thể triển khai IViewLocationExpandergiao diện mà không cần phải làm phiền với các công cụ xem:

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
    }
}

đâu {0}là tên khung nhìn đích, {1}- tên bộ điều khiển và {2}- tên khu vực.

Bạn có thể trả lại danh sách các vị trí của riêng mình, hợp nhất nó với default viewLocations( .Union(viewLocations)) hoặc chỉ thay đổi chúng ( viewLocations.Select(path => "/AnotherPath" + path)).

Để đăng ký trình mở rộng vị trí chế độ xem tùy chỉnh của bạn trong MVC, hãy thêm các dòng tiếp theo vào ConfigureServicesphương thức trong Startup.cstệp:

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

3
Tôi ước tôi có thể bỏ phiếu này với 10 phiếu bầu. Là chính xác những gì cần thiết trong Asp.net 5 / MVC 6. Đẹp. Rất hữu ích trong trường hợp của tôi (và những trường hợp khác) khi bạn muốn nhóm các khu vực thành siêu khu vực cho các trang web lớn hơn hoặc các nhóm hợp lý.
drawid

Phần Startup.cs nên là: services.Configure <RazorViewEngineOptions> Nó đi trong phương pháp này: public void ConfigureServices (dịch vụ IServiceCollection)
OrangeKing89

42

Thực sự có rất nhiều phương pháp dễ dàng hơn so với mã hóa cứng các đường dẫn vào hàm tạo của bạn. Dưới đây là một ví dụ về việc mở rộng công cụ Razor để thêm các đường dẫn mới. Một điều tôi không hoàn toàn chắc chắn là liệu các đường dẫn bạn thêm vào đây có được lưu vào bộ nhớ đệm hay không:

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();
    }
}

Và Global.asax.cs của bạn

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);
}

Một điều cần lưu ý: vị trí tùy chỉnh của bạn sẽ cần tệp ViewStart.cshtml trong thư mục gốc của nó.


23

Nếu bạn chỉ muốn thêm các đường dẫn mới, bạn có thể thêm vào các công cụ chế độ xem mặc định và dành một số dòng mã:

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);

Ứng dụng tương tự WebFormEngine


2
Đối với Chế độ xem: sử dụng razorEngine.ViewLocationFormats.
Aldentev

13

Thay vì phân lớp con RazorViewEngine hoặc thay thế nó hoàn toàn, bạn chỉ có thể thay đổi thuộc tính PartialViewLocationFormats của RazorViewEngine hiện có. Mã này có trong 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
Điều này phù hợp với tôi, ngoại trừ loại động cơ dao cạo là 'FixedRazorViewEngine' thay vì 'RazorViewEngine'. Ngoài ra, tôi đưa ra một ngoại lệ nếu không tìm thấy động cơ vì nó ngăn ứng dụng của tôi khởi chạy thành công.
Rob

3

Lần cuối tôi kiểm tra, điều này yêu cầu bạn phải xây dựng ViewEngine của riêng mình. Tôi không biết liệu họ có làm cho nó dễ dàng hơn trong RC1 hay không.

Cách tiếp cận cơ bản mà tôi đã sử dụng trước RC đầu tiên, trong ViewEngine của riêng tôi, để phân chia không gian tên của bộ điều khiển và tìm kiếm các thư mục phù hợp với các phần.

BIÊN TẬP:

Đã quay lại và tìm thấy mã. Đây là ý tưởng chung.

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
Nó thực sự dễ dàng hơn nhiều. Lớp con WebFormsViewEngine và sau đó chỉ cần thêm vào mảng đường dẫn mà nó đã tìm kiếm trong hàm tạo của bạn.
Craig Stuntz

Tốt để biết. Lần cuối cùng tôi cần sửa đổi bộ sưu tập đó, nó không thể thực hiện được theo cách đó.
Joel

Đáng nói là bạn cần đặt biến "baseControllerNamespace" thành không gian tên bộ điều khiển cơ sở của bạn (ví dụ: "Project.Controllers"), nhưng nếu không thì đã thực hiện chính xác những gì tôi cần, 7 năm sau khi được đăng.
nguyên mẫu 14

3

Hãy thử một cái gì đó như sau:

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

Lưu ý: đối với ASP.NET MVC 2, chúng có các đường dẫn vị trí bổ sung mà bạn sẽ cần đặt cho các chế độ xem trong 'Khu vực'.

 AreaViewLocationFormats
 AreaPartialViewLocationFormats
 AreaMasterLocationFormats

Tạo một công cụ xem cho một Khu vực được mô tả trên blog của Phil .

Lưu ý: Đây là bản phát hành xem trước 1 nên có thể thay đổi.


1

Hầu hết các câu trả lời ở đây, xóa các vị trí hiện có bằng cách gọi ViewEngines.Engines.Clear()và sau đó thêm lại chúng vào ... không cần phải làm điều này.

Chúng tôi có thể chỉ cần thêm các vị trí mới vào các vị trí hiện có, như được hiển thị bên dưới:

// 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();   
    }
}

Bây giờ bạn có thể định cấu hình dự án của mình để sử dụng những điều trên RazorViewEnginetrong Global.asax:

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

Xem hướng dẫn này để biết thêm thông tin.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.