Building a Real Time ProgressBar using ASP.NET Atlas

 

Building a Real Time ProgressBar using ASP.NET Atlas

Dflying | 27 March, 2006 23:44

That will be very cool and useful if you can show your user a ProgressBar on a web page which displays the actual progress of some long operations. Now let’s try to make it possible by using ASP.NET Atlas. This post can also show you some basic conceptions about extending Atlas client side controls. Also, the source code and demo can be downloaded here.

The basic ideas to implement this will be easy. Build an Atlas client side control and query a service to find how much we’ve done every tick. Then get the response and update the UI of progress bar. So in this demo, we separate the code into four parts:

  1. Web Service which processes a time consuming task.
  2. Web Service which is used to query the time consuming task and get the progress.
  3. Client side Atlas ProgressBar control which renders the UI and client side logic. This is the core component of the demo and can also be reused in other pages/projects without any changes.
  4. ASP.NET page contains the Atlas controls and Web Service references, which runs the application.

Let’s go through the four steps.

Time Consuming Web Service

In the real world, a time consuming function may runs like following:

 

[WebMethod]
public void TimeConsumingTask()
{
ConnectToDataBase();
GetSomeValueFromDataBase();
CopySomeFilesFromDisk();
GetARemoteFile();
}

Then we can insert some helpers to show how much the work has been done:

 

[WebMethod]
public void TimeConsumingTask()
{
setProgress(0);
ConnectToDataBase();
setProgress(10);
GetSomeValueFromDataBase();
setProgress(40);
CopySomeFilesFromDisk();
setProgress(50);
GetARemoteFile();
setProgress(100);
}

In this demo we just store the progress value in Cache and use Thread.Sleep() to delay the processing:

 

[WebMethod]
public int StartTimeConsumingTask()
{
string processKey = this.Context.Request.UserHostAddress;
string threadLockKey = "thread" + this.Context.Request.UserHostAddress;
object threadLock = this.Context.Cache[threadLockKey];
if (threadLock == null)
{
threadLock = new object();
this.Context.Cache[threadLockKey] = threadLock;
}
 
// Only allow 1 running task per user.
if (!Monitor.TryEnter(threadLock, 0))
return -1;
 
DateTime startTime = DateTime.Now;
 
// Simulate a time-consuming task.
for (int i = 1; i <= 100; i++)
{
// Update the progress for this task.
this.Context.Cache[processKey] = i;
Thread.Sleep(70);
}
 
Monitor.Exit(threadLock);
 
return (DateTime.Now - startTime).Seconds;
}

GetProgress Web Service

This should be easy.We just get the progress value from Cache:

 

[WebMethod]
public int GetProgress()
{
string processKey = this.Context.Request.UserHostAddress;
object progress = this.Context.Cache[processKey];
if (progress != null)
{
return (int)progress;
}
return 0;
}

Atlas ProgressBar control

Step 1: Derive from Sys.UI.Control

ProgressBar control should derive from the base Atlas control class, Sys.UI.Control and let’s make it a sealed class. The Sys.UI.Control base class contains some useful things, such as associating itself with an HTML element, which is the so called binding. Additionally, you should register your type to make it possible to instantiate it declaratively. Also register your class to let Atlas know it for further options, such as describing what the type is, etc.

 

Sys.UI.ProgressBar = function(associatedElement) {
Sys.UI.ProgressBar.initializeBase(this, [associatedElement]);
 
}
Type.registerSealedClass('Sys.UI.ProgressBar', Sys.UI.Control);
Sys.TypeDescriptor.addType('script','progressBar', Sys.UI.ProgressBar);

Step 2: Add private fields and the Setter/Getter

We have to add some configurable properties for our control. In this case, we have 3 properties:

  1. Interval. How often shall we call the service to update the progress?
  2. Service Url. Where is the Web Service file located?
  3. Service Method. Which method shall we call on this service to get the progress?

Properties have to follow an exact naming convention: the getter of the property should be a function prefixed with 'get_', and the setter should be prefixed with 'set_' and expect 1 parameter. Additionally, we should add these properties to our control's descriptor. Please see step 4 on how this should be done. For the service method property, we have following code:

 

var _serviceMethod;
 
this.get_serviceMethod = function() {
return _serviceMethod;
}
 
this.set_serviceMethod = function(value) {
_serviceMethod = value;
}

Step 3: Add a Timer to query the service on every tick

Here we include a Sys.Timer to query the service. Also define a delegate to represent the function that we want the timer to invoke on each tick. To get rid of the browser memory leak, we should make sure to finish the clean up when our control is disposing.
Still, notice that we prevent the control from querying the service multiple times when we are still waiting for a response.

 

var _timer = new Sys.Timer();
var _responsePending;
var _tickHandler;
var _obj = this;
 
this.initialize = function() {
Sys.UI.ProgressBar.callBaseMethod(this, 'initialize');
_tickHandler = Function.createDelegate(this, this._onTimerTick);
_timer.tick.add(_tickHandler);
this.set_progress(0);
}
 
this.dispose = function() {
if (_timer) {
_timer.tick.remove(_tickHandler);
_tickHandler = null;
_timer.dispose();
}
_timer = null;
associatedElement = null;
_obj = null;
 
Sys.UI.ProgressBar.callBaseMethod(this, 'dispose');
}
 
this._onTimerTick = function(sender, eventArgs) {
if (!_responsePending) {
_responsePending = true;
// Asynchronously call the service method.
Sys.Net.ServiceMethod.invoke(_serviceURL, _serviceMethod, null,
null, _onMethodComplete);
}
}
 
function _onMethodComplete(result) {
// Update the progress bar.
_obj.set_progress(result);
_responsePending = false;
}

Step 4: Add control methods

We should be able to start/stop our progress bar. And since this control is an Atlas object, we want it be known by the Atlas framework by describing its methods in the descriptor.

 

this.getDescriptor = function() {
var td = Sys.UI.ProgressBar.callBaseMethod(this, 'getDescriptor');
td.addProperty('interval', Number);
td.addProperty('progress', Number);
td.addProperty('serviceURL', String);
td.addProperty('serviceMethod', String);
td.addMethod('start');
td.addMethod('stop');
return td;
}
 
this.start = function() {
_timer.set_enabled(true);
}
 
this.stop = function() {
_timer.set_enabled(false);
}

Oh… till now, the control’s done! Save it as ProgressBar.js.

ASP.NET Testing Page

Of course, the first step of building every Atlas page is adding a ScriptManager server control. In this case we refer to our ProgressBar control, time consuming web service and GetProgress web service. (The two web services are located in one file: TaskService.asmx)

 

<atlas:ScriptManager ID="ScriptManager1" runat="server" >
<Scripts>
<atlas:ScriptReference Path="ScriptLibrary/ProgressBar.js" ScriptName="Custom" />
</Scripts>
<Services>
<atlas:ServiceReference Path="TaskService.asmx" />
</Services>
</atlas:ScriptManager>

Then styles and layouts:

 

<style type="text/css">
* {
font-family: tahoma;
}
.progressBarContainer {
border: 1px solid #000;
width: 500px;
height: 15px;
}
.progressBar {
background-color: green;
height: 15px;
width: 0px;
font-weight: bold;
}
</style>
<div>Task Progress</div>
<div class="progressBarContainer">
<div id="pb" class="progressBar"></div>
</div>
<input type="button" id="start" onclick="startTask();return false;"
value="Start the Time Consuming Task!" />
<div id="output" ></div>

At last is the JavaScript event handler which makes the ProgressBar control ran.

 

<script type="text/javascript" language="javascript">
function startTask()
{
// new ProgressBar
var pb = new Sys.UI.ProgressBar($('pb'));
pb.set_interval(500);
pb.set_serviceURL('TaskService.asmx');
pb.set_serviceMethod('GetProgress');
pb.initialize();
// start the task
TaskService.StartTimeConsumingTask(onTaskCompleted);
// start the ProgressBar
pb.start();
}
function onTaskCompleted(result)
{
// alert the time cost
if (result != -1)
$('output').innerHTML = 'Task completed in ' + result + ' seconds.';
}
</script>

Screen Shots and Download

Great, everything’s done now! Let’s run it!

Not started:

Running:

Completed:

Source code available here.

posted on 2007-08-14 10:07 virus 阅读(228) 评论(0)  编辑 收藏 网摘 所属分类: .net 2.0




发表评论

昵称: [登录] [注册]

主页:

邮箱:(仅博主可见)

评论内容:

  登录  注册

[使用Ctrl+Enter键快速提交评论]

0 854815




相关文章:

相关链接:
<2007年8月>
2930311234
567891011
12131415161718
19202122232425
2627282930311
2345678

导航

统计

公告

大家快乐就好,可不要因为工作累坏身体啊,身体是工作的本钱啊!
任何一个傻瓜都能写出计算机可以理解的程序,只有写出人类容易理解的程序才是优秀的程序员。
我的MSN:jorden008@hotmail.com

觉得这里的文章对你有用的话,帮忙点一下下面的广告,先谢谢了。

与我联系

搜索

 

常用链接

留言簿

我参加的小组

我参与的团队

我的标签

随笔分类(195)

随笔档案(216)

文章分类(37)

收藏夹(3)

.NET 2.0 Dispose

.NET 2.0 Socket

.NET File IO

.NET Office

.NET WinForm控件开发

.NET 加密解密

.NET 序列化

.NET 自定义异常

.NET安装部署

.NET技巧

.NET开源项目

.NET学习资源

《阿一web标准学堂》

ACM题库

ActiveX插件

AjaxPro

ASP.NET 2.0

ASP.NET 2.0 Cache

ASP.NET 2.0 GridView

ASP.NET 2.0的用户密码加密(注册登录)

ASP.NET 3.5

ASP.NET AJAX

ASP.NET LINQ

ASP.NET MVC

ASP.NET Navigation

ASP.NET Repeater

ASP.NET TreeView

ASP.NET ViewState

ASP.NET Webservice

ASP.NET 一般处理程序 ashx

c# 3.0

c# Dllimport c++

c# ORM

c# WndProc

c# 面向接口设计

c#+winform自动升级

c#操作数据库

c#插件编程

c#事件委托

Castle

eclipse社区

Enterprise Library

ExtJS

HtmlParse

InfoPath + Workflow

j2ee struts2

java se6帮助

java 相关

javascript

java社区

JQuery

MSN机器人开发

myeclipse6+hibernate

MySql

NHibernate

ObjectDataSource

OCS开发

OR-Mapping

Petshop 4.0

SharePoint Designer

SharePoint MVP

SharePoint2007

SharpDevelop

SQL Server 2005

TDD

VMWare

web.config

WEB控件开发

windows service

Windows Workflow

windows2003

WinForm DataGridView技巧

WMI编程

XML

常用正则表达式

创业信息

弹出窗口

飞信

分词算法

国内牛人

国外牛人

前端界面开发

权限系统设计思路

软件项目管理

设计模式

数据字典

系统定时运行,计划任务

下载好地方

友情链接

专业SVN托管服务

最新随笔

积分与排名

最新评论

阅读排行榜