扫二维码与项目经理沟通
我们在微信上24小时期待你的声音
解答本文疑问/技术咨询/运营咨询/技术建议/互联网交流
第 1 天
创新互联建站-专业网站定制、快速模板网站建设、高性价比巍山网站开发、企业建站全套包干低至880元,成熟完善的模板库,直接使用。一站式巍山网站制作公司更省心,省钱,快速模板网站建设找我们,业务覆盖巍山地区。费用合理售后完善,十年实体公司更值得信赖。
第 2 天
第 3 天
第 4 天
第 5 天
第 6 天
第 7 天
欢迎来到第五天的学习。希望第一天到第四天的学习,你都是开心的。
在这个实验中,我们将会向 Employee 页面添加 Footer。本次实验的目标是理解分部视图(Partial Views)。
什么是「Partial Views」?
逻辑上讲,分部视图(Partial Views) 是一个可重用的视图,它不会被直接显示。它会被其它视图所包含,然后作为该视图的一部分来显示。它类似于 ASP.NET Web Forms 中的用户控件,但是没有后台代码。
第一步:为 Partial View 创建 ViewModel
右击 ViewModel 文件夹,然后创建一个类,命名为 FooterViewModel。
public class FooterViewModel{ public string CompanyName { get; set; } public string Year { get; set; }}
第二步:创建 Partial View
右击「~/Views/Shared」文件夹,选择 Add -> View。
设置视图的名称为 Footer。选中「Create as a partial view」复选框,然后点击「Add」。
注意:我们已经在第一天的学习中谈论了 Shared 文件夹。Shared 文件夹包含了视图,这些视图不会属于一个特定的控制器。在 Shared 文件夹下的视图适用于所有控制器。
第三步:在 Partial View 中显示数据
打开 Footer.cshtml,然后放置如下代码。
@usingWebApplication1.ViewModels
@modelFooterViewModel
@Model.CompanyName©@Model.Year
第四步:在 Main ViewModel 中包含 Footer 数据
打开 EmployeeListViewModel 类,然后增加一个新的属性来承载 Footer 数据。
publicclassEmployeeListViewModel
{
publicList
publicstringUserName{get;set;}
publicFooterViewModelFooterData{get;set;}//New Property
}
在我们的例子中,Footer 视图将会作为 Index 视图的一部分展示。
我们将会在 Index 视图中向 Footer 传输必要数据。
Index 视图是一个 EmployeeListViewModel 的强类型视图,因此 Footer 中需要的数据都应该被封装在 EmployeeListViewModel 类中。
第五步:设置 Footer 数据
打开 EmployeeController,然后在 Index 行为方法中设定 FooterData 属性值。
public ActionResult Index(){ ... ... employeeListViewModel.FooterData = new FooterViewModel(); employeeListViewModel.FooterData.CompanyName = "StepByStepSchools";//Can be set to dynamic value employeeListViewModel.FooterData.Year = DateTime.Now.Year.ToString(); return View("Index", employeeListViewModel);}
第六步:展示 Footer
打开 Index.cshtml 文件,然后在 Table 标签后展示 Footer 视图。
@{ Html.RenderPartial("Footer", Model.FooterData); }
第七步:执行并测试
按下 F5。导航到 Index 视图。
Html.Partial 是用来做什么的?
它类似于 Html.RenderPartial,Html.Partial 用于在视图中展示 Partial View。
它的语法如下。非常简单。
@Html.Partial("Footer", Model.FooterData);
两者的区别是什么?
Html.RenderPartial 将会把 Partial View 的结果写入 HTTP 响应流中,而 Html.Partial 将会以 MvcHtmlString 的格式返回结果。
什么是 MvcHtmlString,为什么 Html.Partial 返回的是 MvcHtmlString,而不是字符串?
首先让我们理解下什么是 MvcHtmlString。
MSDN 的定义是「MvcHtmlString 代表一个 HTML 编码的字符串,这种字符串不应该再次编码」。
更好地理解这个定义,请查看下面代码。
@{ string MyString = "My Simple String";}@MyString
它将会产生如下输出。
正如你所看见的,Razor 展示了所有的内容。可能许多人会以为将输出加粗的字符串,但是 Razor Html 在展示之前对内容进行了编码,这就是为什么我们获得的是纯内容,而不是加粗的字符串。
当我们不想用 Razor 编码时,我们可以使用 MvcHtmlString。MvcHtmlString 是 Razor 的一种表示,即「字符串已经编码了,不再需要额外编码」。
例如我们可以看下面的代码。
@{ string MyString = "My Simple String";}@MvcHtmlString.Create(MyString)
它将会产生如下输出。
为什么 Html.Partial 返回的是 MvcHtmlString,而不是字符串呢?
我 们已经理解了「Razor 将会编码字符串,但是不会对 MvcHtmlString 编码」这一事实。如果 Partial View 内容被认为是像它展示的那样的纯字符串,便没有意义。我们希望它被当成是一个 HTML 内容,这样我们就需要停止 Razor 编码,因此 Partial 方法被设计为返回 MvcHtmlString。
哪个更加推崇,Html.RenderPartial 还是 Html.Partial ?
Html.RenderPartial 更被推崇,因为它更快。
什么时候运用 Html.Partial 更好?
当我们想在展示之前改变 Partial View 返回的结果,推荐使用 Html.Partial。
打开 Index.cshtml,然后打开 Footer,放置如下代码。
@{ MvcHtmlString result = Html.Partial ("Footer", Model.FooterData); string finalResult = result.ToHtmlString().Replace("2015", "20000"); }@MvcHtmlString.Create(finalResult)
现在页脚展示如下。
为什么将 Partial View 放置在 Shared 文件夹下?
因为 Partial View 意味着可以重复利用的资源,因此放置它们的地点是 Shared 文件夹下。
我们不能将 Partial View 放置到一个特殊的控制器文件夹内吗?例如 Employee 或者 Authentication?
我们可以这样做,但是在这种场景下,它将不会适用于指定控制器。
例如:当我们将 Partial View 放置到 Employee 文件夹下,它将不会适用于 AuthenticationController 或者适用于 AuthenticationController 相关的视图。
为什么 Partial View 的定义包含「逻辑」词汇?
在定义中,我们已经知道 Partial View 是一个可重用的视图,但是它不能通过自己执行。它需要放置到其它视图中,然后作为这些视图的一部分来展示。
我们所说的 Partial View 可重用是事实,但是我们提到的执行在逻辑上是事实。技术上而言,这不是一个正确的解释。我们可以创建一个行为方法,来返回如下的视图结果。
public ActionResult MyFooter(){ FooterViewModel FooterData = new FooterViewModel(); FooterData.CompanyName = "StepByStepSchools";//Can be set to dynamic value FooterData.Year = DateTime.Now.Year.ToString(); return View("Footer", FooterData);}
它将会展示如下的输出。
尽管在逻辑上没有意义,但是技术上是可行的。Footer.cshtml 不会包含正确的结构性 HTML。它意味着作为视图的一部分来展示。因为我们说「逻辑上是没有意义的」。
为什么要创建 Partial View,而不是直接在视图底部添加内容?
这样做有两个优势。
可重用性。我们可以将一个 Partial View 运用到多个视图中去。
代码保留。将其放置为一个分割的文件,使其管理和操纵都非常方便。
为什么在 Partial View 中没有创建 Header?
作为最佳实践,我们需要为 Partial View 创建 Header,但是为了保持最初实验简单化,我们并没有这样做。
在本次实验中,我们将会实现 Admin 和 Non-Admin 两种登录功能。
需求是很简单的。即「Non-Admin 用户不能创建 Employees」。
通过这个实验,我们将会理解 MVC 中的两个主题。
Session
Action Filters
现在我们开始进行实验。为了简单化,我们将实验分为两部分。
第一步:创建标识 UserStatus 的枚举
右击 Models 文件夹,选择「Add New Item」。
在对话框中选择「Code File」选项。
在名称栏中输入「UserStatus」,然后点击添加。「Code File」的选项将会创建一个空白的「.cs」文件。
创建一个枚举,命名为 UserStatus,代码如下。
namespace WebApplication1.Models{ public enum UserStatus { AuthenticatedAdmin, AuthentucatedUser, NonAuthenticatedUser }}
第二步:更改业务层功能
删除 IsValidUser 功能,然后创建一个新的功能,命名为 GetUserValidity。
public UserStatus GetUserValidity(UserDetails u){ if (u.UserName == "Admin" && u.Password == "Admin") { return UserStatus.AuthenticatedAdmin; } else if (u.UserName == "Sukesh" && u.Password == "Sukesh") { return UserStatus.AuthentucatedUser; } else { return UserStatus.NonAuthenticatedUser; }}
第三步:更改 DoLogin 行为方法
打开 AuthenticationController,然后更改 DoLogin 行为方法如下。
[HttpPost]public ActionResult DoLogin(UserDetails u){ if (ModelState.IsValid) { EmployeeBusinessLayer bal = new EmployeeBusinessLayer(); //New Code Start UserStatus status = bal.GetUserValidity(u); bool IsAdmin = false; if (status==UserStatus.AuthenticatedAdmin) { IsAdmin = true; } else if (status == UserStatus.AuthentucatedUser) { IsAdmin = false; } else { ModelState.AddModelError("CredentialError", "Invalid Username or Password"); return View("Login"); } FormsAuthentication.SetAuthCookie(u.UserName, false); Session["IsAdmin"] = IsAdmin; return RedirectToAction("Index", "Employee"); //New Code End } else { return View("Login"); }}
正如你所看见的,我们运用 Session 变量来识别用户是 Admin 用户还是 non-Admin 用户。
不知道什么是 Session?
Session 是 ASP.NET 的一个功能,在 ASP.NET MVC 中被重用。
我们运用 Session 变量来承载用户相关的数据。Session 的生命周期取决于用户的生命周期。它将一直可用直到当前的 Session 结束。
第四步:删除已经存在的 AddNew 链接
在「~/Views/Employee」文件夹下打开 Index.cshtml 视图,然后完全删除「Add New」超链接。
第五步:创建 Partial View
右击「~/Views/Employee」文件夹,然后选择 Add -> View。设置视图的名称为「AddNewLink」,然后确保选择「Create as a partial view」复选框。
第六步:在 Partial View 中放置内容
在刚创建的 Partial View 中放置如下内容。
Add New
第七步:创建行为方法
打开 EmployeeController 然后创建一个新的行为方法,命名为「GetAddNewLink」。
public ActionResult GetAddNewLink(){ if (Convert.ToBoolean(Session["IsAdmin"])) { return Partial View("AddNewLink"); } else { return new EmptyResult(); }}
第八步:展示 AddNew 链接
打开 Index.html,然后放置如下代码。
Logout
EmployeeName | Salary |
---|---|
@item.EmployeeName | @item.Salary |
}
正如你所看见的,视图中所有的元素都定义在指定的位置上。
第八步:执行并测试
按下 F5,然后执行应用。导航到 Index 行为上。
第九步:在 CreateEmployee 视图中附上 Layout 页面
打开 Index.cshtml 页面,在顶部会发现如下代码。
@{ Layout = null;}
将其改为如下代码。
@{ Layout = "~/Views/Shared/MyLayout.cshtml";}
第十步:设计 CreateEmployee 视图
像第七步的步骤一样,定义 CreateEmployee 视图的区域。这一次会增加一点。我们将会定义 Header 部分。
完整的 HTML 代码如下。
@usingWebApplication1.Models
@modelEmployee
@{
Layout="~/Views/Shared/MyLayout.cshtml";
}
@sectionTitleSection{
CreateEmployee
}
@sectionHeaderSection{
functionResetForm(){
document.getElementById('TxtFName').value="";
document.getElementById('TxtLName').value="";
document.getElementById('TxtSalary').value="";
}
}
@sectionContentBody{
FirstName:
@Html.ValidationMessage("FirstName")
LastName:
@Html.ValidationMessage("LastName")
Salary:
@Html.ValidationMessage("Salary")
}
第十一步:执行并测试
按下 F5,然后执行应用。通过尝试超链接来导航到 AddNew 行为上。
Index 视图是 EmployeeListViewModel 的强类型视图,EmployeeListViewModel 又是 BaseViewModel 的子类,所以 Index 视图可以运转。但是 CreateEmployee 视图是 CreateEmployeeViewModel 的强类型视图,而 CreateEmployeeViewModel 不是 BaseViewModel 的子类,所以 CreateEmployee 出现了这样的错误。
第十二步:准备 CreateEmployeeViewModel
让 CreateEmployeeViewModel 继承 BaseViewModel,代码如下。
public class CreateEmployeeViewModel:BaseViewModel{...
第十三步:执行并测试
再一次测试。
这次的错误看起来与之前的不一样。错误真正的原因是,我们在 AddNew 行为中没有初始化 Header 和 Footer 的数据。
第十四步:初始化 Header 和 Footer 数据
将 AddNew 行为方法的代码改为如下所示。
public ActionResult AddNew(){ CreateEmployeeViewModel employeeListViewModel = new CreateEmployeeViewModel(); employeeListViewModel.FooterData = new FooterViewModel(); employeeListViewModel.FooterData.CompanyName = "StepByStepSchools";//Can be set to dynamic value employeeListViewModel.FooterData.Year = DateTime.Now.Year.ToString(); employeeListViewModel.UserName = User.Identity.Name; //New Line return View("CreateEmployee", employeeListViewModel);}
第十五步:在 SaveEmployee 中初始化 Header 和 Footer 数据
类似于 SaveEmployee 行为方法一样,我们更改其代码如下。
public ActionResult SaveEmployee(Employee e, string BtnSubmit){ switch (BtnSubmit) { case "Save Employee": if (ModelState.IsValid) { ... } else { CreateEmployeeViewModel vm = new CreateEmployeeViewModel(); ... vm.FooterData = new FooterViewModel(); vm.FooterData.CompanyName = "StepByStepSchools";//Can be set to dynamic value vm.FooterData.Year = DateTime.Now.Year.ToString(); vm.UserName = User.Identity.Name; //New Line return View("CreateEmployee", vm); // Day 4 Change - Passing e here } case "Cancel": return RedirectToAction("Index"); } return new EmptyResult();}
第十六步:执行并测试
按下 F5,然后执行应用。
RenderBody 是用于做什么的?
当我们第一次创建 Layout 页面时,我们有一个 Razor 声明如下。
@Html.RenderBody()
现在让我们来理解下它是做什么的。在内容页面上,我们正常地定义区域,这些区域在布局页声明。
但是奇怪的是,Razor 允许我们在区域外定义一些内容。在内容页面上,所有非区域内的内容将会被 RenderBody 函数呈现。
下图将会更好地进行解释。
我们有嵌套的布局吗?
答案是肯定的。我们能创建一个嵌套了其它布局页的布局页。语法是一样的。
在每一个视图中都指定布局页是必须的吗?
你能够在 Views 文件夹下发现一个特殊的布局页,称为「__ ViewStart.cshtml」。在其内部设定定义,将会应用于所有视图。
例如,在「__ ViewStart.cshtml」中放置如下的代码,将会使「_Layout.cshtml」适用于所有视图的布局。
@{ Layout = "~/Views/Shared/_Layout.cshtml";}
在每一个行为方法中,是否都需要放置 Header 和 Footer 的数据代码?
答案是否定的。我们可以运用 Action 过滤器来避免这种重复。我们将会在接下来的实验中实践。
在子视图中定义所有区域是否是必须的?
答案是肯定的。如果 Section 的声明为必须的,那么默认值是 True。
@RenderSection("HeaderSection",false) // Not required@RenderSection("HeaderSection",true) // required@RenderSection("HeaderSection") // required
在 Lab 23 中,我们已经知道了 ActionFilter 的优势,现在我们来看它的第二点优势。
第一步:从行为方法中删除冗余代码
从 Index,AddNew 和 SaveEmployee 方法中删除 Header 和 Footer 数据的代码。
Header 代码如下。
bvm.UserName = HttpContext.Current.User.Identity.Name;
Footer 代码如下。
bvm.FooterData = new FooterViewModel();bvm.FooterData.CompanyName = "StepByStepSchools";//Can be set to dynamic valuebvm.FooterData.Year = DateTime.Now.Year.ToString();
第二步:创建 HeaderFooterFilter
在 Filters 文件夹下创建一个类,命名为 HeaderFooterFilter,然后通过将它继承 ActionFilterAttribute 类来将其升级 Action 过滤器。
第三步:升级 ViewModel
在 HeaderFooterFilter 类中重写 OnActionExecuted。在这个方法中我们将会得到当前的视图模型,然后将其附上 Header 和 Footer 数据。
public class HeaderFooterFilter : ActionFilterAttribute{ public override void OnActionExecuted(ActionExecutedContext filterContext) { ViewResult v = filterContext.Result as ViewResult; if(v!=null) // v will null when v is not a ViewResult { BaseViewModel bvm = v.Model as BaseViewModel; if(bvm!=null)//bvm will be null when we want a view without Header and footer { bvm.UserName = HttpContext.Current.User.Identity.Name; bvm.FooterData = new FooterViewModel(); bvm.FooterData.CompanyName = "StepByStepSchools";//Can be set to dynamic value bvm.FooterData.Year = DateTime.Now.Year.ToString(); &
当前题目:7天玩转ASP.NETMVC—第5天
浏览路径:http://kswjz.com/article/jpiihe.html扫二维码与项目经理沟通
我们在微信上24小时期待你的声音
解答本文疑问/技术咨询/运营咨询/技术建议/互联网交流