Asp.Net MVC2 控件开发实例(1)
项目中“详细地址”这一数据项要细化到门牌号,比如北京市—海淀区—北四环西路—叉叉楼—5单元—1号,页面上表现为一个数据项,但实际上这个数据项包含很多子项,且不能随意编辑,比如说你自己写了个“宇宙1号楼”,这是不合规范的,那么我就要做一个控件,实现这样一个功能:点击一个标题为"详细地址"的文本框,会弹出一个层,里面有各个地址子项的选择,选择后点确定,会关闭这个弹出层并把选择的结果显示在最开始点击的文本框,最好里面存的是可序列化的数据。我要的效果如下图:
在提交页面时我需要获取到详细地址的所有子项,当然通过formcollection应该可以一项一项获取到,不过就需要每个用到这个控件的人自己来控制,比较麻烦。在这里我将所有的子项名称和值组合成一个json串保存在一个隐藏控件中。构建htmlhelper不多说了,直接上代码:
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表达式,一个是可见文本框的,一个是隐藏域的,这样方便后面取值。
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为
现在我要反序列化这个json(在这之前自然要定义一个实体类StandardAddressModels ,与json的key一一对应)
{
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;
}
2 address = ForeignerManagement.CommonFunction.FMCommon.ConvertToModel(jsonString, address.GetType());
不写这句上面代码不出来,博客园bug。。