在ASP.NET MVC中检索当前视图名称?

42

我有一个局部视图(控件)在多个视图页面中使用,并且我需要将当前视图的名称传递回控制器,以便在出现验证错误时重新绘制原始视图。

可以通过以下方式解决(在控制器方法中)。

var viewName = "Details"; // or whatever
ViewData["viewName"] = viewName;
return(View(viewName, customer));

然后在部分视图中,将其呈现为

<input type="hidden" name="viewName" 
    value="<%=Html.Encode(ViewData["viewName"])%>" />

问题是 - 是否有一些属性或语法可以直接检索它,而不是通过控制器设置它?我已经尝试了显而易见的:

<input type="hidden" name="viewName" 
    value="<%=Html.Encode(this.Name)%>" />

但这并不起作用。我在这里漏掉了什么吗?

谢谢。


1
这有点棘手。我认为应该是可能的,因为在控制器测试期间可以从操作结果获取视图名称...但我不清楚如何从视图本身内部检索它。期待答案... - womp
10个回答

37

如果你不介意将你的代码与特定的视图引擎绑定在一起,那么你可以查看ViewContext.View属性,并将其强制转换为WebFormView

var viewPath = ((WebFormView)ViewContext.View).ViewPath;
我相信这将最终获得视图名称。
编辑:Haacked 绝对是正确的;为了使事情变得更加整洁,我已经将逻辑封装在一个扩展方法中,如下所示:
public static class IViewExtensions {
    public static string GetWebFormViewName(this IView view) {
        if (view is WebFormView) {
            string viewUrl = ((WebFormView)view).ViewPath;
            string viewFileName = viewUrl.Substring(viewUrl.LastIndexOf('/'));
            string viewFileNameWithoutExtension = Path.GetFileNameWithoutExtension(viewFileName);
            return (viewFileNameWithoutExtension);
        } else {
            throw (new InvalidOperationException("This view is not a WebFormView"));
        }
    }
}

看起来它完全符合我想要的。


1
只是一个跟进的问题,因为这已经快两年了:在ASP.NET MVC 3中获取Razor视图名称难道没有更简单的方法吗? - Leniel Maccaferri
你能帮助这可怜的灵魂吗 :P ? https://dev59.com/B1fUa4cB1Zd3GeqPMu9f#6246173 - Leniel Maccaferri
3
可以将类型转换为'BuildManagerCompiledView',而不是WebFormView。RazorView也是从它继承的。 - Remco Ros
2
好的,我有点迷失了。你能发一个例子展示如何在视图中使用这个扩展吗?我怎么得到IView?我正在使用MVC 3。 - NightOwl888

17

我曾经遇到过同样的问题,这是我解决问题的方法:

namespace System.Web.Mvc
{
    public static class HtmlHelperExtensions
    {
        public static string CurrentViewName(this HtmlHelper html)
        {
            return System.IO.Path.GetFileNameWithoutExtension(
                ((RazorView)html.ViewContext.View).ViewPath
            );
        }
    }
}

然后在视图中:

var name = Html.CurrentViewName();

或者简单地说

@Html.CurrentViewName()

除非您通过Html.Partial()包含其他页面,否则此方法无法正常工作,因为您获取的是包含页面而不是被包含页面。 - ekkis

9
如果你只想要操作名称,那么这个方法可以解决问题:
public static string ViewName(this HtmlHelper html)
{
    return html.ViewContext.RouteData.GetRequiredString("action");
}

8
我需要视图名称,而不是操作名称。视图并未包含在路由数据中,它是由控制器方法的返回值确定的。 - Dylan Beattie
2
-1 请看我对安东尼·约翰斯顿答案的评论。这对新手来说完全是误导性的!! - JotaBe

5
最简单的解决方案是使用ViewBag。
public ActionResult Index()
    {
        ViewBag.CurrentView = "Index";
        return View();
    }

在 cshtml 页面上
@{
var viewName = ViewBag.CurrentView;
}

或者,

((RazorView)ViewContext.View).ViewPath

1
但是,当使用还呈现部分视图的视图时,这不是一种可行的方法。我正在研究这个问题,因为我正在编写一个自定义工具提示插件,它足够智能,只需在元素上使用自定义属性即可获取当前视图、属性名称并检索要在元素的“信息”图标中显示的元数据和对另一个字段依赖的业务规则,供最终用户使用。因此,由于呈现部分视图,两个单独的视图可能具有相似的字段名称,用于一个视图中的两个不同的视图模型。将在一次返回的通用视图上工作。 - Casey ScriptFu Pharr

4
如果您正在寻找ASP.NET Core的解决方案,您可以使用以下内容:
@System.IO.Path.GetFileNameWithoutExtension(ViewContext.View.Path)

这将返回当前视图名称。

4

如果你想从一个部分视图中获取文件名,似乎下面的方法可以实现:

public static class HtmlHelperExtensions
{
    public static string GetViewFileName(this HtmlHelper html, object view)
    {
        return @"\\"+ view.GetType().FullName.Replace("ASP._Page_", "").Replace("_cshtml", ".cshtml").Replace("_", @"\\");
    }
}

在部分视图中,您应该像这样做:
var filename = Html.GetViewFileName(this);

或者这个:
@Html.GetViewFileName(this)

如果这不是一个好的方法,请留言评论 - 有没有其他可选方案?

这个对于包含页面来说很有效。我只建议您可以像这样获取当前视图的名称 html.ViewDataContainer.GetType().FullName,这样可以避免将视图传递到方法中。 - ekkis

0

你不应该使用像Nerd Dinner实现的验证方法吗?

这样你就不需要做所有这些事情,而只需返回View。


0
我最近遇到了同样的问题,而我想出的代码片段解决了我的问题。 唯一的问题是在某些情况下,Request.UrlReferrer可能为null。有点晚了,但似乎对我有用,而且我覆盖了Request.UrlReferrer不为null的所有情况。
 if (Request.UrlReferrer != null)
 {
      var viewUrl = Request.UrlReferrer.ToString();
      var actionResultName = viewUrl.Substring(viewUrl.LastIndexOf('/'));
      var viewNameWithoutExtension = actionResultName.TrimStart('/');
 }

0
你可以使用 Razor:
在你的 View header 中
@{
    ViewData["Title"] = "YourViewName";    
}

在您的视图 HTML 中
@{
   var _nameCurrentView = ViewContext.ViewData["Title"];
}

在你的HTML中使用变量@_nameCurrentView

  <li class="breadcrumb-item active">@_nameCurrentView</li>

或在您的操作中使用

  ViewData["Title"]

-3

刚写了一篇关于这个的博客

http://www.antix.co.uk/A-Developers-Blog/Targeting-Pages-with-CSS-in-ASP.NET-MVC

  /// <summary>
  /// <para>Get a string from the route data</para>
  /// </summary>
  public static string RouteString(
      this ViewContext context, string template) {

   foreach (var value in context.RouteData.Values) {

    template = template.Replace(string.Format("{{{0}}}",
            value.Key.ToLower()),
            value.Value == null
                ? string.Empty
                : value.Value.ToString().ToLower());
   }

   return template;
  }

用法

<body class="<%= ViewContext.RouteString("{controller}_{action}") %>">

编辑:是的,正如第一条评论所述,这不会给您视图名称,它会给您控制器和操作。但是将其保留在此处,因为知道它并不重要。


这是一个有趣的想法,我喜欢它。唯一的问题是控制器可以根据某些状态有条件地返回视图,因此这并不一定映射到特定的视图。 - NightOwl888
5
如前面的评论所述,根据行动的结果返回不同的视图非常普遍。或者因为多个行动共享一个视图。因此,这种方法非常不可靠。 - JotaBe

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接