• ASP.NET MVC3 系列教程 – 新的Layout布局系统


    I:回忆MVC2当中MasterPage那些事

    code:

    <!------------Begin-------------->
    <!-- Master文件 -->
    <%@ Master Language="C#" 
        Inherits="System.Web.Mvc.ViewMasterPage" %>
    Master head
    <asp:ContentPlaceHolder ID="MainContent" runat="server" />
    Master1...
    <asp:ContentPlaceHolder ID="OtherContent" runat="server" />
    Master2...
    <asp:ContentPlaceHolder ID="AnyContent" runat="server" />
    Master3...
    <!-------------End--------------->
    
    
    <!------------Begin-------------->
    <!-- 某个View文件 -->
    <%@ Page Language="C#" 
        MasterPageFile="~/Views/Shared/Site.Master" 
        Inherits="System.Web.Mvc.ViewPage" %>
    <asp:Content ID="Content1" ContentPlaceHolderID="MainContent" runat="server">
    MainContent...
    </asp:Content>
    
    <asp:Content ID="Content2" ContentPlaceHolderID="OtherContent" runat="server">
    OtherContent...
    </asp:Content>
    
    <asp:Content ID="Content3" ContentPlaceHolderID="AnyContent" runat="server">
    AnyContent...
    </asp:Content>
    <!-------------End--------------->
    
    
    <!------------Begin-------------->
    <!-- 最后传回给客户端的文件 -->
    Master head
    MainContent...
    Master1...
    OtherContent...
    Master2...
    AnyContent...
    Master3...
    <!-------------End--------------->
    

    我们可以看到在Master中ContentPlaceHolder服务端控件起到了一个占位符的作用.最后输出的,其实是在View当中的Content服务端控件内的内容,接下来开始介绍Layout.

    II:ASP.NET MVC3 新的Layout布局系统

    在MVC3当中我们可以利用新的Layout布局系统来代替掉原来在MVC2当中使用的MasterPage(当然在MVC3当中,如果你是继续使用ASPX视图引擎的话,那么还是可以用回原来的MasterPage,然后~~~~然后~你会和runat=”server”保持着从.NET 1.x到.NET 4.0以来从没有间断过的合作关系,可谓缘分呀!).

    我们在VS2010 MVC3项目中创建Item时,从创建向导中可以看到以下新增的几个Item
    image
    下面进行逐一介绍:

    Layout页:
    该家伙其实就相当于原来的Master文件.为站点的统一主题界面和减少大部分冗余的Html,head,body标记曾作出过很大的贡献.可谓是功不可莫啊!MasterPage他的诞生是在.NET 2.0版本!在服役到.NET4.0版本后出现了一个新成员[Layout]去向他挑战.MasterPage能否经得起新成员的挑战呢?这个还是得留各位观众做详细对比吧!

    Partial页:
    相当于原来的UserControl.它可以为你减轻不少需要重复劳动的时间!

    View页:
    就是View啦.创建它时.一般都是在不需要使用Layout/MasterPage的时候.

    View Page with Layout:
    等同于原来的View Content Page.它的功能只是为了实现原来在Layout/MasterPage下所定义的占位符.当然在原来的MasterPage中如果你没有实现原先定义的占位符<asp:ContentPlaceHolder />,那么在最终合并输出的时候MasterPage占位符<asp:ContentPlaceHolder />那里就会输出空.

    以上这4个新成员都是可以利用新的Razor视图引擎进行工作.如果你还没了解Razor那么可以参考我的另外一编文章

    1.Layout页基础:
    如果你有使用MasterPage的经验,你将会记得如下的几个东西

    A:<%@ Master %>

    B:<%@ Page %>

    C:<asp:ContentPlaceHolder />

    D:<asp:Content />

    但是在Layout中,以上的这些东西将会消失.(作者不排除有WebPages和WebForms兼容工作的可能性)

    取而代之的新功能是:

    A.Layout属性:等同于原来的MasterPageFile属性.

    B.@RenderBody()方法:直接渲染整个View到占位符处,而不需要原来所使用的<asp:Content />.

    C.@RenderPage()方法:渲染指定的页面到占位符处.

    D.@RenderSection方法:声明一个占位符,和原来的<asp:ContentPlaceHolder />功能类似.

    E.@section标记:对@RenderSection方法声明的占位符进行实现,和原来的<asp:Content />功能类似.

    .1.@RenderBody()方法的使用

    首先在~/Views/Shared/下创建一个名为_MyLayout.cshtml的LayoutPage文件,并将默认的内容替换为如下:

    <!DOCTYPE html>
    
    <html>
    <head>
        <title>@ViewBag.Title</title>
    </head>
    <body>
        <div>
            开始渲染Body<br />
            @RenderBody()
            渲染Body结束<br />
        </div>
    </body>
    </html>
    

    然后打开在~/Views/Home/Index.cshtml文件并替换为如下的内容:

    @{
        ViewBag.Title = "首页";
    }
    
    <div>
        这里就是渲染Body啦.~~不需要写神马&lt;asp:Content /&gt;,其实因为RenderBody()不在有歧义.
    </div>
    

    最后输出截图为:
      image

    这个与之前MasterPage的代码量相比之下减少了许多,而更为简洁明了.

    最后别忘记把~/Views/_ViewStart.cshtml中的Layout属性改为:

    Layout = "~/Views/Shared/_MyLayout.cshtml";喔.

    在此,你或许会有疑问了.在_Layout中定义的RenderBody()是Render那个页啊?

    答:其实最后Render页的归属就是Render你所访问的那个页,比如你访问/Home/Index.那么Render就是Home控制器下的Index.cshtml这个文件, 如果访问的是/Ohter/SomePage时,那么Render的是Ohter控制器下的SomePage这个.cshtml!

    在这里可能有的朋友没有接触过MVC.在此补个基础,在默认的路由设置选项下:

    public static void RegisterRoutes(RouteCollection routes)
    {
        routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
    
        routes.MapRoute(
            "Default", // Route name
            "{controller}/{action}/{id}", // URL with parameters
            new { controller = "Home", action = "Index", id = UrlParameter.Optional } // Parameter defaults
        );
    
    }
    

    请求地址:http://localhost/Home/Index的工作流程为下图(这里没有考虑Layout):
    image

    如果这个RenderBody满足不了你的业务需求,请放心,在此介绍另外一个Render方式RenderPage().它可以让你指定要Render的页.

    1.2.@RenderPage()方法的使用

    在~/Views/Home/文件夹下新建立一个ViewPage1.cshtml文件,将内容改为如下:

    <div>
        这里是~/Views/Home/ViewPage1.cshtml,老规矩:还是不用写&lt;asp:Content /&gt;
    </div>
    

    并在原来的_MyLayout.cshtml文件中增加几行代码变成下面的这个样子:

    <!DOCTYPE html>
    
    <html>
    <head>
        <title>@ViewBag.Title</title>
    </head>
    <body>
        <div>
            开始渲染Body<br />
            @RenderBody()
            渲染Body结束<br />
            <br />
            开始渲染其他页<br />
            @RenderPage("~/Views/Home/ViewPage1.cshtml")
            渲染其他页结束<br />
            
        </div>
    </body>
    </html>
    

    我们来看最终的输出效果:
    image

    在这里记住:@RenderBody()只能在_Layout.cshtml中使用一次,而@RenderPage()则可以使用多次!

    好了在这里如果还有不明白的朋友们.我下面上个图说明Render的工作原理
    image

    如果想要了解在Layout中如何使用类似于原来MasterPage中的<asp:ContentPlaceHolder /><asp:Content />功能请继续往下看.

    III:在Layout布局系统中实现类似于原来MasterPage功能的实现方式

    好,写到这里开始介绍上一章节中没有介绍完的两个东西:@RenderSection方法和@section标记
    1.@RenderSection()方法等价于<asp:ContentPlaceHolder />,用途为在Layout中声明一个占位符.
    操作:在原来的_MyLayout.cshtml文件中更改内容为如下:

    @{
        //some code
    }
    <!DOCTYPE html>
    
    <html>
    <head>
        <title>@ViewBag.Title</title>
    </head>
    <body>
        <div>
            开始渲染Body<br />
            @RenderBody()
            渲染Body结束<br />
            <br />
            开始渲染其他页<br />
            @RenderPage("~/Views/Home/ViewPage1.cshtml")
            渲染其他页结束<br />
            <br />
            HOHO,开始学习Section了<br />
            开始渲染Section<br />
            声明方式1(推荐):SectionA:<br />
            @RenderSection("SectionA", false)
            -------<br />
            
            声明方式2:SectionB:<br />
            @{
                if (IsSectionDefined("SectionB"))
                {
                    @RenderSection("SectionB")
                }
            }
            -------<br />
            渲染Sction结束<br />
        </div>
    </body>
    </html>
    

    在~/Views/Home/Index.cshtml中更改为如下内容:

    @{
        ViewBag.Title = "首页";
        
        
        //
        // some code
        //
    }
    
    
    @section SectionA{
        <div>这里是SectionA:也不需要写神马runat="server"啊,有木有</div>
    }
    
    
    @section SectionB{
        <div>这里是SectionB:也不需要写神马&lt;asp:Content /&gt啊,有木有</div>
    }
    
    <div>
        这里就是渲染Body啦.~~不需要写神马&lt;asp:Content /&gt;,其实因为RenderBody()不在有歧义.
    </div>
    

    最后显示的页面效果:
    image


    image

    问:为什么为什么要推荐方式1呢?

    答:因为RenderSection()方法有2个重载.

    如果使用第一个只接受一个string类型参数的重载的话.~如果你在具体的View中没有利用@section来定义实现的话,运行会报错.所以需要配合另外一个方法IsSectionDefined()来使用,才能防止报错.

    而使用第2个重载就可以利用第二个bool类型的参数指明该Section是否为必须的.所以可以在使用该RenderSection方法的时候直接利用第二个重载,再把bool参数值设为false,即使你在具体的View中没有声明实现@section,运行起来也一如既往地蛋定,不Show黄页.

    IV:关于前篇文章中有热心的观众朋友们问到如何在Layout(MasterPage)中读取数据库并初始化页面的问题的解答

    在这里只是做个一简单的示范,新建一个类文件,替换如下:

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Web;
    using System.Data;
    using System.Data.SqlClient; //在这里就用ADO.NET方式吧.EF我接触不久!
    
    namespace Mvc3Application1
    {
        public class ReaderSQL_Date
        {
            private static readonly string _SQL_CONN_STR = "server=.\\mssqlserver,1433;uid=sa;pwd=yourpwd;database=student;";
    
            public static IList<StudentEntity> GetAllStudent()
            {
                //这里仅仅是做演示,生产环境并不这样写
                using (SqlConnection conn = new SqlConnection(_SQL_CONN_STR))
                {
                    SqlCommand cmd = conn.CreateCommand();
                    cmd.CommandType = CommandType.Text;
                    cmd.CommandText = "SELECT [Sno],[Sname],[Sage] FROM [dbo].[STUDENT]";
    
                    IList<StudentEntity> result = new List<StudentEntity>();
                    conn.Open();
                    using (SqlDataReader sdr = cmd.ExecuteReader())
                    {
                        while (sdr.Read())
                        {
                            result.Add(new StudentEntity
                            {
                                S_No = sdr.GetInt32(0),
                                S_Name = sdr.GetString(1),
                                S_Age = sdr.GetInt32(2)
                            });
                        }
                    }
                    //SqlConnection.ClearPool(conn); //可选清理连接池.
    
                    return result;
                }
            }
        }
    
        public class StudentEntity
        {
            public int S_No { get; set; }
            public string S_Name { get; set; }
            public int S_Age { get; set; }
        }
    }
    

    _MyLayout.cshtml替换如下:

    @{
        IList<Mvc3Application1.StudentEntity> studentEntities = Mvc3Application1.ReaderSQL_Date.GetAllStudent();
    }
    <!DOCTYPE html>
    
    <html>
    <head>
        <title>@ViewBag.Title</title>
    </head>
    <body>
        <div>
    
            @{
                <table>
                    <tr>
                        <th>学号</th>
                        <th>姓名</th>
                        <th>年龄</th>
                    </tr>
                    @foreach (Mvc3Application1.StudentEntity item in studentEntities)
                    {
                        <tr>
                            <td>@item.S_No</td>
                            <td>@item.S_Name</td>
                            <td>@item.S_Age</td>
                        </tr>
                    }
                </table>
            }
    
            开始渲染Body<br />
            @RenderBody()
            渲染Body结束<br />
            <br />
            开始渲染其他页<br />
            @RenderPage("~/Views/Home/ViewPage1.cshtml")
            渲染其他页结束<br />
            <br />
            HOHO,开始学习Section了<br />
            开始渲染Section<br />
            声明方式1(推荐):SectionA:<br />
            @RenderSection("SectionA", false)
            -------<br />
            
            声明方式2:SectionB:<br />
            @{
                if (IsSectionDefined("SectionB"))
                {
                    @RenderSection("SectionB")
                }
            }
            -------<br />
            渲染Sction结束<br />
        </div>
    </body>
    </html>
    
    

    最终显示:
    image

    本文到此结束!
    在此谢谢各位关注本系列教程和关注博主的朋友们!有大家的[推荐]我会有动力将这个系列一直写下去!

  • 相关阅读:
    【转】1.5 万字 CSS 基础拾遗(核心知识、常见需求)
    【转】Git不要只会pull和push,试试这5条提高效率的命令
    【转】响应式布局的常用解决方案对比(媒体查询、百分比、rem和vw/vh)
    【ASP.NET Core】MVC模型绑定——实现同一个API方法兼容JSON和Formdata输入
    【ASP.NET Core】MVC模型绑定:自定义InputFormatter读取CSV内容
    【ASP.NET Core】MVC模型绑定:非规范正文内容的处理
    PHP 跨域
    MAC如何使用Docker 配置PHP + Nginx运行环境
    PHP的http_build_query() 方法,数组里面的bool类型会被转化为 1 和 0.
    Mac环境下RabbitMq安装与测试教程
  • 原文地址:https://www.cnblogs.com/highend/p/asp_net_mvc3_layout.html
Copyright © 2020-2023  润新知