做网站,少不了页面,用.net做的话,aspx文件基本都是页面布局什么的,cs文件则是处理程序,这些都很普通啦
但每次在页面里调用变量的时候,都需要在cs里写一大堆东西用来支持页面调用,就很繁琐,很麻烦,很恶心了
然后,老顾就又想着怎么偷懒了。。。。。。怎么能减少cs的改动呢?
首先,我们把需要输出的内容定义成一个Hashtable,然后将Hashtable输出到页面
那么,我们先来实现这个吧
public static class PageControlHelper
{
private static Page CurrentPage
{
get
{
return HttpContext.Current.CurrentHandler as Page;
}
}
public static void SetToControls(Hashtable ht)
{
Page page = CurrentPage;
SetToControls(page, ht);
}
public static void SetToControls(Control ctl, Hashtable ht)
{
if (ht.Count != 0)
{
int size = ht.Keys.Count;
foreach (string key in ht.Keys)
{
object val = ht[key];
if (val == null)
{
continue;
}
Control control = ctl.FindControl(key);
if (control != null)
{
Type t = val.GetType();
if (t.Name == "bool" || t.Name == "Boolean")
{
val = (bool)val ? 1 : 0;
}
#region 根据control类型进行不同的赋值设置
#region CheckBoxList
if (control is CheckBoxList)
{
CheckBoxList cbl = (CheckBoxList)control;
string[] v = val.ToString().Split(new string[] { "," }, StringSplitOptions.None);
foreach (ListItem li in cbl.Items)
{
for (int i = 0; i < v.Length; i++)
{
if (v[i] == li.Value || v[i].Trim() == li.Value)
{
li.Selected = true;
}
}
}
}
#endregion
#region RadioButtonList
if (control is RadioButtonList)
{
RadioButtonList rbl = (RadioButtonList)control;
rbl.SelectedValue = val.ToString();
}
#endregion
#region DropDownList
if (control is DropDownList)
{
DropDownList ddl = (DropDownList)control;
ddl.SelectedValue = val.ToString();
}
#endregion
#region TextBox
if (control is TextBox)
{
TextBox tb = (TextBox)control;
tb.Text = val.ToString();
}
#endregion
#region Label
if (control is Label)
{
Label lab = (Label)control;
lab.Text = val.ToString();
}
#endregion
#region HtmlSelect
if (control is HtmlSelect)
{
HtmlSelect hs = (HtmlSelect)control;
//hs.Items.FindByValue(val.ToString()).Selected = true;
hs.Value = val.ToString();
}
#endregion
#region HtmlInputText,input type=text
if (control is HtmlInputText)
{
HtmlInputText hit = (HtmlInputText)control;
hit.Value = val.ToString();
}
#endregion
#region HtmlInputHidden,input type=hidden
if (control is HtmlInputHidden)
{
HtmlInputHidden hih = (HtmlInputHidden)control;
hih.Value = val.ToString();
}
#endregion
#region HtmlInputPassword,input type=password
if (control is HtmlInputPassword)
{
HtmlInputPassword hip = (HtmlInputPassword)control;
hip.Value = val.ToString();
}
#endregion
#region HtmlInputCheckBox,input type=checkbox,仅单一选项可用
if (control is HtmlInputCheckBox)
{
HtmlInputCheckBox htmlinputcheckbox = (HtmlInputCheckBox)control;
if (val.ToString() == "1")
{
htmlinputcheckbox.Checked = true;
}
}
#endregion
#region HtmlTextArea,textarea标签
if (control is HtmlTextArea)
{
HtmlTextArea area = (HtmlTextArea)control;
area.Value = val.ToString();
}
#endregion
#region HtmlImage,img标签
if (control is HtmlImage)
{
HtmlImage img = (HtmlImage)control;
img.Src = val.ToString();
}
#endregion
#region HtmlAnchor,a标签
if (control is HtmlAnchor)
{
HtmlAnchor ha = (HtmlAnchor)control;
ha.InnerHtml = val.ToString();
}
#endregion
#region HtmlGenericControl
if (control is HtmlGenericControl)
{
HtmlGenericControl hgc = (HtmlGenericControl)control;
hgc.InnerHtml = val.ToString();
}
#endregion
#endregion
}
}
}
}
}
这个是用来实现,所有带有runat="server"的控件,自动获得Hashtable对应的值的
然后我们定义一个基类,让所有页面继承他
public Hashtable HT = new Hashtable();
protected override void OnLoad(EventArgs e)
{
base.OnLoad(e); // 在继承的页面里定义Hashtable的值
PageControlHelper.SetToControls(HT);
}
这样就能直接输出到页面了,但这样还是不太方便,因为我们可能需要将值进行格式化,按照特定格式输出
怎么能支持一下呢?
于是老顾写了一个格式化的类
public static class TemplateParser
{
public static string Parse(string html, Hashtable ht = null)
{
string result = html;
#region 替换$field$设置的变量
if (ht != null && ht.Count > 0)
{
// hashtable 转成datarow
DataRow dr = DataBaseHelper.ToDataRow(ht);
foreach (string key in ht.Keys)
{
result = RegexExpand.Replace(result, @"\$" + key + @"\$", ht[key] == null ? string.Empty : ht[key].ToString(), RegexOptions.IgnoreCase);
MatchCollection ks = RegexExpand.Matches(result, @"\$" + key + @"\.[^$]+\$", RegexOptions.IgnoreCase);
foreach (Match m in ks)
{
// 将 $field$格式变形成^field^格式进行解析
result = result.Replace(m.Value, Parse(dr, RegexExpand.Replace(m.Value, @"^\$|\$$", "^", RegexOptions.IgnoreCase)));
}
}
}
result = RegexExpand.Replace(result, @"[\$][a-z][a-z0-9_]*(\.[^\$\.]+)*[\$]", "");
#endregion
return result;
}
public static class RefFun
{
public static string RefFun_Tostring(object val, string args = null)
{
string result = string.Empty;
if (!string.IsNullOrEmpty(val.ToString()))
{
if (string.IsNullOrEmpty(args))
{
result = val.ToString();
}
else
{
Type t = val.GetType();
MethodInfo mi = t.GetMethod("ToString", new Type[] { typeof(string) });
if (mi != null)
{
result = (string)mi.Invoke(val, new object[] { args });
}
else
{
result = val.ToString();
}
}
}
return result;
}
}
public static string Parse(DataRow row, string format)
{
string result = format;
MatchCollection mc = RegexExpand.Matches(result, @"[\^](\w+)(?:\.([^\.]+?)((?:(\.[^\^\.]+)*?)?))?[\^]");
for (int i = 0; i < mc.Count; i++)
{
string field = mc[i].Groups[1].Value.ToUpper();
string arg = mc[i].Groups[2].Value.ToLower();
string para = mc[i].Groups[3].Value;
// 如果DataRow存在对应字段
if (row.Table.Columns.IndexOf(field) > -1)
{
try
{
// 使用枚举定义链接类型
LinkType lt = LinkType.Default;
OutputLinkType olt;
object obj = row.ItemArray[row.Table.Columns.IndexOf(field)];
string val = obj.ToString();
#region 无参数仅字段名的直接输出
if (string.IsNullOrEmpty(arg))
{
if (!string.IsNullOrEmpty(val.Trim()))
{
switch (obj.GetType().Name)
{
case "DateTime":
val = ((DateTime)obj).ToString("yyyy-MM-dd");
break;
case "Money":
val = RegexExpand.Replace(val, @"\.\d+$", "");
break;
case "Decimal":
val = RegexExpand.Replace(val, @"\.\d+$", "");
break;
}
result = RegexExpand.Replace(result, RegexExpand.Replace(mc[i].Value, @"\^", "\\^"), val);
}
else
{
if (RegexExpand.IsMatch(result, @"(?<=<[^<>]*?)" + RegexExpand.Replace(mc[i].Value, @"\^", "\\^") + "(?=[^<>]*?>)", RegexOptions.IgnoreCase))
{
result = RegexExpand.Replace(result, @"<(\w+)(?!\w)[^<>]*?" + RegexExpand.Replace(mc[i].Value, @"\^", "\\^") + @"([^<>]*?/>|([\s\S](?!<\1(?!\w)))*?</\1(?!\w)[^<>]*?>)", "", RegexOptions.IgnoreCase);
}
else
{
result = RegexExpand.Replace(result, @"(?<=(^|>))[^>]*?" + RegexExpand.Replace(mc[i].Value, @"\^", "\\^") + @"[^<]*?(?=(<|$))", "");
}
}
}
#endregion
#region 限长字符串
else if (RegexExpand.IsMatch(arg, @"^\d+$"))
{
int len = 0;
int.TryParse(arg, out len);
if (len > 0)
{
val = StringExpand.ByteSubstring(RegexExpand.Replace(val, @"<[^<>]*?>|[\r\n\s\t]| ", ""), len);
}
result = RegexExpand.Replace(result, RegexExpand.Replace(mc[i].Value, @"\^", "\\^"), val);
}
#endregion
#region 尝试判断是否是链接格式
else if (Enum.TryParse<OutputLinkType>(arg, out olt))
{
lt = (LinkType)olt;
result = RegexExpand.Replace(result, RegexExpand.Replace(mc[i].Value, @"\^", "\\^"), MakeLink(row.ItemArray[row.Table.Columns.IndexOf(field)].ToString(), lt, true));
// MakeLink用来生成链接,自行设置
}
#endregion
#region 尝试反射到方法
else
{
#region 尝试反射成功则以反射处理
Type t = typeof(RefFun);
MethodInfo method = t.GetMethod("RefFun_" + RegexExpand.Replace(arg, @"^[a-z]+", RegexExpand.ToTitleCase, RegexOptions.IgnoreCase));
if (method != null)
{
try
{
ParameterInfo[] paras = method.GetParameters();
// 由于反射时,反射方法形参具有不同类型,所以根据形参类型预判断需要传递的实参值
if (paras[0].ParameterType == typeof(object))
{
result = RegexExpand.Replace(result, RegexExpand.Replace(mc[i].Value, @"\^", "\\^"), (string)method.Invoke(null, new object[] { obj, RegexExpand.Replace(para, @"^\.", "") }));
}
else
{
result = RegexExpand.Replace(result, RegexExpand.Replace(mc[i].Value, @"\^", "\\^"), (string)method.Invoke(null, new object[] { val, RegexExpand.Replace(para, @"^\.", "") }));
}
}
catch (Exception ex)
{
LogHelper.LogMessage(logType, ex, "RefFun_" + RegexExpand.Replace(arg, @"^[a-z]+", RegexExpand.ToTitleCase, RegexOptions.IgnoreCase) + " 值:" + val + ",异常:" + RegexExpand.Replace(para, @"^\.", ""));
}
}
#endregion
#region 当反射失败且未输出时使用最终处理
else
{
LogHelper.LogMessage(logType, mc[i].Value + " 无解析方法");
result = RegexExpand.Replace(result, RegexExpand.Replace(mc[i].Value, @"\^", "\\^"), row.ItemArray[row.Table.Columns.IndexOf(field)].ToString());
}
#endregion
}
#endregion
}
catch (Exception ex)
{
LogHelper.LogMessage(logType, ex, "字段:" + field + ",参数:" + arg + para);
}
}
}
return result;
}
}
这样,我们可以对html存在的$field$类型的内容进行变量解析了,如果要输出特定格式,就自己再RefFun类里追加自己的解析方法即可,只要是RefFun_开头,后边跟方法名,然后方法名首字母大写,都可以通过反射调用了
那么,现在我们来实现,怎么通过基类进行调用这个解析吧
在PageControlHelper里,追加一个方法
public static void BindHashtableParameters(Hashtable ht, Control ctls = null)
{
ControlCollection cc = ctls == null ? CurrentPage.Controls : ctls.Controls;
foreach (Control ctl in cc)
{
if (ctl.Controls.Count > 0)
{
BindHashtableParameters(ht, ctl);
}
if (ctl is RadioButtonList)
{
RadioButtonList rbl = (RadioButtonList)ctl;
for (int i = 0; i < rbl.Items.Count; i++)
{
ListItem li = rbl.Items[i];
li.Text = TemplateParser.Parse(li.Text, ht);
}
}
Type t = ctl.GetType();
string text = string.Empty;
PropertyInfo pi = t.GetProperty("InnerHtml");
if (pi == null)
{
pi = t.GetProperty("Text");
}
if (pi != null)
{
try
{
text = pi.GetValue(ctl, null).ToString();
}
catch
{
continue;
}
}
else
{
continue;
}
string parsed = TemplateParser.Parse(text, ht);
if (text != parsed)
{
try
{
if (pi.CanWrite)
{
pi.SetValue(ctl, parsed, null);
}
else
{
if (ctl is DataBoundLiteralControl)
{
if (ctl.Parent is RepeaterItem)
{
RepeaterItem ri = ctl.Parent as RepeaterItem;
ri.Controls.Clear();
LiteralControl lc = new LiteralControl(parsed);
ri.Controls.Add(lc);
}
else
{
HtmlGenericControl parent = ctl.Parent as HtmlGenericControl;
parent.InnerHtml = parsed;
}
}
}
}
catch (Exception ex)
{
throw ex;
}
}
}
}
然后,修改我们的基类
public Hashtable HT = new Hashtable();
protected override void OnLoad(EventArgs e)
{
base.OnLoad(e); // 在继承的页面里定义Hashtable的值
PageControlHelper.BindHashtableParameters(HT);
PageControlHelper.SetToControls(HT);
}
这样,我们就可以对所有继承了这个基类的页面直接输出变量了,支持runat="server"控件,Hmmmm,但这个不支持格式化输出,支持$field$格式变量,这个支持格式化输出,例如$content.40$就是输出40个字符的内容,$datetime.tostring.yyyy-MM-dd HH:mm$就是格式化输出日期和时间
好了,这就可以偷懒了,除了部分特定运算,基本不再修改cs文件就可以将内容输出出来了,把所有从数据库获得的数据直接扔到hashtable里,让他自动解析吧!
有人说了,比如我的数据库里,有一个地域信息,但他是数值型的,我输出可不能是数字,那么,就自己定义RefFun吧,$city.city.full$,变量定义成类似这样的格式即可,第一个city是字段名,第二个city是RefFun_City这个方法的方法名,full就是输出格式什么的了
多定义几个常用的输出方式,那么后期就基本不再需要考虑变量输出问题了,越来越偷懒了