MVC3学习第十章 葵花点穴手之反手一指----MVC3通过强类型和支架模版便捷的创建视图
本章学习内容
1.通过完善商品类别管理的增删改查熟悉利用强类型和支架模版便捷的创建视图
2.完善商品管理的增删改,实现MVC下的图片上传以及了解ActionResult,ViewResult等视图返回值的异同
1.通过完善商品类别管理的增删改查了解ActionResult,ViewResult等视图返回值的异同
右键Controllers文件夹,添加Category控制器,完整代码如下:
using System; using System.Collections.Generic; using System.Data; using System.Data.Entity; using System.Linq; using System.Web; using System.Web.Mvc; using MyShopTest.Models; namespace MyShopTest.Controllers { public class CategoryController : Controller { private MyShopDataEntities db = new MyShopDataEntities(); /// <summary> /// 类别列表 /// </summary> /// <returns></returns> public ViewResult Index() { var list = db.Category.ToList(); return View(list); } /// <summary> /// 添加类别视图展示 /// </summary> /// <returns></returns> public ActionResult Create() { return View(); } /// <summary> /// 添加类别处理 /// </summary> /// <param name="categeory"></param> /// <returns></returns> [HttpPost] public ActionResult Create(Categeory categeory) { if (ModelState.IsValid) { db.Category.Add(categeory); db.SaveChanges(); return RedirectToAction("Index"); } return View(categeory); } /// <summary> /// 编辑类别展示 /// </summary> /// <param name="id"></param> /// <returns></returns> public ActionResult Edit(int id) { Categeory categeory = db.Category.Find(id); return View(categeory); } /// <summary> /// 编辑处理 /// </summary> /// <param name="categeory"></param> /// <returns></returns> [HttpPost] public ActionResult Edit(Categeory categeory) { if (ModelState.IsValid) { db.Entry(categeory).State = EntityState.Modified; db.SaveChanges(); return RedirectToAction("Index"); } return View(categeory); } /// <summary> /// 删除信息确认 /// </summary> /// <param name="id"></param> /// <returns></returns> public ActionResult Delete(int id) { Categeory categeory = db.Category.Find(id); ViewBag.ErrorInfo = ""; return View(categeory); } /// <summary> /// 删除处理 /// </summary> /// <param name="id"></param> /// <returns></returns> [HttpPost, ActionName("Delete")] public ActionResult DeleteConfirmed(int id) { Categeory categeory = db.Category.Find(id); var products = db.Products.Where(p => p.CategoryId == id); if (products != null) { ViewBag.ErrorInfo = "该类别下存在商品,请先删除商品!"; return View(categeory); } else { db.Category.Remove(categeory); db.SaveChanges(); return RedirectToAction("Index"); } } } }
重新编译项目,右键category控制器Create Action任意处,添加对应视图,这次添加视图有些不一样,注意看截图:
这一次我们创建视图的选项与以前有所不同,我们选择了强类型是category,支架模版是Create,意思不难理解,就是创建Category模型类的Create视图,实体模型系统会自动加载目前解决方案里的实体类,支架模版表明要生成哪种操作的视图,有Create,Edit,Delete,List等等,创建成功后系统会自动生成对应操作的视图,可以大大节省我们编码的时间,记住,虽然脚手架可以为我们自动生成代码,但只是标准的 ASP.NET MVC 代码, ,支架可以节省你手工创建控制器,以及强类型视图的时间,但是,具体的细节内容还是需要自己来处理,我们修改完整代码如下:
@model MyShopTest.Models.Categeory @{ ViewBag.Title = "添加类别"; } <h2>添加类别</h2> <script src="@Url.Content("~/Scripts/jquery.validate.min.js")" type="text/javascript"></script> <script src="@Url.Content("~/Scripts/jquery.validate.unobtrusive.min.js")" type="text/javascript"></script> @using (Html.BeginForm()) { @Html.ValidationSummary(true) <fieldset> <legend>Categeory</legend> <div class="editor-label"> @Html.LabelFor(model => model.Name) </div> <div class="editor-field"> @Html.EditorFor(model => model.Name) @Html.ValidationMessageFor(model => model.Name) </div> <p> <input type="submit" value="Create" /> </p> </fieldset> } <div> @Html.ActionLink("返回列表", "Index") </div>
用同样的方法创建编辑视图,列表视图,删除视图,完整代码如下:
Edit.cshtml:
@model MyShopTest.Models.Categeory @{ ViewBag.Title = "类别编辑"; } <h2>类别编辑</h2> <script src="@Url.Content("~/Scripts/jquery.validate.min.js")" type="text/javascript"></script> <script src="@Url.Content("~/Scripts/jquery.validate.unobtrusive.min.js")" type="text/javascript"></script> @using (Html.BeginForm()) { @Html.ValidationSummary(true) <fieldset> <legend>类别编辑</legend> @Html.HiddenFor(model => model.Id) <div class="editor-label"> @Html.LabelFor(model => model.Name) </div> <div class="editor-field"> @Html.EditorFor(model => model.Name) @Html.ValidationMessageFor(model => model.Name) </div> <p> <input type="submit" value="保存" /> </p> </fieldset> } <div> @Html.ActionLink("返回列表", "Index") </div>
Delete.cshtml:
@model MyShopTest.Models.Categeory @{ ViewBag.Title = "Delete"; } <script src="@Url.Content("~/Scripts/jquery.validate.min.js")" type="text/javascript"></script> <script src="@Url.Content("~/Scripts/jquery.validate.unobtrusive.min.js")" type="text/javascript"></script> <h2>删除</h2> <h3>确认删除该类别吗?</h3> <fieldset> <legend>确认类别信息</legend> <div class="display-label">类别名称</div> <div class="display-field"> @Html.DisplayFor(model => model.Name) </div> </fieldset> @using (Html.BeginForm()) { <div style="color:Red; font-size:14px;"> @ViewBag.ErrorInfo </div> <p> <input type="submit" value="确认删除" /> | @Html.ActionLink("返回列表", "Index") </p> }
Index.cshtml:
@model IEnumerable<MyShopTest.Models.Categeory> @{ ViewBag.Title = "类别管理"; } <script src="../../Scripts/jquery-1.5.1-vsdoc.js" type="text/javascript"></script> <h2> 类别管理</h2> <p> @Html.ActionLink("添加类别", "Create") </p> <table> <tr> <th> 类别名称 </th> <th> </th> </tr> @foreach (var item in Model) { <tr> <td> @Html.DisplayFor(modelItem => item.Name) </td> <td> @Html.ActionLink("编辑", "Edit", new { id = item.Id }) | @Html.ActionLink("删除", "Delete", new { id = item.Id }) | </td> </tr> } </table>
重新编译项目,在运行之前,我们还需添加类别管理的链接,打开_Layout.cshtml,修改完整代码如下
<!DOCTYPE html> <html> <head> <title>@ViewBag.Title</title> <link href="@Url.Content("~/Content/Site.css")" rel="stylesheet" type="text/css" /> <script src="@Url.Content("~/Scripts/jquery-1.5.1.min.js")" type="text/javascript"></script> </head> <body> <div class="page"> <div id="header"> <div id="title"> <h1> 我的商店</h1> </div> <div id="logindisplay"> <a href="/UserInfo/Login">登录</a>|<a href="/UserInfo/Register">注册</a> </div> <div id="menucontainer"> <ul id="menu"> <li>@Html.ActionLink("主页", "Index", "Home")</li> <li>@Html.ActionLink("新闻动态", "Index", "Home")</li> <li>@Html.ActionLink("互动留言", "Index", "Home")</li> <li>@Html.ActionLink("关于我们", "About", "Home")</li> </ul> </div> </div> <div id="main"> @RenderBody() </div> <div id="footer"> <ul style="position: relative; text-align: left;"> <li style="display: inline;">@Html.ActionLink("类别管理", "Index", "Category")</li> <li style="display: inline;">@Html.ActionLink("商品管理", "Index", "Product")</li> <li style="display: inline;">@Html.ActionLink("留言管理", "Index", "FeedBack")</li> <li style="display: inline;">@Html.ActionLink("新闻管理", "Index", "News")</li> <li style="display: inline;">@Html.ActionLink("用户管理", "Index", "UserInfo")</li> </ul> </div> </div> </body> </html>
到目前我们的管理功能已经越来越多了,因为时间精力有限并没有找一个好看的前台模版来实现这个项目,所以我们就用这个默认的样式来完成这个项目,为了便于后续扩展而不用我们一次次来改动母版页,我们将管理菜单放在下方,前台浏览菜单放在头部。希望样式糙但讲的东西不糙。
重新编译项目,点击类别管理:
进入类别操作界面:
这个功能都很简单,大家可以自行测试一下,应该是没有问题的。
2.完善商品管理的增删改了解ActionResult,ViewResult等视图返回值的异同
现在我们来添加商品的有关操作,首先添加商品的控制器,我们采用一种更加简便的方式,右键Controllers文件夹,添加控制器:
命名为ProductController,在基架选项里模版,选择包含读写操作的视图和控制器,模型类选择Product,数据文上下类就是我们之前添加的操作数据库的类,之前我们在添加类别时使用了强类型和支架模版来创建视图,应该不难理解此处的选项意义,系统会根据选择的模版,实体模型,数据库上下文类一次帮我们创建好增删改查所需的文件和代码;点击添加,等待操作完成,我们来看一下系统帮我创建了那些东西:
ProductController.cs:
using System; using System.Collections.Generic; using System.Data; using System.Data.Entity; using System.Linq; using System.Web; using System.Web.Mvc; using MyShopTest.Models; namespace MyShopTest.Controllers { public class ProductController : Controller { private MyShopDataEntities db = new MyShopDataEntities(); // // GET: /Product/ public ViewResult Index() { var products = db.Products.Include(p => p.Category); return View(products.ToList()); } // // GET: /Product/Details/5 public ViewResult Details(int id) { Product product = db.Products.Find(id); return View(product); } // // GET: /Product/Create public ActionResult Create() { ViewBag.CategoryId = new SelectList(db.Category, "Id", "Name"); return View(); } // // POST: /Product/Create [HttpPost] public ActionResult Create(Product product) { if (ModelState.IsValid) { db.Products.Add(product); db.SaveChanges(); return RedirectToAction("Index"); } ViewBag.CategoryId = new SelectList(db.Category, "Id", "Name", product.CategoryId); return View(product); } // // GET: /Product/Edit/5 public ActionResult Edit(int id) { Product product = db.Products.Find(id); ViewBag.CategoryId = new SelectList(db.Category, "Id", "Name", product.CategoryId); return View(product); } // // POST: /Product/Edit/5 [HttpPost] public ActionResult Edit(Product product) { if (ModelState.IsValid) { db.Entry(product).State = EntityState.Modified; db.SaveChanges(); return RedirectToAction("Index"); } ViewBag.CategoryId = new SelectList(db.Category, "Id", "Name", product.CategoryId); return View(product); } // // GET: /Product/Delete/5 public ActionResult Delete(int id) { Product product = db.Products.Find(id); return View(product); } // // POST: /Product/Delete/5 [HttpPost, ActionName("Delete")] public ActionResult DeleteConfirmed(int id) { Product product = db.Products.Find(id); db.Products.Remove(product); db.SaveChanges(); return RedirectToAction("Index"); } protected override void Dispose(bool disposing) { db.Dispose(); base.Dispose(disposing); } } }
Create视图:
@model MyShopTest.Models.Product @{ ViewBag.Title = "Create"; } <h2>Create</h2> <script src="@Url.Content("~/Scripts/jquery.validate.min.js")" type="text/javascript"></script> <script src="@Url.Content("~/Scripts/jquery.validate.unobtrusive.min.js")" type="text/javascript"></script> @using (Html.BeginForm()) { @Html.ValidationSummary(true) <fieldset> <legend>Product</legend> <div class="editor-label"> @Html.LabelFor(model => model.Title) </div> <div class="editor-field"> @Html.EditorFor(model => model.Title) @Html.ValidationMessageFor(model => model.Title) </div> <div class="editor-label"> @Html.LabelFor(model => model.Price) </div> <div class="editor-field"> @Html.EditorFor(model => model.Price) @Html.ValidationMessageFor(model => model.Price) </div> <div class="editor-label"> @Html.LabelFor(model => model.ImgUrl) </div> <div class="editor-field"> @Html.EditorFor(model => model.ImgUrl) @Html.ValidationMessageFor(model => model.ImgUrl) </div> <div class="editor-label"> @Html.LabelFor(model => model.CategoryId, "Category") </div> <div class="editor-field"> @Html.DropDownList("CategoryId", String.Empty) @Html.ValidationMessageFor(model => model.CategoryId) </div> <div class="editor-label"> @Html.LabelFor(model => model.AddTime) </div> <div class="editor-field"> @Html.EditorFor(model => model.AddTime) @Html.ValidationMessageFor(model => model.AddTime) </div> <p> <input type="submit" value="Create" /> </p> </fieldset> } <div> @Html.ActionLink("Back to List", "Index") </div>
Index视图:
@model IEnumerable<MyShopTest.Models.Product> @{ ViewBag.Title = "Index"; } <h2>Index</h2> <p> @Html.ActionLink("Create New", "Create") </p> <table> <tr> <th> Title </th> <th> Price </th> <th> ImgUrl </th> <th> Category </th> <th> AddTime </th> <th></th> </tr> @foreach (var item in Model) { <tr> <td> @Html.DisplayFor(modelItem => item.Title) </td> <td> @Html.DisplayFor(modelItem => item.Price) </td> <td> @Html.DisplayFor(modelItem => item.ImgUrl) </td> <td> @Html.DisplayFor(modelItem => item.Category.Name) </td> <td> @Html.DisplayFor(modelItem => item.AddTime) </td> <td> @Html.ActionLink("Edit", "Edit", new { id=item.Id }) | @Html.ActionLink("Details", "Details", new { id=item.Id }) | @Html.ActionLink("Delete", "Delete", new { id=item.Id }) </td> </tr> } </table>
其他的就不再列举,我们可以看到绝大多数的操作都已经完成了,当然还是和之前说的一样,细节还是要我们自己来处理,有兴趣的可以运行一下代码看看,确实是可以用的,在此就不给出截图了,我们修改ProductController完整代码如下:
using System; using System.Collections.Generic; using System.Data; using System.Data.Entity; using System.Linq; using System.Web; using System.Web.Mvc; using MyShopTest.Models; using System.Data.Entity.Validation; namespace MyShopTest.Controllers { public class ProductController : Controller { private MyShopDataEntities db = new MyShopDataEntities(); //列表 public ViewResult Index(string title = "") { var products = Search(title); ViewBag.Name = title; return View(products.ToList()); } //商品查询 public IEnumerable<Product> Search(string titles) { var products = db.Products.Where(p => p.Id > 0);//此处的查询没有任何意义,只是返回所有数据 if (!string.IsNullOrEmpty(titles)) { products = products.Where(p => p.Title.IndexOf(titles) > -1); } return products; } //详细 public ViewResult Details(int id) { Product product = db.Products.Find(id); return View(product); } //添加 public ActionResult Create() { ViewBag.CategoryId = new SelectList(db.Category, "Id", "Name"); return View(); } //添加处理 [HttpPost] public ActionResult Create(Product product)//此时已经自动匹配好了 { try { //图片 var file = Request.Files[0]; var img = new CommonHelper().fileSaveAs(file, 0, 2); if (img.IndexOf("UpFile") > -1) { product.ImgUrl = img; } else { ModelState.AddModelError("error", img);//添加了key的错误信息,前台使用Html.ValidationMessage(key)的形式访问,如果key为空,用Html.ValidationSummary(true)显示 ViewBag.CategoryId = new SelectList(db.Category, "Id", "Name", product.CategoryId); return View(product); } db.Products.Add(product); db.SaveChanges(); return RedirectToAction("Index"); } catch (DbEntityValidationException ex) { string mes = ex.Message; ViewBag.CategoryId = new SelectList(db.Category, "Id", "Name", product.CategoryId); return View(product); } } //编辑 public ActionResult Edit(int id) { Product product = db.Products.Find(id); ViewBag.CategoryId = new SelectList(db.Category, "Id", "Name", product.CategoryId); return View(product); } //编辑处理 [HttpPost] public ActionResult Edit(int id, FormCollection collection) { var product = db.Products.Find(id); //ModelState.IsValid,此处相当于后台验证,防止前台验证因为各种情况被跳过或失效 try { bool updateRes = TryUpdateModel(product);//如果字段符合则会赋值,否则保持原有 //db.Entry(product).State = EntityState.Modified; if (updateRes) { //图片 var file = Request.Files[0]; var img = new CommonHelper().fileSaveAs(file, 0, 2); if (img.IndexOf("UpFile") > -1) { product.ImgUrl = img; } //else //{ // ModelState.AddModelError("", img); // ViewBag.CategoryId = new SelectList(db.Category, "Id", "Name", product.CategoryId); // return View(product); //} db.SaveChanges(); return RedirectToAction("Index"); } else { ViewBag.CategoryId = new SelectList(db.Category, "Id", "Name", product.CategoryId); ModelState.AddModelError("", "更新失败!"); return View(product); } } catch (Exception) { ViewBag.CategoryId = new SelectList(db.Category, "Id", "Name", product.CategoryId); return View(product); } } //删除处理 [HttpPost] public ActionResult Delete(FormCollection coll) { string ids = coll["ckSelect"]; foreach (var item in ids.Split(','))//循环每一项Id { if (item != "false")//筛选掉自动生成的checkbox初始值 { var user = db.Products.Find(Convert.ToInt32(item)); db.Products.Remove(user); } } db.SaveChanges(); return RedirectToAction("Index"); } protected override void Dispose(bool disposing) { db.Dispose(); base.Dispose(disposing); } } }
我们来解析一下代码中的新知识:
//详细 public ViewResult Details(int id) { Product product = db.Products.Find(id); return View(product); } //添加 public ActionResult Create() { ViewBag.CategoryId = new SelectList(db.Category, "Id", "Name"); return View(); }
此段代码中一个返回视图ActionResult ,另一个返回了ViewResult,我们解析一下二者:ActionResult是个抽象类,然后ViewResult间接实现了ActionResult;ActionResult 实现一些功能就如“操作方法执行的命令”,比如有较多的逻辑处理操作的,ViewResult 呈现视图,多用于简单的显示视图,以及用于执行结果的方法。像是从数据库读取信息之类的,把信息呈现在界面上面,多数时候两者可以通用(当然我说的通用是可以这样写可以达到一样的功能效果,个人理解有限)
//图片 var file = Request.Files[0]; var img = new CommonHelper().fileSaveAs(file, 0, 2); if (img.IndexOf("UpFile") > -1) { product.ImgUrl = img; }
此段代码用来上传图片,Request.Files用来获取视图层的上传文件信息,CommonHelper里面封装的是一些公共方法(其中含有文件上传),稍后我们会展示该类代码。
ViewBag.CategoryId = new SelectList(db.Category, "Id", "Name", product.CategoryId);
此段代码是表示初始化一个SelectList,也就是weform中的Dropdownlist,html中的select标签,第一个参数指定的是数据源,第二个参数指定的是value,第三个参数指定的是name,第四个参数指定的是默认值,new SelectList有很多不同参数的构造函数,具体大家可以转到定义看一看;
bool updateRes = TryUpdateModel(product);
以前我们使用的更新是db.Entry(user).State = EntityState.Modified;,还有一种更新是TryUpdateModel(product),它适用于当表单参数很多,并且不少参数并不是用来匹配实体的又或者获取的表单参数用法各异的,使用TryUpdateModel的意思是会尝试将表单里的值和实体进行匹配,匹配全部成功会返回true,否则返回false;还有一种方法是UpdateModel(product),他会直接进行更新,不成功便报错。
在此解析一下UpdateModel方法,tryUPdate类似:
当调用UpdateModel方法时,它至少做了两件事情。
A、 把提交的数据(FormCollection中的数据)与Movie类实例的属性匹配并自动更新。
B、 将每个匹配的FormCollection中的数据实例化为System.Web.Mvc.ModelState类,并根据键值分别加入ModelState集合中。
好了,Product控制器的新知识应该就这么多
右键Models,新建类CommonHelper.cs,修改完整代码如下:
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.IO; using System.Text.RegularExpressions; using System.Web.UI; using System.Web.UI.HtmlControls; using System.Web.UI.WebControls; using System.Web; using System.Web.Mvc; using System.Data; using System.Configuration; namespace MyShopTest.Models { public class CommonHelper { //基础配置数据,均来源于web.config //上传的文件类型,*为不限制 private string fileType = ConfigurationManager.AppSettings["upFileType"]; //上传的图片类型,*为不限制 private string imgType = ConfigurationManager.AppSettings["upImageType"]; //上传的视频类型,*为不限制 private string videoType = ConfigurationManager.AppSettings["upVideoType"]; //上传的文件大小,以mb为单位 private int fileSize = Convert.ToInt32(ConfigurationManager.AppSettings["upFileSize"]); //默认是否打水印 private bool isWater = ConfigurationManager.AppSettings["isWater"] == "true"; //打水印类型,2是图片,1是文本 private int WaterType = Convert.ToInt32(ConfigurationManager.AppSettings["WaterType"]); //水印文本 private string WaterText = ConfigurationManager.AppSettings["WaterText"]; //水印文本字体大小 private int TextSize = Convert.ToInt32(ConfigurationManager.AppSettings["TextSize"]); //水印图片路径 private string WaterImg = ConfigurationManager.AppSettings["WaterImg"]; //文件上传路径 private string filePath = ConfigurationManager.AppSettings["upFilePath"]; /// <summary> /// 文件上传方法 /// </summary> /// <param name="_postedFile">HttpPostedFile</param> /// <param name="_isWater">是否打水印,0代表默认,1代表打,2代表不打</param> /// <param name="_fileType">文件类型,1代表普通文档文件,2代表图片文件,3代表视频文件</param> /// <returns>上传成功的路径,错误返回错误信息</returns> public string fileSaveAs(HttpPostedFileBase _postedFile, int _isWater, int _fileType) { //获得允许上传的文件 string realType = this.fileType; switch (_fileType) { case 1: realType = this.fileType; break; case 2: realType = this.imgType; break; case 3: realType = this.videoType; break; } try { string _fileExt = _postedFile.FileName.Substring(_postedFile.FileName.LastIndexOf(".") + 1); //验证合法的文件 if (!CheckFileExt(realType, _fileExt)) { return "不允许上传" + _fileExt + "类型的文件!"; } if (this.fileSize > 0 && _postedFile.ContentLength > fileSize * 1024 * 1024) { return "文件超过限制的大小啦!"; } string _fileName = DateTime.Now.ToString("yyyyMMddHHmmssff") + "." + _fileExt; //随机文件名 //检查保存的路径 是否有/开头结尾 if (this.filePath.StartsWith("/") == false) { this.filePath = "/" + this.filePath; } if (this.filePath.EndsWith("/") == false) { this.filePath = this.filePath + "/"; } //按日期归类保存 string _datePath = DateTime.Now.ToString("yyyyMMdd") + "/"; this.filePath += _datePath; //获得要保存的文件路径 string serverFileName = this.filePath + _fileName; //物理完整路径 string toFileFullPath = HttpContext.Current.Server.MapPath(this.filePath); //检查是否有该路径没有就创建 if (!Directory.Exists(toFileFullPath)) { Directory.CreateDirectory(toFileFullPath); } //将要保存的完整文件名 string toFile = toFileFullPath + _fileName; //保存文件 _postedFile.SaveAs(toFile); //如果是图片,判断是否打图片水印 if (_fileType == 2 && CheckFileExt(realType, _fileExt) && (_isWater == 2 || _isWater == 0 && isWater)) { switch (WaterType) { //文本 case 1: if (!string.IsNullOrEmpty(WaterText)) { ImageWaterMark.AddImageSignText(serverFileName, this.filePath + _fileName, this.WaterText, 9, 80, "Arial", TextSize); } break; case 2: if (WaterImg != "none" && System.IO.File.Exists(HttpContext.Current.Server.MapPath(WaterImg))) { ImageWaterMark.AddImageSignPic(serverFileName, this.filePath + _fileName, this.WaterImg, 9, 80, 8); } break; } } return serverFileName; } catch { return "上传失败!"; } } /// <summary> /// 检查是否为合法的上传文件 /// </summary> /// <returns>bool</returns> private bool CheckFileExt(string _fileType, string _fileExt) { if (_fileType != "*") { string[] allowExt = _fileType.Split('|'); for (int i = 0; i < allowExt.Length; i++) { if (allowExt[i].ToLower() == "." + _fileExt.ToLower()) { return true; } } return false; } else { return true; } } } }
右键Models,添加ImageWaterMark.cs类里面封装的是打水印的方法,修改完整代码如下:
using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.Drawing.Imaging; using System.Drawing; using System.IO; namespace MyShopTest.Models { public class ImageWaterMark { /// <summary> /// 图片水印 /// </summary> /// <param name="imgPath">服务器图片相对路径</param> /// <param name="filename">保存文件名</param> /// <param name="watermarkFilename">水印文件相对路径</param> /// <param name="watermarkStatus">图片水印位置 0=不使用 1=左上 2=中上 3=右上 4=左中 9=右下</param> /// <param name="quality">附加水印图片质量,0-100</param> /// <param name="watermarkTransparency">水印的透明度 1--10 10为不透明</param> public static void AddImageSignPic(string imgPath, string filename, string watermarkFilename, int watermarkStatus, int quality, int watermarkTransparency) { byte[] _ImageBytes = File.ReadAllBytes(HttpContext.Current.Server.MapPath(imgPath)); Image img = Image.FromStream(new System.IO.MemoryStream(_ImageBytes)); filename = HttpContext.Current.Server.MapPath(filename); if (watermarkFilename.StartsWith("/") == false) watermarkFilename = "/" + watermarkFilename; watermarkFilename = HttpContext.Current.Server.MapPath(watermarkFilename); if (!File.Exists(watermarkFilename)) return; Graphics g = Graphics.FromImage(img); //设置高质量插值法 //g.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.High; //设置高质量,低速度呈现平滑程度 //g.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighQuality; Image watermark = new Bitmap(watermarkFilename); if (watermark.Height >= img.Height || watermark.Width >= img.Width) return; ImageAttributes imageAttributes = new ImageAttributes(); ColorMap colorMap = new ColorMap(); colorMap.OldColor = Color.FromArgb(255, 0, 255, 0); colorMap.NewColor = Color.FromArgb(0, 0, 0, 0); ColorMap[] remapTable = { colorMap }; imageAttributes.SetRemapTable(remapTable, ColorAdjustType.Bitmap); float transparency = 0.5F; if (watermarkTransparency >= 1 && watermarkTransparency <= 10) transparency = (watermarkTransparency / 10.0F); float[][] colorMatrixElements = { new float[] {1.0f, 0.0f, 0.0f, 0.0f, 0.0f}, new float[] {0.0f, 1.0f, 0.0f, 0.0f, 0.0f}, new float[] {0.0f, 0.0f, 1.0f, 0.0f, 0.0f}, new float[] {0.0f, 0.0f, 0.0f, transparency, 0.0f}, new float[] {0.0f, 0.0f, 0.0f, 0.0f, 1.0f} }; ColorMatrix colorMatrix = new ColorMatrix(colorMatrixElements); imageAttributes.SetColorMatrix(colorMatrix, ColorMatrixFlag.Default, ColorAdjustType.Bitmap); int xpos = 0; int ypos = 0; switch (watermarkStatus) { case 1: xpos = (int)(img.Width * (float).01); ypos = (int)(img.Height * (float).01); break; case 2: xpos = (int)((img.Width * (float).50) - (watermark.Width / 2)); ypos = (int)(img.Height * (float).01); break; case 3: xpos = (int)((img.Width * (float).99) - (watermark.Width)); ypos = (int)(img.Height * (float).01); break; case 4: xpos = (int)(img.Width * (float).01); ypos = (int)((img.Height * (float).50) - (watermark.Height / 2)); break; case 5: xpos = (int)((img.Width * (float).50) - (watermark.Width / 2)); ypos = (int)((img.Height * (float).50) - (watermark.Height / 2)); break; case 6: xpos = (int)((img.Width * (float).99) - (watermark.Width)); ypos = (int)((img.Height * (float).50) - (watermark.Height / 2)); break; case 7: xpos = (int)(img.Width * (float).01); ypos = (int)((img.Height * (float).99) - watermark.Height); break; case 8: xpos = (int)((img.Width * (float).50) - (watermark.Width / 2)); ypos = (int)((img.Height * (float).99) - watermark.Height); break; case 9: xpos = (int)((img.Width * (float).99) - (watermark.Width)); ypos = (int)((img.Height * (float).99) - watermark.Height); break; } g.DrawImage(watermark, new Rectangle(xpos, ypos, watermark.Width, watermark.Height), 0, 0, watermark.Width, watermark.Height, GraphicsUnit.Pixel, imageAttributes); ImageCodecInfo[] codecs = ImageCodecInfo.GetImageEncoders(); ImageCodecInfo ici = null; foreach (ImageCodecInfo codec in codecs) { if (codec.MimeType.IndexOf("jpeg") > -1) ici = codec; } EncoderParameters encoderParams = new EncoderParameters(); long[] qualityParam = new long[1]; if (quality < 0 || quality > 100) quality = 80; qualityParam[0] = quality; EncoderParameter encoderParam = new EncoderParameter(System.Drawing.Imaging.Encoder.Quality, qualityParam); encoderParams.Param[0] = encoderParam; if (ici != null) img.Save(filename, ici, encoderParams); else img.Save(filename); g.Dispose(); img.Dispose(); watermark.Dispose(); imageAttributes.Dispose(); } /// <summary> /// 文字水印 /// </summary> /// <param name="imgPath">服务器图片相对路径</param> /// <param name="filename">保存文件名</param> /// <param name="watermarkText">水印文字</param> /// <param name="watermarkStatus">图片水印位置 0=不使用 1=左上 2=中上 3=右上 4=左中 9=右下</param> /// <param name="quality">附加水印图片质量,0-100</param> /// <param name="fontname">字体</param> /// <param name="fontsize">字体大小</param> public static void AddImageSignText(string imgPath, string filename, string watermarkText, int watermarkStatus, int quality, string fontname, int fontsize) { byte[] _ImageBytes = File.ReadAllBytes(HttpContext.Current.Server.MapPath(imgPath)); Image img = Image.FromStream(new System.IO.MemoryStream(_ImageBytes)); filename = HttpContext.Current.Server.MapPath(filename); Graphics g = Graphics.FromImage(img); Font drawFont = new Font(fontname, fontsize, FontStyle.Regular, GraphicsUnit.Pixel); SizeF crSize; crSize = g.MeasureString(watermarkText, drawFont); float xpos = 0; float ypos = 0; switch (watermarkStatus) { case 1: xpos = (float)img.Width * (float).01; ypos = (float)img.Height * (float).01; break; case 2: xpos = ((float)img.Width * (float).50) - (crSize.Width / 2); ypos = (float)img.Height * (float).01; break; case 3: xpos = ((float)img.Width * (float).99) - crSize.Width; ypos = (float)img.Height * (float).01; break; case 4: xpos = (float)img.Width * (float).01; ypos = ((float)img.Height * (float).50) - (crSize.Height / 2); break; case 5: xpos = ((float)img.Width * (float).50) - (crSize.Width / 2); ypos = ((float)img.Height * (float).50) - (crSize.Height / 2); break; case 6: xpos = ((float)img.Width * (float).99) - crSize.Width; ypos = ((float)img.Height * (float).50) - (crSize.Height / 2); break; case 7: xpos = (float)img.Width * (float).01; ypos = ((float)img.Height * (float).99) - crSize.Height; break; case 8: xpos = ((float)img.Width * (float).50) - (crSize.Width / 2); ypos = ((float)img.Height * (float).99) - crSize.Height; break; case 9: xpos = ((float)img.Width * (float).99) - crSize.Width; ypos = ((float)img.Height * (float).99) - crSize.Height; break; } g.DrawString(watermarkText, drawFont, new SolidBrush(Color.White), xpos + 1, ypos + 1); g.DrawString(watermarkText, drawFont, new SolidBrush(Color.Black), xpos, ypos); ImageCodecInfo[] codecs = ImageCodecInfo.GetImageEncoders(); ImageCodecInfo ici = null; foreach (ImageCodecInfo codec in codecs) { if (codec.MimeType.IndexOf("jpeg") > -1) ici = codec; } EncoderParameters encoderParams = new EncoderParameters(); long[] qualityParam = new long[1]; if (quality < 0 || quality > 100) quality = 80; qualityParam[0] = quality; EncoderParameter encoderParam = new EncoderParameter(System.Drawing.Imaging.Encoder.Quality, qualityParam); encoderParams.Param[0] = encoderParam; if (ici != null) img.Save(filename, ici, encoderParams); else img.Save(filename); g.Dispose(); img.Dispose(); } } }
现在我们来看看修改后的视图(删除Views/Product/Delete.cshtml):
Create.cshtml
@model MyShopTest.Models.Product @{ ViewBag.Title = "商品添加"; } <h2> 商品添加</h2> <script src="@Url.Content("~/Scripts/jquery.validate.min.js")" type="text/javascript"></script> <script src="@Url.Content("~/Scripts/jquery.validate.unobtrusive.min.js")" type="text/javascript"></script> @using (Html.BeginForm("Create", "Product", FormMethod.Post, new { enctype = "multipart/form-data" })) { @*@Html.ValidationSummary(true)*@ @Html.ValidationMessage("error") <fieldset> <legend>商品添加</legend> <div class="editor-label"> @Html.LabelFor(model => model.Title) </div> <div class="editor-field"> @Html.EditorFor(model => model.Title) @Html.ValidationMessageFor(model => model.Title) </div> <div class="editor-label"> @Html.LabelFor(model => model.Price) </div> <div class="editor-field"> @Html.EditorFor(model => model.Price) @Html.ValidationMessageFor(model => model.Price) </div> <div class="editor-label"> @Html.LabelFor(model => model.ImgUrl) </div> <div class="editor-field"> <input type="file" name="filImgs" /> <input type="hidden" name="ImgUrl" value="0" /> @Html.ValidationMessageFor(model => model.ImgUrl) </div> <div class="editor-label"> @Html.LabelFor(model => model.CategoryId, "类别") </div> <div class="editor-field"> @Html.DropDownList("CategoryId") @Html.ValidationMessageFor(model => model.CategoryId) @Html.Hidden("AddTime",DateTime.Now) </div> <p> <input type="submit" value="添加" /> </p> </fieldset> } <div> @Html.ActionLink("返回列表", "Index") </div>
enctype = "multipart/form-data"
这是表示该表单的以流的形式提交,因为该表单内有文件上传,另外说一下new { enctype = "multipart/form-data" }这种参数形式,表示的是表单的html属性,是一种name=value的形式,也就是你想给这个表单另外加上什么属性,就这样表示出来就可以了,多个属性间用逗号隔开,我们可以看一下解析后的代码:
直接把enctype = "multipart/form-data"加到了表单属性里,
@Html.DropDownList("CategoryId")
这段代码的意思是会从ViewBag和model里寻找名为CategoryId的selectList来生成下拉框,它有多重重载方式,可以指定默认选项等。
修改后完整Edit.cshtml:
@model MyShopTest.Models.Product @{ ViewBag.Title = "商品编辑"; } <h2> 商品编辑</h2> <script src="@Url.Content("~/Scripts/jquery.validate.min.js")" type="text/javascript"></script> <script src="@Url.Content("~/Scripts/jquery.validate.unobtrusive.min.js")" type="text/javascript"></script> @using (Html.BeginForm("Edit", "Product", FormMethod.Post, new { enctype = "multipart/form-data" })) { @Html.ValidationSummary(true) <fieldset> <legend>商品编辑</legend> @Html.HiddenFor(model => model.Id) <div class="editor-label"> @Html.LabelFor(model => model.Title) </div> <div class="editor-field"> @Html.EditorFor(model => model.Title) @Html.ValidationMessageFor(model => model.Title) </div> <div class="editor-label"> @Html.LabelFor(model => model.Price) </div> <div class="editor-field"> @Html.EditorFor(model => model.Price) @Html.ValidationMessageFor(model => model.Price) </div> <div class="editor-label"> @Html.LabelFor(model => model.ImgUrl) </div> <div class="editor-field"> <input type="file" name="filImgs"/> <input type="hidden" name="ImgUrl" value="@Model.ImgUrl"/> @Html.ValidationMessageFor(model => model.ImgUrl) </div> <div class="editor-label"> @Html.LabelFor(model => model.CategoryId, "类别") </div> <div class="editor-field"> @Html.DropDownList("CategoryId") @Html.ValidationMessageFor(model => model.CategoryId) </div> <p> <input type="submit" value="保存" /> </p> </fieldset> } <div> @Html.ActionLink("返回列表", "Index") </div>
修改后完整Details.cshtml:
@model MyShopTest.Models.Product @{ ViewBag.Title = "商品详细"; } <h2> 商品详细</h2> <fieldset> <legend>商品详细</legend> <div style="margin: 0 auto;"> <img src="@Model.ImgUrl" width="300px" height="200px"/> <br /> <ul> <li> @Html.DisplayFor(model => model.Title) </li> <li> @Html.DisplayFor(model => model.Category.Name) </li> <li> @Html.DisplayFor(model => model.Price) </li> <li> @Html.DisplayFor(model => model.AddTime) </li> </ul> </div> </fieldset> <p> @Html.ActionLink("返回列表", "Index") | </p>
修改后完整Index.cshtml:
@model IEnumerable<MyShopTest.Models.Product> @{ ViewBag.Title = "商品列表"; } <script src="../../js/Helper.js" type="text/javascript"></script> <script type="text/jscript"> function search() { $("form").attr("action", "/Product/Index").submit(); //更改表单提交的Action } </script> <h2> 商品列表</h2> @using (Html.BeginForm("Delete", "Product", FormMethod.Post)) { <p> @Html.ActionLink("添加", "Create") <input type="button" onclick="delIds();" value="批量删除" /> </p> <p> 名称:<input type="text" name="title" value="@ViewBag.Name" /> <input type="button" value="查询" onclick="search();" /> </p> <table> <tr> <th> <input type="checkbox" name="ckAll" id="ckAll" onclick="selectAll();" /><span id="spInfo">全选</span> </th> <th> 名称 </th> <th> 价格 </th> <th> 类别 </th> <th> 上架时间 </th> <th> 操作 </th> </tr> @foreach (var item in Model) { <tr> <td> @Html.CheckBox("ckSelect", false, new { value = item.Id }) </td> <td> @Html.DisplayFor(modelItem => item.Title) </td> <td> @Html.DisplayFor(modelItem => item.Price) </td> <td> @Html.DisplayFor(modelItem => item.Category.Name) </td> <td> @Html.DisplayFor(modelItem => item.AddTime) </td> <td> @Html.ActionLink("编辑", "Edit", new { id = item.Id }) | @Html.ActionLink("查看", "Details", new { id = item.Id }, new { target="_blank"}) | </td> </tr> } </table> }
item.Category.Name系统会自动识别主外键关系,得到对应类别的名称
在项目根目录下添加js文件夹,添加文件Helper.js,完整代码如下:
function confirmDel(itemId,url) { if (confirm("确认删除吗?")) { window.location.href = url + itemId; } } function selectAll() { var checked = $("#ckAll").attr("checked"); $("input[name='ckSelect']").attr("checked", checked); if (checked) { $("#spInfo").html("反选"); } else { $("#spInfo").html("全选"); } } function delIds() { if (confirm("确认要删除选中数据吗?")) { var checkedCount = $("input[name='ckSelect']:checked").length; if (checkedCount > 0) { $("form").first().submit(); //提交表单 } else { alert("请先选择操作项!"); } } }
重新编译项目,运行,点击商品管理:
点击编辑,自行上传一些图片,
查看详细:
至此,商品和类别管理已基本完成