[转载]jquery.simple.tree插件,更简单,兼容性更好的无限树插件!
[转载]jquery.simple.tree插件,更简单,兼容性更好的无限树插件! – 我是一只鸟,想飞飞不高! – 博客园.
在这里介绍一款小巧,功能强大,能拖拽,支持异步,且兼容性更高的jquery Tree插件:
效果如下:
选择:

拖拽:

jquery.simple.tree.官网地址: http://news.kg/wp-content/uploads/tree/(貌似已经打不开),不过因为操作比较简单,所以我们暂且用之。
前面讲过jquery EasyUI Tree插件,简单易用,但经过测试仍有诸多缺点,
例如:
1、兼容IE8的AJAX有问题。
2、如果异步返回数据较慢,将可能导致加载失败。
3、我们只使用其中的Tree功能,但其体积实在有点庞大。…
而我们需要的是,兼容性好,异步,体积小(用Tree的场景实在比较少,所以还是专用的代码文件比较好。)
好了,我们开始jquery.simple.tree之旅:
首先,要加载文件,一共三个:CSS、Jquery主文件、还有其本身的js文件;
然后,是定义Tree的代码;
最后,写出这根树的根节点HTML代码;
前台代码如下:
html代码
[转载]使用 Gmail 拨打国内电话,通话双方均免费的方法 – cnblogs版主 – 博客园.
Update: 这个方法现在已经不行了。现在美国和加拿大国内电话、短信全部免费。美国和中国间的国际长途费用为0.02美元/分钟。
我们知道使用 Gmail 给美国和加拿大拨打电话是完全免费的,现在 Google 则再一次震撼到了我们!只要使用 Gmail 拨打国内电话,通话双方均全部免费!
操作的步骤如下:
1、确保登录 Gmail 后可以看到在 Chat 列表中看到 “Call phone”按钮。这有2个方法:
其一、使用美国或加拿大 IP 地址登录 Gmail 肯定能看到该按钮;
其二、把 Gmail 语言设置成 US English ,也是有可能使用到呼叫功能的。
2、安装 Gmail voice and video chat 浏览器插件。
3、点击”Chat”列表中的”Call phone”按钮,拨打你自己的 Google Voice 号码,如:(111) 222-3333 。如果你还没有 Google Voice 号码的话,可以参考这篇文章注册一个。
4、听到 Google Voice 语音提示:“您当前没有新的消息,按“2”健拨打电话,按“4”健更改设置”。我们按“2”健。
5、再次听到 Google Voice 语音提示:“请按键输入您要拨打的电话号码,按“#”号健确认。如果是国际长途,则请先按键“011”,再输入国家号,最后输入电话号码”。
举2个例子:
如果我要拨打的中国移动手机号为:13866667777,则我应按键输入:0118613866667777#
如果我要拨打的国内固定电话号码为:021-77778888,则我应按键输入:011862177778888#(注:21为上海区号,大家不要输入021,这是错误的。)
6、再次听到 Google Vocie 语音提示:“这是一个免费电话!”,接下来电话就直接接通了呢。
这么大的好事,大家赶紧自己动手试试吧!
[转载]Asp.net中文件的压缩与解压
[转载]Asp.net中文件的压缩与解压 – 清风飘过 – 博客园.
这里笔者为大家介绍在ASP.NET中使用文件的压缩与解压。在ASP.NET中使用压缩给大家带来的好处是显而易见的,首先是减小了服务器端 文件存储的空间,其次下载时候下载的是压缩文件想必也会有效果吧,特别是比较大的文件。有的客户可能会很粗心上传的是文件,那么可以通过判断后缀名来判断 文件,不是压缩文件,就可以压缩文件,在存储。
这里笔者引用了一个DLL文件(ICSharpCode.SharpZipLib.dll)(包含在本文代码中),调用其中的函数,就可以对文件进行压缩及解压了。其中压缩笔者主要用到的函数是
1 /// <summary> 2 /// 压缩文件 3 /// </summary> 4 /// <param name="fileName">要压缩的所有文件(完全路径)</param> 5 /// <param name="name">压缩后文件路径</param> 6 /// <param name="Level">压缩级别</param> 7 public void ZipFileMain(string[] filenames, string name, int Level) 8 { 9 ZipOutputStream s = new ZipOutputStream(File.Create(name)); 10 Crc32 crc = new Crc32(); 11 //压缩级别 12 s.SetLevel(Level); // 0 - store only to 9 - means best compression 13 try 14 { 15 foreach (string file in filenames) 16 { 17 //打开压缩文件 18 FileStream fs = File.OpenRead(file); 19 20 byte[] buffer = new byte[fs.Length]; 21 fs.Read(buffer, 0, buffer.Length); 22 23 //建立压缩实体 24 ZipEntry entry = new ZipEntry(System.IO.Path.GetFileName(file)); 25 26 //时间 27 entry.DateTime = DateTime.Now; 28 29 // set Size and the crc, because the information 30 // about the size and crc should be stored in the header 31 // if it is not set it is automatically written in the footer. 32 // (in this case size == crc == -1 in the header) 33 // Some ZIP programs have problems with zip files that don't store 34 // the size and crc in the header. 35 //空间大小 36 entry.Size = fs.Length; 37 fs.Close(); 38 crc.Reset(); 39 crc.Update(buffer); 40 entry.Crc = crc.Value; 41 s.PutNextEntry(entry); 42 s.Write(buffer, 0, buffer.Length); 43 } 44 } 45 catch 46 { 47 throw; 48 } 49 finally 50 { 51 s.Finish(); 52 s.Close(); 53 } 54 55 }
解压缩的主要代码
1 /// <summary> 2 /// 解压文件 3 /// </summary> 4 /// <param name="ZipPath">被解压的文件路径</param> 5 /// <param name="Path">解压后文件的路径</param> 6 public void UnZip(string ZipPath,string Path) 7 { 8 ZipInputStream s = new ZipInputStream(File.OpenRead(ZipPath)); 9 10 ZipEntry theEntry; 11 try 12 { 13 while ((theEntry = s.GetNextEntry()) != null) 14 { 15 string fileName = System.IO.Path.GetFileName(theEntry.Name); 16 17 //生成解压目录 18 Directory.CreateDirectory(Path); 19 20 if (fileName != String.Empty) 21 { 22 //解压文件 23 FileStream streamWriter = File.Create(Path + fileName); 24 25 int size = 2048; 26 byte[] data = new byte[2048]; 27 while (true) 28 { 29 size = s.Read(data, 0, data.Length); 30 if (size > 0) 31 { 32 streamWriter.Write(data, 0, size); 33 } 34 else 35 { 36 37 streamWriter.Close(); 38 streamWriter.Dispose(); 39 break; 40 } 41 } 42 43 streamWriter.Close(); 44 streamWriter.Dispose(); 45 } 46 } 47 } 48 catch 49 { 50 throw; 51 } 52 finally 53 { 54 s.Close(); 55 s.Dispose(); 56 } 57 } 58 }
那么我这里做了个简单的测试程序(点击下载)

这里已知道要被压缩文件,这里只需填入要被压缩到的路径(”D:\text\”)解压路径一样。这里解压级别越大,压缩的就越厉害。
可以看测试小程序,将解压/压缩引入到你的项目中
好了,如果本文对你有作用请您留个言语,让笔者知道 ^_^
[转载]QQ窗口抓取及如何进行自动化操作
[转载]QQ窗口抓取及如何进行自动化操作 – wuhuacong(伍华聪)的专栏 – 博客园.
本文在文章开始,先介绍下窗口的抓取软件,Spy++及AccExplorer32.exe,前者是大名鼎鼎的微软出品,几乎可以抓取所有的Windows窗口及控件(其实也是一个窗口),另外一个也是类似,功能可以互补一下。
首先看看两者的界面,在抓取QQ的【查找联系人/群/企业】的窗口时候的情况:

Sp++的界面

AccExplorer32.exe界面
两者界面不同,不过基本功能有重叠,Spy++提供的功能可能会更多一些,这两个软件都可以对列出的窗口进行一系列的操作,如可以找到相关的窗口信息,窗口位置,以及对各种窗口操作的信息,包括鼠标、键盘等一系列的操作都可以模拟出来,非常强大。
为 了模拟抓取窗口以及对窗口的各种操作,我们可以通过FindWindow和FindWindowEx、SendMessage、PostMessage等 Windows消息来进行处理,便可实现基本的窗口、控件操作,另外按钮的操作,我们则可以模拟鼠标单击某个坐标点的方式实现按钮的单击操作模拟。 模拟的QQ界面窗口如下所示,是一个查找窗口的。

我们通过抓取窗口信息,我们看到该窗口下面只有两个可见窗口,分别对应两个输入控件,可以发现该窗口的一个输入(账号和昵称输入框中的任何一个)的Class 名称如下所示:

首先我们来创建一个界面,如下所示,用来模拟相关的操作。

我们在辅助类中定义几个函数用来实现窗口的操作
[DllImport("user32.dll")]
[DllImport(
"user32.dll", EntryPoint = "FindWindow")]private static extern IntPtr FindWindowWin32(string className, string windowName);
[DllImport(
"user32.dll ")]public static extern int GetClassName(IntPtr hWnd, [Out] StringBuilder className, int maxCount);
[DllImport(
"user32.dll")]private static extern int SendMessage(IntPtr window, int message, int wparam, int lparam);
[DllImport(
"user32", CharSet = CharSet.Auto)]private extern static int SendMessage(IntPtr hWnd, int wMsg, int wParam, string lpstring);
[DllImport(
"user32.dll")]private static extern int PostMessage(IntPtr window, int message, int wparam, int lparam);
在实际的按钮操作代码中,我们简化了具体的操作,只需要调用辅助类就可以了
private void btnSearch_Click(object sender, EventArgs e)
Win32Window win = Win32Window.FindWindow(null, this.txtWindowName.Text);
if (win != null)
{
ArrayList list = win.Children;
foreach (Win32Window sub in list)
{
if (sub.Visible && sub.ClassName == “ATL:30A4D1D8“)
{
sub.SendMessage(WindowMessage.WM_SETTEXT, 0, this.txtInput.Text);
}
}
}
int x = 288;
int y = 328;
win.ClickWindow(“left“, x, y, false);
}
其中位置信息时通过Spy++监控出来的信息。

最后结果如下所示,实现控件内容的修改,并执行了单击按钮的操作,弹出添加好友确认信息。

如果要进行进一步的操作,在继续对弹出的窗口进一步分析即可,其他所有的窗口操作,原理一样,这样就可以实现很多程序的自动模拟操作,是不是很方便呢。
[转载]从一个WEB请求说开去(三)
[转载]从一个WEB请求说开去(三) – 大河 – 博客园.
由于最近一段时间忙的我晕头转向,所以一直没时间继续我的WEB请求之旅,深感抱歉。
开篇之前,我必须向大家道个歉,由于我的知识水平有限,对ASP.NET没有一个系统性的认知,所以不对的地方请朋友们不吝赐教。说真的,我特别怕由于我的认知不足而误导大家,请大家一定要带着脑袋以批判的角度来阅读拙作。砖头准备好了吗?开始。
上文我们主要描述了iis6.0的两个核心组件WAS和Worker Progress,而本文重点讲述一下WEB请求在工作进程中都经历了什么,下面我把描述工作进程的图示放大,如下:

我们知道,当一个有效的WEB请求到达HTTP.SYS后,发现没有相应有效的工作进程,则HTTP.SYS会告诉WAS(svchost.exe),以 为之创建并启动一个工作进程(w3wp.exe)。在启动工作进程的过程中加载ISAPI(非托管代码)以及CLR(托管代码),然后在工作进程中创建应 用程序域。其实如上一系列操作只是为WEB请求准备好其运行环境。
当一个WEB请求进入已经准备好的工作进程边界(工作进程从HTTP.SYS的应用程序池对应的消息队列中取出该WEB请求),那此时首先会经过 ISAPI FILTER的处理,比如修改http报头和URL等,具体功能参见上文。接下来就会将处理的WEB请求交给ISAPI EXTENSION。那么ISAPI EXTENSION会干什么呢?我们先看下图:

我们从上图可以看出,它就是一个应用程序扩展名的映射,即文件扩展名与处理该文件的应用程序的一个映射表,接下来我们看看具有aspx扩展名的文件是由哪个应用程序集处理(dll)的,如下图:

看到了吗?其实也就告诉我们对于aspx扩展名的文件,要交给aspnet_isapi.dll处理,其实大部分文件都映射到了 aspnet_isapi.dll,也就是说大部分动态的web请求文件都交由程序集aspnet_isapi.dll来处理。对于静态文件比如jpg、 js、以及css等都会直接将文件返回给客户端。注意:aspnet_isapi.dll是一个非托管的Win32动态库,里面封装了很多用于与web进 行交互的函数,它的处理速度那是相当的快。另外它被加载到了工作进程中,也就是说非托管的aspnet_isapi.dll与托管的web应用都处于一个 工作进程中,这显然会加快其处理速度。跨进程访问是要付出代价的,所以我们不必为此担心。那么如何才能调用aspnet_isapi.dll提供的一系列 服务呢,这就引出了ISAPIRuntime,它的主要功能就是负责ISAPI扩展的非托管代码与托管代码之间的沟通与交互。您只要记住这点就行了,具体 实现细节我认为没必要深究。如果您对此感兴趣可以借助reflector.exe等工具进行跟踪研判。
我们的WEB请求走到到哪儿了,没错,它经过ISAPI顺利的进入到了ASP.NET通道接下来他会一直走啊走,经过HttpApplication、httphandles等经过处理后将其响应返回给http.sys,最终到达客户端浏览器。
本来关于IIS6.0还有很多说不完的话题,比如ASP.NET application的生命周期,以及web page的生命周期、以及如何自定义httpmodules,如何自定义httphandles等等,网上这方面的资料已经很多了,我也不再赘述。
其实写到此我只大概介绍了IIS6.0的三个核心组件HTTP.SYS、WAS以及Worker Progress,另外两个(Inetinfo.exe和IIS MetaBase)只是轻描淡写,如果有时间我会继续说说关于这两个核心组件的内容,另外会顺带说说SSL。
为了给大家一个关于WEB请求之旅的整体印象,我会在下一篇做个总结。如有疏漏之处还望朋友们多多海涵。
[转载]简单但有用的SQL脚本Part6:特殊需要的行转列
[转载]简单但有用的SQL脚本Part6:特殊需要的行转列 – 我帅故我在 – 博客园.
一、数据库SQL Server行转列
需求:原始表的数据的结构如图1所示,把相同的guid的code值转换为列值。

(图1)
目标:我们希望达到的效果如图2所示,这里的guid变成唯一的了,这行的记录中包含了这个guid所对应的code字段值。

(图2)
分析与实现:要实现图1到图2的转变,这就是所谓的行转列,下面我们来讲讲具体的实现:
1. 首先我们先创建一个测试表,方便后面的效果展现;
[转载]asp.net mvc 2.0+Silverlight播放器开发的TeamVideo视频播放网站–系列1 – 爱因斯坦的小脑 – 博客园.
背景:经常有一些电影大家想一起分享,为了照顾到很多同事,以及大家来交流评论电影,我最近抽时间使用ASP.NET MVC2.0和Silverlight播放器来做个视频播放网站。今天就来这里和大家分享下。
播放界面展示:

,,,,ASP.NET MVC这个我知道大家都写了好多,当然我这里不会重点去说明asp.net MVC了。我主要想和大家分享的是如何在asp.net mvc中使用Silverlight来播放不同的视频。通过不同的id来播放不同的视频。
我使用的Silverlight团队开发的Silverlight Media Framework 2.0来作为播放器的。
当然这里有提供了Smooth Streaming播放器,也就是视频直播的那种。结合Smooth Stream Client可以进行视频直播。参考文档:http://smf.codeplex.com/documentation
使用asp.net mvc来创建网站速度确实快。。。。。。我接下来就说明下如何在两天内抽空把整个网站给架好(很简单的网站,所以2,3天搞定)。
1.数据库模型的创建:
自己手绘了几下,思考着应该有哪些哪些字段和字段的类型,如上图。
a. Entity Data Model的创建:
数据模型有了,我们就来创建数据库,为了节省时间,我没使用POCO或是Code-First来创建Entity Data Model,直接使用的是系统自动生成edmx文件。
b.对Model的验证规则的创建:
没错,对于用户输入的数据我们需要来验证是否符合我们在数据库定义的长度和范围等,所以验证规则的定义是必要的。你当然可以使用jQuery的Validation来在客户验证。例如Video实体的验证如下:
| 1 | [MetadataType(typeof(VideoMetaData))] |
| 2 | public partial class Video |
| 3 | { |
| 4 | // Validation rules for Video class |
| 5 | |
| 6 | [Bind(Exclude = "Id")] |
| 7 | public class VideoMetaData |
| 8 | { |
| 9 | [ScaffoldColumn(false)] |
| 10 | public object Id { get; set; } |
| 11 | |
| 12 | [Required(ErrorMessage="An Title is required")] |
| 13 | [StringLength(255)] |
| 14 | public object Title { get; set; } |
| 15 | |
| 16 | [DisplayName("Video URL")] |
| 17 | [Required(ErrorMessage="Url is required")] |
| 18 | [StringLength(255)] |
| 19 | public object Url { get; set; } |
| 20 | |
| 21 | [DisplayName("Leading Actors")] |
| 22 | [Required(ErrorMessage="Leading Actors are required")] |
| 23 | [StringLength(100)] |
| 24 | public object Actors { get; set; } |
| 25 | |
| 26 | [Required(ErrorMessage="Review information is required")] |
| 27 | public object Review { get; set; } |
| 28 | |
| 29 | [Required(ErrorMessage="Year is required")] |
| 30 | [RegularExpression(@"^\d{4}$", ErrorMessage = "Year is not valid")] |
| 31 | public object Year { get; set; } |
| 32 | } |
| 33 | } |
需要注意的是这里有’Year’字段是个四位整数,所以我们需要用正则表达式来确定是否年份填写正确。
而在Rating实体的验证规则中需要注意Rating的范围1-5且只能是整数
| 1 | [Required(ErrorMessage="Rating is required")] |
| 2 | [Range(1,5,ErrorMessage="Rating must be between 1 and 5")] |
| 3 | public object Rating { get; set; } |
c.ViewModel部分的创建:
对于ViewModel的部分创建,主要是为了在controller对于view时能够更加容易的传递数据,所以我们定义了ViewModel部分。
例如在视频播放页面的布局如下:
所以我们需要给此页面的Model数据包含Video=>Comments=>Rating。它的ViewModel代码如下:
| 1 | public class VideoBrowseViewModel |
| 2 | { |
| 3 | public Video Video { get; set; } |
| 4 | |
| 5 | int Rating { get; set; } |
| 6 | |
| 7 | public List<Comment> Comments { get; set; } |
| 8 | } |
d.Controller部分的创建:
先贴上代码来看看:
| 1 | public ActionResult Index() |
| 2 | { |
| 3 | |
| 4 | // Retrieve Video from database |
| 5 | var VideoModel = mediaDB.Videos.Include(“Comments“).Include(“Ratings“).FirstOrDefault(); |
| 6 | |
| 7 | float sum = VideoModel.Ratings.Sum(r => r.Rating1); |
| 8 | |
| 9 | |
| 10 | var viewModel = new VideoBrowseViewModel() |
| 11 | { |
| 12 | Video = VideoModel, |
| 13 | rate=Convert.ToInt32(sum/VideoModel.Ratings.Count), |
| 14 | Comments = VideoModel.Comments.ToList() |
| 15 | }; |
| 16 | |
| 17 | return View(viewModel); |
| 18 | |
| 19 | } |
使用include属性来同时得到Video对应的Comments和Ratings。最后需要得到的rate是对于该video的所有rating的平均值,所以需要计算下来传递给view。
e.View界面的传值和Silverlight播放器的源文件选择
上面的传值传过来,在view界面直接绑定很简单,但是如何给Silverlight的播放器来传递数据源呢?
首先需要把xap文件包含在项目中,如下图:
接下来是使用JavaScript来为创建一个播放器的object:
| 1 | <script type=“text/JavaScript“> |
| 2 | $(document).ready(function () { |
| 3 | |
| 4 | // the media player |
| 5 | var obj = “<object data=’data:application/x-silverlight-2,’ type=’application/x-silverlight-2′ width=’100%’ height=’320′>\n“; |
| 6 | obj = obj + “<param name=’source’ value=’ProgressiveDownloadPlayer.xap’ /> \n “; |
| 7 | obj = obj + “<param name=’onError’ value=’onSilverlightError’ /> \n “; |
| 8 | obj = obj + “<param name=’background’ value=’#3E344A’ /> \n “; |
| 9 | obj = obj + “<param name=’minRuntimeVersion’ value=’4.0.50401.0′ /> \n “; |
| 10 | obj = obj + “<param name=’autoUpgrade’ value=’true’ /> \n “; |
| 11 | obj = obj + “<param name=’InitParams’ value=’mediaurl=<%=Model.Video.Url %>’ /> \n “; |
| 12 | obj = obj + “ <a href=’http://go.microsoft.com/fwlink/?LinkID=149156&v=4.0.50401.0′ style=’text-decoration:none’> <img src=’http://go.microsoft.com/fwlink/?LinkId=161376′ alt=’Get Microsoft Silverlight’ style=’border-style:none’/></a>“; |
| 13 | obj = obj + “</a>“; |
| 14 | |
| 15 | // set it as host control html ,and fade in slowly ^_^ |
| 16 | $(“#silverlightControlHost“).html(obj).slideDown(“slow“); |
| 17 | }); |
| 18 | </script> |
这里注意‘mediaurl的是通过我们来绑定Model.Video.Url来得到的。比如传过去的Video信息如下:
传过来值后,我们把这些拼好的字符串作为div的html来显示,就可以播放视频了。
在下面的文章中我会给大家介绍电影的List展示盒分类的展示,以及其它Rating的ajax实现和评论的ajax方法实现。
最后一篇文章中是结合TinyMac编辑器来进行后台的维护以及权限的管理等等。。。。。。。
Cheers
BRs
Nic
[转载]ASP.NET MVC表单的组件化
[转载]ASP.NET MVC表单的组件化 – 阿不 – 博客园.
ASP.NET WebForm最重要的特性之一就是它的界面元素的组件化,简单的输入控件就不必多说,特别是那些类似于Repeater,GridView这样的模板控 件,真的给开发人员带来了极大的方便。而在ASP.NET MVC的视图中,虽然技术上我们仍然可以使用这WebForm的Server Control,但是从理念上,我们是必须要完全避免这种情况的发生。很多习惯WebForm开发模式的开发人员,除了不习惯没有Postback外,可 能最大的抱怨就是MVC的表单开发方式。在大部分情况,他们需要自己完全去控件HTML标签。在显示数据列表时,需要通过foreach控制数据的输出, 当有一些特殊的输出控制时(比如奇偶行不同模板),还要做额外的工作,在界面上定义各种临时变量。这样重复的工作,除了会让开发人员烦躁不说,当一个表单 开发下来,充斥着if..else这样的逻辑判断,不规则的“{”“}”,也给我们阅读和日后的修改带来相当大的麻烦。本文的目的就是为解决这些问题提供 一些思路。
输入表单
对于输入表单的组件化,我们的解决思路来源于Mvccontrib,Mvccontrib是一个致力于改善和提高开发人员在使用ASP.NET MVC框架开发Web时的开发体验和开发效率的辅助框架。在里面有一个InputBuilder的功能,Mvccontrib首先根据不同的数据类型定义了一些常用的输入,输出模板。在开发人员在设计Model时,预先设置好一些必须的元数据供View使用,这样就可以提高HTML代码的复用性,更多细节请阅读:http://www.lostechies.com/blogs/hex/archive/2009/06/09/opinionated-input-builders-for-asp-net-mvc-using-partials-part-i.aspx。
这种通过在Model添加元数据,来支持View开发的模型在ASP.NET MVC2中得到了极大的应用。下面的代码就是MVC2项目模板的例子:
01 |
[PropertiesMustMatch("Password", "ConfirmPassword", ErrorMessage = "The password and confirmation password do not match.")] |
02 |
public class RegisterModel |
03 |
{ |
04 |
[Required] |
05 |
[DisplayName("User name")] |
06 |
public string UserName { get; set; } |
07 |
08 |
[Required] |
09 |
[DataType(DataType.EmailAddress)] |
10 |
[DisplayName("Email address")] |
11 |
public string Email { get; set; } |
12 |
13 |
[Required] |
14 |
[ValidatePasswordLength] |
15 |
[DataType(DataType.Password)] |
16 |
[DisplayName("Password")] |
17 |
public string Password { get; set; } |
18 |
19 |
[Required] |
20 |
[DataType(DataType.Password)] |
21 |
[DisplayName("Confirm password")] |
22 |
public string ConfirmPassword { get; set; } |
23 |
} |
View是这样的:
1 |
<div class="editor-label"> |
2 |
<%: Html.LabelFor(m => m.UserName) %> |
3 |
</div> |
4 |
<div class="editor-field"> |
5 |
<%: Html.TextBoxFor(m => m.UserName) %> |
6 |
<%: Html.ValidationMessageFor(m => m.UserName) %> |
7 |
</div> |
以上的LabelFor,就会从UserName这个属性的元数据中去得到[DisplayName("User name")],显示作为label。TextBoxFor会自动生成input标签,并且把UserName的值也赋给标签值。添加<%: Html.ValidationMessageFor(m => m.UserName) %> ,则会把数据验证消息输出到这里。我们会发现这样,虽然已经可以帮我节省了大量了时间。但是你会也发现,每一个字段都复制和拷贝这两个DIV的内容,这部 分也是一个相当重复和繁琐的工作。当我们把TextBoxFor替换成EditorFor,就会进一步发现原来每个字段都是这样的结构和内容,我们根本不 需要任何修改,那为何还要去复制呢?如果我们能直接使用EditorFor来代替上面的两个Div,根据不同的输入类型,定义不同的输入控件模板。于是, 我们的输入表单就变成这样:
01 |
<%Html.EnableClientValidation();%> |
02 |
<% using (this.Html.BeginForm()) |
03 |
{ %> |
04 |
<%: Html.EditorFor(m=>m.UserName)%> |
05 |
<%: Html.EditorFor(m=>m.Password) %> |
06 |
<%: Html.EditorFor(m=>m.Password)%> |
07 |
<%: Html.EditorFor(m=>m.ConfirmPassword) %> |
08 |
<%: Html.EditorFor(m=>m.Email) %> |
09 |
<input type="submit" value="Submit" /> |
10 |
<%} %> |
对于这样一个高度模式化的表单,一行一行去写代码也是相当的讨厌,特别是我可能必须要去检查一下有没有哪一个字段漏掉了。我们还可以进一步简化开发,写一个VS扩展,得到当前强类型模板所使用的Model类型,自动生成所有的字段模板,然后再根据需要手工去调整:
这篇博客就是为了写这个扩展时,得到当前上文Model类型实例而遇到的难题的记录。
列表表单
相对于输入表单,列表表单一般情况都是一行一行的输出数据。在WebForm中,我们可以使用Repeater,GridView这样的模板,给我们提供了非常大的便利。但是在MVC中,目前还没有一个非常好,非常方便的办法让我们方便快速去显示一个列表。在Mvccontrib中,给我们提供了一个强类型的Grid扩展,让我们可以以强类型的方式来输出table,但我并不喜欢那样的做法,要生成一个字段相对多点的列表,那个表达式写起来没有HTML标签来的轻松。
继承元数据和控件模板的做法,我把Model中,要显示在Grid的字段,都加上特定的元数据:
01 |
[GridAction(ActionName = "Delete", DisplayName = "Delete", RouteValueProperties = "UserName")] |
02 |
public class RegisterModel |
03 |
{ |
04 |
[GridColumn] |
05 |
[Required] |
06 |
[DisplayName("User name")] |
07 |
public string UserName { get; set; } |
08 |
09 |
[GridColumn] |
10 |
[Required] |
11 |
[DataType(DataType.EmailAddress)] |
12 |
[DisplayName("Email address")] |
13 |
public string Email { get; set; } |
14 |
15 |
[Required] |
16 |
[DataType(DataType.Password)] |
17 |
[DisplayName("Password")] |
18 |
public string Password { get; set; } |
19 |
20 |
[Required] |
21 |
[DataType(DataType.Password)] |
22 |
[DisplayName("Confirm password")] |
23 |
public string ConfirmPassword { get; set; } |
24 |
} |
以上加GridColumn的两个字段就是将来会被显示在grid的字段。同时Grid中的每一行都有一种操作,Delete。我们在列表中这样来写模板:
01 |
<%@ Page Title="" Language="C#" MasterPageFile="~/Views/Shared/Site.Master" Inherits="System.Web.Mvc.ViewPage<System.Collections.Generic.IEnumerable<MvcFormSample.Models.RegisterModel>>" %> |
02 |
03 |
<asp:Content ID="Content1" ContentPlaceHolderID="TitleContent" runat="server"> |
04 |
List |
05 |
</asp:Content> |
06 |
<asp:Content ID="Content2" ContentPlaceHolderID="MainContent" runat="server"> |
07 |
<h2> |
08 |
List</h2> |
09 |
<%: Html.GridForModel() %> |
10 |
</asp:Content> |
在Views\Share写一个默认的Grid模板:
01 |
<%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<Kooboo.Web.Mvc.Grid.GridModel>" %> |
02 |
<div class="table-container"> |
03 |
<table> |
04 |
<thead> |
05 |
<tr> |
06 |
<% foreach (var column in Model.GridColumns) |
07 |
{ %> |
08 |
<th> |
09 |
<%:column.GetFormattedHeaderText(ViewContext)%> |
10 |
</th> |
11 |
<%} %> |
12 |
<%if (Model.GridActions.Count() > 0) |
13 |
{ %> |
14 |
<th> |
15 |
<%:"Actions" %> |
16 |
</th> |
17 |
<%} %> |
18 |
</tr> |
19 |
</thead> |
20 |
<tbody> |
21 |
<% |
22 |
foreach (var item in Model.GridItems) |
23 |
{ |
24 |
%> |
25 |
<tr <% if(item.IsAlternatingItem) {%>class="alternatingItem" <%} %>> |
26 |
<% foreach (var itemValue in item.GetItemValues(ViewContext)) |
27 |
{%> |
28 |
<td> |
29 |
<%: itemValue %> |
30 |
</td> |
31 |
<%} %> |
32 |
<td> |
33 |
<% foreach (var action in item.GetItemActions(ViewContext)) |
34 |
{ |
35 |
if (action.Visible) |
36 |
{%> |
37 |
<%: Html.ActionLink(action.DisplayName, action.ActionName, action.RouteValues, new RouteValueDictionary(new |
38 |
{ |
39 |
onclick = string.IsNullOrEmpty(action.ConfirmMessage) ? "" : |
40 |
"JavaScript:return confirm('" + action.ConfirmMessage + "')" |
41 |
}))%> |
42 |
<% } |
43 |
} %> |
44 |
</td> |
45 |
</tr> |
46 |
<% |
47 |
} |
48 |
%> |
49 |
</tbody> |
50 |
</table> |
51 |
</div> |
通过以上的封装,我们就可以大大减少在写列表表格时的HTML复制。有时间,字段在列表中的显示并不是简单的把值显示出来,有可能还需要格式化等操作。这时,我们可以通过在GridColumnAttribute添加相应的设置来进行输出的控制。
总之,我们总是希望找到一种就为经济实惠并且可行的表单开发方式。以上的做法,View Model的元数据是基础。而很多时候这些与视图相关的元数据并不会在设计业务模型时被设计好,这篇博客就是针对这种情况扩展。
上文的例子请从这里下载。
[转载]ASP.NET MVC 之如何创建自定义路由约束
[转载]ASP.NET MVC 之如何创建自定义路由约束 – JasenKin – 博客园.
本文将讲解如何创建一个路由约束以及创建一个自定义路由约束
创建一个路由约束(C#)
你能够使用路由约束来限制匹配一个特殊路径的浏览器请求。你能够使用一个正则表达式来制定一个路由约束。
例如,假设你已经定义路由如下:
Listing 1 – Global.asax.cs
routes.MapRoute(
“Product”,
“Product/{productId}”,
new {controller=”Product”, action=”Details”}
);
Listing 1 包含一个命名为Product的路由. 你能够使用这个 Product route来将将浏览器请求映射到ProductController,如下:
Listing 2 – Controllers\ProductController.cs
using System.Web.MVC;
namespace MVCApplication1.Controllers
{
public class ProductController : Controller
{
public ActionResult Details(int productId)
{
return View();
}
}
}
注意:Details() action 接收一个命名为 productId的单一参数. 这个参数是整型参数.
在Listing 1 will中定义的route将匹配一下的任何一个URLs:
?/Product/23
?/Product/7
遗憾的,这个route也同样匹配以下的URLs:
?/Product/blah
?/Product/apple
因为Details() action预期接收一个整型的参数,当请求中包含的内容不同于整数时,它将导致一个错误。
你真正想要做的,仅仅是匹配包含一个的整数productId的URLs。当你定义一个route时,你能够使用一个限制条件来限制URLs,使它匹配这个route。在Listing 3中,这个route包含一个只匹配整数的正则表达式约束。
Listing 3 – Global.asax.cs
routes.MapRoute(
“Product”,
“Product/{productId}”,
new {controller=”Product”, action=”Details”},
new {productId = @”\d+” }
);
这个真正表达式约束\d+ 匹配一个或多个整数. 这个约束导致Product route匹配如下的URLs:
?/Product/3
?/Product/8999
但不是如下的URLs:
?/Product/apple
?/Product
这个浏览器请求将被另一个route处理。或者,如果没有匹配的routes, “The resource could not be found ”错误将被返回.
创建一个自定义路由约束 (C#)
演示如何创建一个自定义的路由约束.约束接口中的Match方法如下:
IRouteConstraint.Match Method
bool Match(
HttpContextBase httpContext,
Route route,
string parameterName,
RouteValueDictionary values,
RouteDirection routeDirection
)
你可以通过实现IRouteConstraint接口来创建一个路径约束,并且通过几个步骤把它添加到你的路径中。IRouteConstraint仅有一个Match方法,它返回一个布尔值。这个布尔值决定该请求是否应该被route对象处理。
如何创建一个ASP.NET MVC应用程序来模拟一个仅仅在视图中显示年份,月份,日期的文章系统,类似于博客系统的路径?
(一)首先,创建一个ArchiveController,它包含一个仅仅显示年份,月份,日期值的Index action 方法。
[转载]社交游戏(Social Game)开发简单教程与粗略总结之基础篇 – Minds book of my CS Road – 博客园.
为了使得整个系列文章的逻辑性更加严密,本文旨在介绍一些社交游戏的相关基础,当前的市场状况,发展情况,以及对未来的展望。当然文中的一些数字与表述可能不太准确,在不影响传达数字表意的前提下,读者可对数字做一些上下浮动的估量,或许会更准确些
。
引入
当无数人在谈论微博时代的到来和中国SNS去泡沫化之时,依附于SNS平台之上的社交游戏也开始展露出头角,在互联网的大蛋糕上寻找着自己的那块cheese,而随着年利润达若干亿美元的zynga高调地宣称自己的玩家过亿时,大家才真正开始注意起这个不起眼的蓝海来,当然如今已是大红海了(下文会详细的说明)。
当facebook定义了SNS时,zynga也定义了社交游戏,随后便是数不尽的跟随者,如myspace,orkut,开心网,如 playdom,6waves,五分钟等,当然也只有facebook和zynga赚的盆满钵满,跟随只能有残羹剩饭的份,但是那已是相当诱人的了。
与其它游戏形式的关系
那么社交游戏又是一种怎样的游戏类型,它与传统游戏、网络游戏、网页游戏有什么不同,下面对这几个问题加以分析。
社交游戏,即social game,从组词来看它由soical和game组成,game决定了其只是游戏的一个类别而已,与其它游戏类别也并无大的差别;而关键是social, 也即说明社交游戏是基于社会关系之上的一种游戏新类型,其建立在依附的SNS平台庞大的用户群之上,在具有某种社会关系的玩家之间,基于一定的社交元素来 保证与这些玩家之间的互动,最终将游戏的趣味娱乐性本质延展至社交层次。所以说,除了需要具有游戏本身的趣味性、娱乐性等基本元素外,将用户的游戏行为导向为社交行为则是一款社交游戏能否成功的根本因素,例如风靡的“偷菜”,许多玩家偷的已不是菜,而是一种社交目的,如暧昧,亲昵,友谊等。
当然你也许会说,网游也具有同样互动的因素呀,且不闻借某款游戏来赢得一辈子幸福的同学也不在少数哈,还有大量的公会,还有“贾君鹏,你妈喊你吃饭”,这 不是社交吗?我不否认这种网游建立起的关系的存在和普遍性,但相比于建立在已有大量关系基础上的社交游戏,网游的社交性显得少了许多。试想,你去玩偷菜 时,你的邻居列表都是自己所熟知的人,其中不乏自己喜欢或者厌恶的人,你不是更有欲望去他们地里做些什么动作吗?当你去偷菜时只是为了简单的得到经验和升 级吗?显然,网游这方面具有先天的单薄性。总之,网游的社交性相比于社交游戏更显松弛和单薄。
同样,有很多同学或许也称开心网上的游戏为网页游戏,所以有必要区分下这两个概念。从词的范畴来说,网页游戏与社交游戏定义的角度不同,前者是基于游戏的呈现载体,而后者是基于游戏的功能目的,其从属关系可从下图简单说明。
其中相交的部分即是定义的角度相同时二者的重合关系,换句话说,以社交为目的的网页游戏即是社交游戏,而以网页形式为呈现载体的社交游戏也是网页游戏,当然,网页游戏也可只是单机版,而同样社交游戏也可出现在ipad,ps等载体上。
作为本小节的结束语,社交游戏从传统游戏的追求游戏的乐趣与娱乐的基础上,延展至追求一种实际社会关系的某种影响上,而马斯洛的五种需求层次之中的“社交需求”也正解释了为什么社交游戏会得到强劲的发展的原因。看图不说话。
市场状况
市场容量的分析通常由第三方的咨询公司来给出,这里引用相关的新闻(或者“所谓的”新闻)来给个直观的感受:
- 美国社交游戏的市场在2012年超过20亿美元
- zynga今年收入将达8.5亿美元,明年将达10亿美元
- 日本社交游戏市场规模将达10亿美元
- 据艾瑞统计,2008年中国休闲类社交网络市场规模为1.9亿元,较2007年的1.2亿元增长64.0%。2009年达到3.7亿元,并在2012年达到16.1亿元。这个增幅是每一个淘金者都会看红眼的。
除此而外,我们可以看看下面艾瑞关于社交游戏市场规模的一些预测,虽然各个版本的数字有一定的差异,但总体趋势是相同的。
本图引用自http://www.iresearch.com.cn/View/121267.html
而在CNNIC的2010年中国网页游戏调查报告有如下一段研究发现:
- 中国网页游戏用户规模达1.05 亿,其中社交类网页游戏用户规模最大,达到9209
,大型网页游戏用户规模2384万,网页单机游戏用户规模3791 万。- 19岁到 30 岁用户构成网页游戏最大用户群体,比例为64.5%。
- 网页游戏用户的有收入人群比例较大,为77.1%, 其中平均月收入在1000元到 3000
的用户比例最大,占到总体用户的43.7%。- 朋友介绍与搜索引擎是网页游戏信息获取的主要渠道,比例均为71.5%。
推荐朋友可以阅读下CNNIC的报告。
营利模式
社交游戏在营利上也采取与当下免费网游同样的策略,即游戏本身免费,而以出售道具或者增值产品来获得收入,当然随着游戏本身的发展相应的营利模式也在不断拓展,例如植入广告,周边产品的开发。具体可用下表来总结:
| 营利方式 | 典型示例 | 备注 |
| 道具出售 | 开心农场的道具 | 如种子等 |
| 植入广告 | tikiisland中的可口可乐广告 | 还有开心网的相关广告植入等。 |
| 周边产品 | 泡泡鱼的淘宝店 | 不过似乎销售不是特别理想。 |
当然,社交游戏的社交性决定其必须依附于具有大量用户的SNS网络,如国外的facebook,myspace,国内的开心网,人人网等,平台商自不会看 着你独自享用这样的赢利机会,于是人人网,facebook等平台相继收取30%左右分成来压榨开发商的利润空间,这也是之前不久zynga与facebook之间以生掐架的原因。
除了平台商的分成外,如果开发商使用第三方的支付系统,仍需向其提供10%左右不等的分成。
当然上面是说开发商自己开发,自己运营所涉及到的额外支出,如果开发商是通过第三方的发布商来在某个平台上进行发布(原因是开发商的人力和财力所限,或者 发布商与平台商的关系等),那么发布商通常要分去50%左右的分成(如plinga在VZ上发布游戏),所以到头来能够回到游戏开发商的收入已是少之又少 了。
市场发展情况
正如文首说所的,社交游戏市场已经从蓝海变成了血腥的红海,而随着EA对playfish,Disney对playdom以及zynga对若干公司的收 购,整个社交游戏市场已经开始了新一轮的格局调整,在此过程中竞争力差的公司会相继出局,遗留的只能是那些有大量资源(流量,高质量产品)的开发商、发布 商等。
在如今的市场环境下,大量的开发商不再试图去创新,而是所谓的“山寨”(当然我们公司也逃不出),zynga出了款挖宝的游戏,国内的厂商开始也开 发;playdom出了款城市建造的游戏,国内连忙跟进,于是市场上充斥了大量的同类型的城市类游戏。更为可怕的是,其它领域在市场格局调整时的惨剧也同 样在社交游戏上上演——恶性的竞争与拷贝,这点在社交游戏上更为突出。从技术角度来讲,社交游戏前端通常采用flash,而拥有网络的任一个人都可将其下 载,使用反编译工具查看相关的源码,如果下载反编译的是一个公司,那么后果可想而知,原厂家用6个月心血开发而来的游戏,你(或者某个厂家)只用几周调通 前端写个后端即可,当然技术的话题可留作本系列文章的技术篇中细说。
在市场不断成熟的条件下,开发商的日子并不好过,特别是话语权低的小开发商,你不断要受到平台商的无理压榨,甚至随时删除你的游戏,也要小心同行的恶意拷贝,在受尽压榨的处境下充满中成为另一个中国zynga的美好梦想,当然或许仅是梦想而已。
未来的展望
一次去中关村买移动硬盘时,发现了一个有趣的事情,我循着海龙各个商贩的展台过时,看到的是各个店员“忙时则招揽生意,闲时则偷偷菜”,甚至我询问生意的声音也没能把沉迷的店员从泡泡龙中拉出来。
市场仍在向好发展,社会对社交游戏的认可也越来越高,对于购买道具也越来越习惯和接受(应该感谢QQ的市场培育),而且购买渠道也十分畅通(各个第三方的支付系统),相信社交游戏市场在剧烈的调整下正在逐步走向成熟和稳定,并且会构成游戏中的一个重要的分支。
当然,未来社交游戏的属性也会有一定的调整,除了更少的急功近利和恶性竞争,还需要建立起一定的行业标准,以及协调与各个利益方的关系,这样整个社交游戏产业才能更好的、保持良性的发展。
最后说一个小故事,说,某一天,大家都在公司上班,为成为中国的zynga在努力,突然一个同事震惊地要我们去看个东西,原来是在人人上看到了一款与 social city完全一样的游戏(叫A游戏吧),美工UI等完全相同(显然是直接拿人家的),不同的只是语言和开发商署名,我们在好奇版权问题时,这时一个同事冒 出个句,“人人网是山寨,A游戏也是山寨,山寨上的山寨,山寨中的山寨,严格的审核不是自己抽自己?”于是我们大呼经典继而继续努力。
最后附一张世界sns的布局图,当然或许太过粗略,也不完全符合实际,但是欣欣向荣的形势还是看得见的。
本图摘自http://www.mapsofworld.com/images/world-social-network-map.jpg
