玩转C科技.NET

从学会做人开始认识这个世界!http://volnet.github.io

导航

统计

公告

我们究竟是否有在“Asp.net中模仿Winform的MessageBox ”的必要?

前几天Bēniaǒ同学MSN询问我关于confirm在Web中的用法,据说,Bēniaǒ长期做WinForm程序,不太熟悉Web上类似WinForm上的MessageBox.Show方法,当时很困,也不是太理解Bēniaǒ同学要表达什么意思,就决定写一个小示例给Bēniaǒ同学直接看看是不是,后来Bēniaǒ同学去睡觉了,留下我一个人奋战~。Bēniaǒ同学刚下线不久我就搞完了,把核心代码直接用MSN给他发过去了,后来Bēniaǒ同学写了一篇《由var js = confirm("确认操作?");引发的技术难题》,其中感谢了我,矣,只可惜依Bēniaǒ同学所述,好像没有收到我的留言。

当时有跟Bēniaǒ同学说了一下思路,不知道他是不是理解了。不过今天看了Bēniaǒ同学文章的观点,又看了他对别的同学的留言的回复,我也说不清楚是Bēniaǒ同学没理解我的思路还是我没有理解Bēniaǒ同学的思路……(汗死)

其实Bēniaǒ同学问我的时候就跟我讲他打算发篇文章问问,Bēniaǒ同学发这篇《由var js = confirm("确认操作?");引发的技术难题》的时候,我估计还在睡觉,名字也太隐秘了,我也没有在意,直到下午代码乱了同学发了一篇《Asp.net中模仿Winform的MessageBox》,其中提到了Bēniaǒ同学的问题,我才想到Bēniaǒ同学那天问过同样的问题。

经过仔细考量两位同学的代码和文字后(代码乱了同学的文章算是很容易理解的,Bēniaǒ同学的语言太深奥,有点绕,不是太理解~不过看了各种留言后,也了解了大概),觉得二位同学都很执着和努力,向二位同学的努力表示敬意。但是有一点没有想明白,很多留言的朋友也没有想太明白,就是“为什么非要在Web上实现Windows.Forms中的MessageBox.Show()方法呢?”。

为什么非要在Web上实现Windows.Forms中的MessageBox.Show()方法呢?

经过一些简单的分析,发现可能有这么几点理由:

1、认为MessageBox.Show的这种使用方式是一种约定俗成的必然。Web的发展相对于WinForm的发展来说,还是很新的,大家在写第一个VB/C++/C#/Java的代码的时候,很可能就是用一个MessageBox.Show方式来写HelloWorld的,就算后来JavaScript代码盛行的时候,最常写的也是alert(msg);由于这些展示对话框的方法简单直接,很容易被人感性地理解为是一个“原子方法”,它也就自然被人们认为是一种必然。

2、认为Web是WinForm的一种延伸。ASP.NET和很多的Web技术不同,它秉承了微软一贯的观点,就是入门容易。我们知道微软是怎么降低这个门槛的。之前大部分程序员都在做Windows程序,Windows程序的使用编写方式都被公认为一种优秀的实践,而微软期待将这种实现移植到Web上,正如微软也封装足够多的代码使一大堆的Ajax脚本变成了对程序员几乎透明的方式。这种价值观和运作方式固然带来了革命,我们可以说,没有这样的包装,没有ASP.NET,Web至今仍然不够普及或者无法胜任更多更复杂的应用。但也由此带给了程序员依赖性。就我所见,很多程序员甚至不能区分WebForm和WinForm的本质区别。这也不能怪他们,Visual Studio作为几乎是最优秀的一款IDE,让程序员省去了不少粗枝大叶,再加上微软团队的辛勤和智慧,让编程也变得普及起来,但是门槛的降低并不能解决许多高端的问题,作为一名资深的程序员,我们不可能要求自己停留在拖控件的水平。这也是很多人常说的微软程序员贬值的原因,但是观察那一堆设计的成果,所有的方便不是因为编程本身变成一件很简单的事,而是因为微软为我们思考了更多,这是很多其他软件公司和团队做不到的。也就是这种精益求精的精神,让这种包装变成了透明状。(好像跑题了)因此很多程序员认为Web应该和WinForm一样,应该用同样的编程方式去实现。但是这里面涉及到了很多复杂的东西,可能从设计上来讲是不美观的,从实现上来讲是低效的。因此如果你认为为什么这么简单的功能微软都不为你做好的时候,你就要想清楚,到底是不是自己应该换一个思路了。因为就我个人觉得,那个号称拥有全世界最精良大脑的团队,肯定不会连我都能想到的东西给遗漏了。所以80%以上的几率是我自己本身出了问题(当然了,按照2:8原理,有些东西我们应该自己做)。

现在回到那个问题,我们该如何去完成这个“确认执行”的操作呢?

从本质上讲,Web是客户从浏览器发送HTTP请求给Web服务器,然后Web服务器返回指定的页面给浏览器,然后断开连接!注意最后一步是断开连接。在PostBack模型中,我们将一些数据或者变量,存放到页面的一些变量中,文档被整体发送给了服务器,服务器根据得到的数据,存取那些他们认识的变量,看看这些变量是否发生了变化,如果发生了变化就执行相应的更新,将这些更新整合成新的Web页面,然后再将这些内容发送回客户端,如此反复,这也就是为什么有ViewState的原因。(请参考ViewState的相关文档了解具体的细节)

有的同学可能要问了,那Ajax不会刷新,那是不是就不是这样了呢?这个问题要回到Ajax的本质上来,Ajax通过一个异步的机制能够向服务器请求数据,这个机制通常是由XMLHttpRequest对象来实现的,这个对象最早由Microsoft Internet Explorer实现,但是那个时候用的人很少,总之还没有形成规模也没有Ajax这个词。它的存在完全只是因为微软要在Microsoft Outlook中使用该组件,但是诸多原因,现在它红透了,导致很多人又被扑面而来的一大堆概念弄昏了头脑。从本质上,我们还是发送了一个HTTP请求,然后对回复的文本进行了处理。但可能因为Ajax涉及到了比较多的JavaScript,因此很多人反倒弄清了一些WebForm上的概念,但有些人可能越来越糊涂了。

那么对一个静态HTML无交互的页面,我们通常怎么做呢?(保存这段代码到<filename>.html文件,打开即可)

静态HTML页面confirm

是的,这几乎是Web中最简单且唯一的处理这种“确认执行”的形式了。它利用了JavaScript中的window.confirm方法,弹出阻塞浏览器进程的对话框以实现征求用户同意的方法。这种方式相信大家一定都耳熟能详了。

下面我们将它移植到ASP.NET中,似乎这种说法并不准确,按照Bēniaǒ同学的说法,就是要在后台代码(CodeBehind)中编写上述逻辑。以实现确认以及取消的操作。Bēniaǒ同学的思路核心是像在WinForm中所惯用的,也就是从MessageBox.Show的返回值中得知用户的确认情况,究竟是前进还是后退。但是这一点Bēniaǒ同学最后认为不可能,好像是无法解决的,但其实要把是否确认的信息返回服务端是完全可能的,随便找一个隐藏域就可以把该数据传回服务端了。但这里只解决了将“确认状态”传回的功能,但是却没有完成后续逻辑的执行。

论:在一次回发中不可能完成Winform中的相似代码!

什么是Winform中使用MessageBox.Show的相似代码?这些代码是什么?下面的代码片段展示了基本的MessageBox.Show的代码:

DialogResult result = MessageBox.Show("Are you sure?""Warning", MessageBoxButtons.YesNo, MessageBoxIcon.Information);
if (result == DialogResult.Yes)
{
    
//TODO
}

else
{
    
//TODO
}

相信不需要任何解释大家就可以看明白这段代码,现在我们将它们放到一个WebForm的按钮中(假设在VS中我们在页面拖放一个Button,双击Button在相应的Button1_Click方法内插入以上代码)。

这个时候我们需要确定我们的MessageBox.Show方法应该具备哪些输入输出,可以看出,我们需要传入一些提示信息,返回用户的选择(通常是“是”/“否”)。因此,我们在代码第一行中则需要“等待用户输入”,而我们知道我们在Web中,这种等待是不切实际的,假设有一种等待机制的话,那么假设我们的用户在弹出框后关闭浏览器进程了(这里都不想说离开去吃饭了),那么结果就是这个等待将会无限期地等待下去,当然有的同学会说可以设置过期时间,但是就服务器有限的连接数一条原则也不允许我们这么做。这个方法必须枪毙,相信大家也都会认同。

在上述代码中TODO中的内容则为实现逻辑,我们按下按钮后执行该方法(Button1_Click),要么是,要么否,但是既然客户的确认信息无法传回来,我们是不能臆断客户的选择。所以我们这个判断将无法执行下去。

深入点讲,当我们按下一个按钮的时候,我们必然引起一次回发,回发表明我们应该执行该方法(Button1_Click),该方法则要求客户端告诉我们用户的选择是什么。这必然导致用户的选择信息需要再次传到服务端,这样我们综合这两个信息,才能完成这个判断。但是这时候就产生了一个死锁。用户在等服务端,服务端在等用户。

可能有同学说了,代码乱了同学的那篇《Asp.net中模仿Winform的MessageBox》中,就是这么做的,确实可以啊!没错,但是我们需要看清楚,代码乱了同学用了两次回发的方式。可能你执行了代码乱了同学提供的代码后并看不出回发,但是你要注意到他采用了一个UpdatePanel,也就是它的回发对你隐藏了而已。两次回发必然带来性能的严重损耗,当然你非得说这个严重程度强烈到跑不动那也不至于,我只是提醒您一下,那个方法有一点固然的BUG。即便你不在乎Ajax下的性能损耗,你也应该去掉Ajax UpdatePanel看看提示效果。在你看到确认框的时候页面已经回发了,你看到的是空白的页面。对于示例可能你看不出什么问题,假想一下博客园的首页一个按钮点击后一片空白会是什么场景。也就是它受限于Ajax UpdatePanel(或者类似的机制)。另外我还是需要强调一下性能,为什么我们用Ajax?因为我们期待更好的用户体验和性能提升,然而很多朋友只在乎用户体验却不在乎性能,这将成为一种隐患,也是一个不好的习惯,程序员虽然不应该吹毛求疵,但也要有基本的精益求精的精神嘛。

既然我们需要在一次回发中把这些都搞定,那我们该怎么做呢?

其实针对这个问题,代码乱了同学的代码看上去确实很酷,只可惜多了一次回发,而且在无UpdatePanel时的效果欠佳。简单的分析过后发现这几乎是个不可能改进的缺陷。我们只能寻找固有的方式去完成这个任务。

这个任务既有它的局限性,我们是否真的应该用WinForm的那种方式来实现呢?

其实代码乱了同学的代码稍微改一下就可以避免二次回发的毛病了,不过很可惜的是,那样的修改同样需要改动调用方式,结果就不是那么美了。

下面是我提供的两个简单的思路,希望能够抛砖引玉,我的目的绝不是在宣扬这样两种或者其他类似的方式,我旨在告诫人们Web不是WinForm,不同的东西为什么总要扯到一起呢?

思路1:

既然我们点击按钮,那么我们在页面中通过confrim必然产生一个true/false,纯脚本的方式,我们对true执行一段代码,对false的时候也执行一段代码。而现在,我们对true的时候放行,它将按照正常模式回到服务端执行Button1_Click中的代码,对于false的时候,如果我们允许执行代码,为了避免控件事件验证的诸多麻烦,我们直接向页面添加一个隐藏的按钮,将我们的执行逻辑分配给这个隐藏按钮,通过得到该隐藏按钮再模拟点击,即可完成任务。

思路1调用代码

 

思路1核心代码

思路2:

既然我们有了confirm,那么confirm必然有结果。既然我们的点击势必要回发,我们就可以将我们confirm的结果存在页面上,然后在回发到服务端后找回这个confirm的结果,然后执行指定的逻辑。

思路2调用代码

 

思路2核心代码

 

这两个思路都很淳朴和原始,所以不会引来多次回发这样的性能问题,而且不同于WinForm的调用方式,旨在告诉WinForm用户Web与WinForm不是相同的。

 

软件下载

# Non-members may check out a read-only working copy anonymously over HTTP.
svn checkout http://v-labs.googlecode.com/svn/trunk/ v-labs-read-only
download http://code.google.com/p/v-labs/downloads/list

点此下载:

http://v-labs.googlecode.com/files/WebAppConfirmControl0.1.zip

 

posted on 2008-10-26 02:23 volnet(可以叫我大V) 阅读(...) 评论(...) 编辑 收藏

使用Live Messenger联系我
关闭