Asp.Net MVC2 控件开发实例(1)

项目中“详细地址”这一数据项要细化到门牌号,比如北京市—海淀区—北四环西路—叉叉楼—5单元—1号,页面上表现为一个数据项,但实际上这个数据项包含很多子项,且不能随意编辑,比如说你自己写了个“宇宙1号楼”,这是不合规范的,那么我就要做一个控件,实现这样一个功能:点击一个标题为"详细地址"的文本框,会弹出一个层,里面有各个地址子项的选择,选择后点确定,会关闭这个弹出层并把选择的结果显示在最开始点击的文本框,最好里面存的是可序列化的数据。我要的效果如下图:

 

在提交页面时我需要获取到详细地址的所有子项,当然通过formcollection应该可以一项一项获取到,不过就需要每个用到这个控件的人自己来控制,比较麻烦。在这里我将所有的子项名称和值组合成一个json串保存在一个隐藏控件中。构建htmlhelper不多说了,直接上代码:

helper
 1 public static MvcHtmlString AddressFor<TModel, TValue>(this HtmlHelper<TModel> helper, Expression<Func<TModel, TValue>> expressionMC, Expression<Func<TModel, TValue>> expressionDM, object htmlAttributes)
 2         {
 3             string modelMCName = ExpressionHelper.GetExpressionText(expressionMC);
 4             string modelDMName = ExpressionHelper.GetExpressionText(expressionDM);
 5 
 6             StringBuilder sb = new StringBuilder();
 7             TagBuilder tag = new TagBuilder("input");
 8             tag.Attributes.Add("type""text");
 9             tag.Attributes.Add("name", modelMCName);
10             tag.GenerateId(modelMCName);
11 
12             TagBuilder tagHidden = new TagBuilder("input");
13             tagHidden.Attributes.Add("type""hidden");
14             tagHidden.Attributes.Add("name", modelDMName);
15             tagHidden.GenerateId(modelDMName);
16 
17             StringBuilder sbClick = new StringBuilder();
18             sbClick.Append("if($('#divHid').html().length < 1){");
19             sbClick.Append("$.post('/FM/Address/AddressFloatView',function(data){$('#divHid').html(data);}");
20             sbClick.Append(")};");
21             sbClick.Append("$('#divHid').dialog('open');");
22 
23             RouteValueDictionary dictionary = new RouteValueDictionary();
24             dictionary.Add("onclick", sbClick.ToString());
25             if (htmlAttributes != null)
26                 tag.MergeAttributes(new RouteValueDictionary(htmlAttributes));
27             tag.MergeAttributes(dictionary);
28             sb.Append(tag.ToString(TagRenderMode.SelfClosing));
29             sb.Append(tagHidden.ToString(TagRenderMode.SelfClosing));
30             sb.Append(GetScript(modelMCName, modelDMName));
31             return MvcHtmlString.Create(sb.ToString());
32         }

 

 这里我绑定了两个lamda表达式,一个是可见文本框的,一个是隐藏域的,这样方便后面取值。 

GetScript方法
 1 private static string GetScript(string modelMCName, string modelDMName)
 2         {
 3             StringBuilder script = new StringBuilder();
 4             script.Append("<script type='text/javascript'>");
 5             script.Append("$(function () {");
 6             script.Append("$(document.forms[0]).after('<form id=\"formAddress\"><div id=\"divHid\"></div></form>');");
 7             script.Append("initDialog('#formAddress', '标准化地址', '#divHid', 400, 530);");
 8             script.Append("});");
 9             script.Append("function setValToAddress(){");
10             script.Append("var v='';var jsonContent = '{';");
11             script.Append("$.each($('#divHid input[type!=\"button\"]'),");
12             script.Append("function (i, n) {if ($(this).attr('type') == 'text' && $(this).attr('disabled') == false) v += $(this).val(); ");
13             script.Append("jsonContent += '\"' + $(this).attr('id') + '\":\"' + $(this).val() + '\",';});");
14             script.Append("jsonContent = jsonContent.substring(0, jsonContent.length - 1) + '}';");
15             script.AppendFormat("$('{0}').val(v);""#" + modelMCName);
16             script.AppendFormat("$('{0}').val(jsonContent);""#" + modelDMName);
17             script.Append("}");
18 
19             script.Append("</script>");
20             return script.ToString();
21         }

调用时为以下形式:

 地址标准化测试helper:<%=Html.AddressFor(m => m.Address, m => m.HidAddress, new { @style = "width:300px" })%>

 

 由于特殊原因,弹出层的代码我选择了异步加载:$.post('/FM/Address/AddressFloatView',function(data){$('#divHid').html(data);}

 AddressFloatView是这个PartialView的action,里面直接return PartialView(address);不过异步action要加[HttpPost()]。

 做到这,我已经可以获取到拼接后的详细地址的名称(保存在文本控件中)和详细地址的代码,值的json字符串,如下图

 

 隐藏域的html为

代码
1 <INPUT id=HidAddress value='{"DZ_XZQH":"110000","Auto_DZ_XZQH":"北京","DZ_PCS":"320502520000","Auto_DZ_PCS":"沧浪分局公园派出所","DZ_JLX":"","Auto_DZ_JLX":"","DZ_MPQZ":"01","Auto_DZ_MPQZ":"附","DZ_MPH":"32","DZ_MPHZ":"1","Auto_DZ_MPHZ":"号","DZ_FH":"456","DZ_FHHZ":"1","Auto_DZ_FHHZ":"号","DZ_ZLQZ":"01","Auto_DZ_ZLQZ":"东楼","DZ_ZLH":"567","DZ_ZLHZ":"2","Auto_DZ_ZLHZ":"栋","DZ_DYH":"23","DZ_SH":"43","DZ_SHHZ":"2","Auto_DZ_SHHZ":"号","DZ_FSDZ":"地球","DZ_DJDW":"320500000000","DZ_DJR":"320500000000201000000255","DZ_DJSJ":"2010-12-07"}' type=hidden name=HidAddress /> 

现在我要反序列化这个json(在这之前自然要定义一个实体类StandardAddressModels ,与json的key一一对应)

代码
public static StandardAddressModels ConvertToAddressModel(string jsonString)
        {
            MemoryStream stream 
= new MemoryStream(Encoding.UTF8.GetBytes(jsonString));
            DataContractJsonSerializer ser 
= new DataContractJsonSerializer(typeof(StandardAddressModels));
            stream.Position 
= 0;
            StandardAddressModels address 
= (StandardAddressModels)ser.ReadObject(stream);
            stream.Close();
            
return address;
        }

 现在我就可以调用这个方法获取序列化的结构体了:

StandardAddressModels address = ForeignerManagement.CommonFunction.FMCommon.ConvertToAddressModel(jsonString); 

扩展一下,反序列化为其他的结构体 :

需要定义一个接口 IDataContract ,然后向反序列化的结构体继承这个接口即可

代码:

public static StandardAddressModels ConvertToAddressModel(string jsonString)
        {
            MemoryStream stream = new MemoryStream(Encoding.UTF8.GetBytes(jsonString));
            DataContractJsonSerializer ser = new DataContractJsonSerializer(typeof(StandardAddressModels));
            stream.Position = 0;
            StandardAddressModels address = (StandardAddressModels)ser.ReadObject(stream);
            stream.Close();
            return address;
        }

 调用时:
1 StandardAddressModels address = new StandardAddressModels();
2 address = ForeignerManagement.CommonFunction.FMCommon.ConvertToModel(jsonString, address.GetType());

 

不写这句上面代码不出来,博客园bug。。

 

 

 

 

posted @ 2010-12-07 16:17  喵了个汪的2371  阅读(1267)  评论(3编辑  收藏  举报