在MVC4中动态添加内容到布局

时间:2013-08-08 13:36:59

标签: asp.net-mvc-4

我有一个主布局文件,我的所有视图都在其中呈现。我想使用以下规则向此布局的特定区域中的用户显示消息:

  1. 在我们的维护期内,用户登录后会在每个页面上显示警告。
  2. 在我们的维护期限附近,用户登录后会在每个页面上显示警告(但内容与#1不同)
  3. 在正常时段,仅在登录时显示消息,而不是在后续页面中显示消息
  4. 我可能希望从其他视图/控制器向此区域添加其他消息,但不希望必须知道我是否重叠维护警告
  5. 我真的在努力做正确的事。现在我有类似的东西:

    public class LayoutController : Controller
    {
        [ChildActionOnly]
        public IHtmlString GetMarginMessages() {
            loadMaintenanceMessages();
            var messages = this.ViewBag.MarginMessages.ToSingleString();
            return new HtmlString(messages);
        }
    
        private List<string> loadMaintenanceMessages() {
            if (withinMaintenancePeriod)
            {
                this.ViewBag.MarginMessages.Add("foo");
            } 
            else if (nearMaintenancePeriod) {
                this.ViewBag.MarginMessages.Add("bar");
            }
        }
    }
    

    然后在我的布局中我可以:

    <div id="marginMessage">@Html.Action("GetMarginMessages")</div>
    

    在其他页面或控制器中,我可以:

    this.ViewBag.MarginMessages.Add("Something")    // or have it go through a helper of sorts
    

    这是考虑这个问题的正确方法吗?我对使用ViewBag并不感到兴奋,但我没有看到更好的方法来管理视图间/控制器共享。并且对每个视图渲染执行维护时间段检查也感觉不太正确。

    我缺少哪些其他选项?

3 个答案:

答案 0 :(得分:4)

为此,我在主布局视图中有一个部分呈现单独的操作。像

这样的东西
(page stuff...)
<div id="marginMessages>
    Html.Action("GetMarginMessages", "Infrastructure")
</div>
(more page stuff...)

你有InfrastructureController作为控制器来处理横幅关注问题,如保证金信息,通知消息等。该控制器将有一个方法GetMarginMessages,它可以确定是否需要显示任何消息,如果是,则返回包含所需消息的部分视图。如果没有消息,则可以返回EmptyResult,并且当div为空时,您应确保页面看起来正常。

对于更复杂的逻辑,您可以创建一个派生自ActionFilterAttribute的动作过滤器,该过滤器在控制器方法(OnActionExecuted())之后或在视图呈现之前捕获请求(OnResultExecuting()) 。 (从理论上讲,你使用的是哪一个并不重要。)

从那里,您可以使用filterContext

  • 查看用户刚刚点击的控制器方法和操作
  • 查看控制器放入模型的值ViewBagTempData
  • 在模型中添加/删除/更改值ViewBagTempData

因此,从那里开始,您应该能够设置值来告诉您的消息 - 呈现局部视图它需要做什么。然后,部分视图可以检索这些值并使用它们,但它需要(只需记住在尝试使用它们之前对所有内容进行空值检查)。

设置完成后,此操作过滤器可应用于:

  • 所有方法,将其添加到GlobalFilterCollection中的Application_Start() Global.asax.cs
  • 中的方法
  • 整个控制器,将其添加到控制器文件
  • 中的行public class MyController : Controller上方
  • 一种特定方法,通过在该方法上方添加

当然,在任何这些地方你都可以将值传递给构造函数,并且在过滤器中有一些逻辑可以解决哪一个覆盖哪个。

答案 1 :(得分:0)

我认为最好的办法就是拥有一个BaseController,所有其他控制器都可以从中继承。在基本控制器中,您可以执行所有请求共有的逻辑,例如:

public class BaseController : Controller
{
    // This is the instance of your business logic that will figure out what messages need to be displayed based on various parameters you specify.
    // I leave it to you to write the GetAllMessages method for the MessageService.
    var messagesService = new MessagesService();
    // This is the local variable that will hold all your system messages.
    var systemMessages  = new List<SystemMessage>();

    protected override void Initialize(RequestContext requestContext)
    {
        base.Initialize(requestContext);
        systemMessages = messageService.GetAllMessages();
        ViewBag.SystemMessages = systemMessages;
    }
}

完成此设置后,所有其他控制器将从基本控制器继承:

public class SomeController : BaseController
{
    // Controller logic here...
}

现在您可以通过控制器的messagesService属性访问系统消息。此外,您可以通过ViewBag.SystemMessages将它们提供给您。现在的原因是因为您可以通过创建这样的自定义剃刀视图来进一步推动这一点:

namespace YourProject.Views
{
    public abstract class CustomWebViewPage : WebViewPage
    {
        private List<SystemMessage> _systemMessages = new List<SystemMessage>();
        public List<SystemMessage> SystemMessages 
        {
            get
            {
               try
               {
                   _systemMessages = (List<SystemMessage>())ViewBag.SystemMessages;
               }
               catch (Exception)
               {
                   _systemMessages = new List<SystemMessage>();;
               }
               return _systemMessages;
            }
        }
    }

    public abstract class CustomWebViewPage<TModel> : WebViewPage<TModel> where TModel : class
    {
        private List<SystemMessage> _systemMessages = new List<SystemMessage>();
        public List<SystemMessage> SystemMessages 
        {
            get
            {
               try
               {
                   _systemMessages = (List<SystemMessage>())ViewBag.SystemMessages;
               }
               catch (Exception)
               {
                   _systemMessages = new List<SystemMessage>();;
               }
               return _systemMessages;
            }
        }
    }
}

这允许您做的是直接在您的剃刀视图(布局和常规视图)中引用系统消息,如下所示:

@SystemMessages

要使其工作的最后一步是将web.config文件中的页面声明修改为以下内容:

<pages pageBaseType="YourProject.Views.CustomWebViewPage">

这需要在与Views相关的所有web.config文件中完成,以便在Views文件夹和您可能拥有的任何区域视图文件夹中完成。完成此设置后,您的所有视图都将能够通过@SystemMessages语法引用系统消息。

有关自定义Razor视图的更多信息,您可以在此处阅读Phil Haack的帖子:

http://haacked.com/archive/2011/02/21/changing-base-type-of-a-razor-view.aspx

答案 2 :(得分:0)

另一个简单的选择(取决于你需要多么复杂)是使用MVC的部分:

http://www.c-sharpcorner.com/UploadFile/3d39b4/Asp-Net-mvc-4-layout-and-section-in-razor/

在“布局”视图中,您可以引用一个部分:

@RenderSection("featured", required: false)

然后在任何视图中,您可以选择此元素中的内容:

@section featured
{
    <!--Whatever you would like in here-->
    <h1>@ViewBag.Title.</h1>                 
}

然后,对于任何不需要它的观点,您只是不包含部分定义。

此外,如果多个视图使用相同的部分内容,您可以使用仅包含内容的子布局:

  • 参考您的主要布局
  • 定义部分(因此您只需要为几个定义一次 视图)