转自http://www.cnblogs.com/think8848/archive/2011/09/14/2175432.html
转载请注明作者(think8848)和出处(http://think8848.cnblogs.com)
依照本人惯例,开篇先说些与主题无关的话:本来打算把写博客的这个习惯坚持下去,就算不能出精品,也能出一些水货,对于某些小问题提供点解决方案,
但是今年的8月真可谓是多事之“秋”,很多事情都凑到一起去了,几乎没有时间学习新的东西,更别说去写博客了,9月眼看要过去一半了,昨天才憋出一个小东
西,觉得还稍能滥竽充数下。
打算用ASP.NET MVC实现公司的某产品了,昨天遇到一个问题:在异常发生时转回提交前的页面后,原来输入的内容不见了,这可是个大问题,记得以前我在写《ASP.NET MVC异常处理方案》一文时已经解决了这个问题,怎么又看不见提交前的输入了呢,把以前的代码打开后发现了问题所在:
在当前的代码中表单的代码为:
< input id &#61; "txtName" name &#61; "Name" type &#61; "text" /> |
而之前能出现效果的表单代码为&#xff1a;
&#64;Html.TextBoxFor(x &#61;> x.Name) |
稍经测试&#xff0c;就发现&#xff0c;使用Html的扩展方法生成的标签可以获得提交之前的值&#xff0c;但是自已手写的则不行&#xff0c;所以这个
TextBox扩展方法中肯定有某种机制&#xff0c;能自动将值填进标签中。一开始和同事讨论后觉得&#xff0c;使用
&#64;Html.TextBoxFor方法有一个好处&#xff0c;那就是如果更换了Name属性的名称&#xff0c;VS可以自动重构代码&#xff0c;使*.cshtml代码的x.Name自
动更新至新的属性名&#xff0c;经测试后发现原来不是想的这么回事&#xff0c;修改了Name属性的名称&#xff0c;如:PName&#xff0c;使用VS重构代码&#xff0c;发现在视图中属性名居然没有改过
来&#xff1b;而且如果使用Html的扩展方法&#xff0c;似乎也有一些问题&#xff0c;最重要一点就是不直观&#xff0c;在目前的Razor引擎中还不太明显&#xff0c;反正也没有设计器&#xff0c;但是如果以后有
了Razor引擎有了设计器功能&#xff0c;基本可以断定的是&#xff0c;使用&#64;Html.TextBoxFor()的方式很难能做到所见即所得的效果&#xff0c;而且在一个
cshtml页面中&#xff0c;即时不能使用设计器&#xff0c;看代码时如果视图上使用&#64;Html.XXX也不是很直观&#xff0c;既然使用Html扩展方法的方式即不能有利于重构代
码&#xff0c;又不直观&#xff0c;那么使用Html标签的理由似乎就变的充分多了&#xff0c;如果使用这种方法&#xff0c;即使不会C#的人也可以写出来页面。
在这种想法的驱动下&#xff0c;想出一个办法&#xff1a;自已实现一个填充标签值的扩展方法。于是打开ASP.NET MVC
3源代码&#xff0c;看看在这个TextBox内部到底在做些什么&#xff0c;为什么它可以把模型(ViewData.Model)中的值&#xff0c;以及
ViewData.ModelState中的值填充到标签中&#xff0c;一步一步查下来&#xff0c;发现原来实现方法比较简单&#xff0c;直接上代码&#xff1a;
public static class HtmlValueExtension |
public static MvcHtmlString Value( this HtmlHelper html, Expression> expression) |
ModelMetadata metadata &#61; ModelMetadata.FromLambdaExpression(expression, html.ViewData); |
return Value(html, metadata.PropertyName); |
public static MvcHtmlString Value( this HtmlHelper html, string name) |
string attemptedValue &#61; null ; |
if (html.ViewData.ModelState.TryGetValue(name, out modelState)) |
if (modelState.Value !&#61; null ) |
attemptedValue &#61; modelState.Value.ConvertTo( typeof ( string ), null ).ToString(); |
return new MvcHtmlString(attemptedValue ?? Convert.ToString(html.ViewData.Eval(name), CultureInfo.CurrentCulture)); |
定义一个HtmlHelper的扩展方法Value&#xff0c;然后根据
Lambda表达式获取到指定属性的元数据&#xff0c;优先考虑从ModelState中拿出对应的数据&#xff0c;也就是提交前页面表单数据&#xff0c;如果这个数据为null,则尝
试ViewData.Model中指定的数据&#xff0c;很简单吧:)
有了这个类&#xff0c;在页面上使用如下代码调用&#xff1a;
< input id &#61; "txtDeptName" name &#61; "Name" type &#61; "text" value&#61;"&#64;Html.Value(x &#61;> x.Name)"/> |
这样&#xff0c;就可以达到与&#64;Html.TextBox()一样的效果了&#xff0c;但是从视图的代码角度来说&#xff0c;直观了不少&#xff0c;而且如果以后Razor引擎有了设计器&#xff0c;估计也可以不用调试也能看到页面效果了。
最后再友情提示下&#xff0c;如果您在一个Razor的视图中定义了一个表单标签&#xff0c;这个表单标签的值并不对应Model的某个属性&#xff0c;这时如果您想获取提交前的值话&#xff0c;使用Request.Params["TagName"]即可。