2004年02月11日

其实用C#可以完成其他程序设计语言的几乎全部功能,当然C#自身的许多独到的功能,是其他程序语言所无法实现的,这就是C#越来越受到广大程序员的喜欢的原因。本文就来探讨一下用C#去实现一个重要的功能,用他来编写一个捕获当前屏幕地程序。通过这个程序,我们将了解到C#是如何调用API函数,和.Net框架中的类库内容是多么地丰富,功能是多么地强大。


一. 程序设计开发及运行环境:


(1).微软视窗2000服务器版


(2)..Net FrameWork SDK Beta 2


二. 程序设计的关键步骤:


要想完成这个功能,首先要了解一下在C#中如何调用API(应用程序接口)函数。虽然在.Net框架中已经提供了许多类库,这些类库的功能也十分强大,但对于一些Windows底层编程来说,还是要通过调用这些API函数才可以实现。所有API都在”Kernel”、”User “和”GDI”三个库中得以运行:其中”Kernel”,他的库名为 “KERNEL32.DLL”, 他主要用于产生与操作系统之间的关联,譬如:程序加载,上下文选择,文件输入输出,内存管理等等。”User “这个类库在Win32中名叫 “USER32.DLL”。 它允许管理全部的用户接口。譬如:窗口 、菜单 、对话框 、图标等等。”GDI”(图象设备接口),它在Win32中的库名为:”GDI32.dll”,它是图形输出库。使用GDI Windows”画”出窗口、菜单以及对话框等;它能创建图形输出;它也能保存图形文件。由于本文所涉及到是图象问题,所有调用的类库是”GDI32.dll”。在本文程序中我们使用的API函数是”BitBlt”,这个函数对于广大程序员来说,一定不感觉到陌生,因为在图象处理方面他的用途是相对广的,在用其他程序语言编程中,时常也要和他打交道。在.Net FrameWork SDK中有一个名字空间”System.Runtime.InteropServices”,此名字空间提供了一系列的类来访问COM对象,和调用本地的API函数。下面是在C#中声明此函数:


[ System.Runtime.InteropServices.DllImportAttribute ( "gdi32.dll" ) ]
private static extern bool BitBlt (
IntPtr hdcDest , // 目标 DC的句柄
int nXDest ,
int nYDest ,
int nWidth ,
int nHeight ,
IntPtr hdcSrc , // 源DC的句柄
int nXSrc ,
int nYSrc ,
System.Int32 dwRop // 光栅的处理数值
) ;



通过上面这个声明,就可以在下面的代码中使用此函数了。


下面是用C#做屏幕捕获程序的具体实现步骤:


(1).首先要获得当前屏幕的graphic对象,通过以下代码可以实现:


Graphics g1 = this.CreateGraphics ( ) ;



(2).创建一个Bitmap对象,并且这个Bitmap对象的大小是当前屏幕:


首先要获得当前屏幕的大小,通过名字空间”System.Windows.Forms”中的”Screen”类的GetWorkingArea()方法,可以实现。下面是得到当前屏幕的长(Height)和宽(Width):


Rectangle rect = new Rectangle ( ) ;
rect = Screen.GetWorkingArea ( this ) ;
“屏幕宽”= rect.Width ;
“屏幕长”= rect.Height ;



至此就可以得到我们想要的Bitmap了,通过下列语句可以实现:


Image MyImage = new Bitmap ( rect.Width , rect.Height , g1 ) ;
//创建以屏幕大小为标准的位图



(3).获得当前屏幕和此Bitmap对象的DC,这可以通过下列语句实现:


//得到屏幕的DC
IntPtr dc1 = g1.GetHdc ( ) ;
//得到Bitmap的DC
IntPtr dc2 = g2.GetHdc ( ) ;



(4).调用API函数,把当前屏幕拷贝到创建的Bitmap中:


BitBlt ( dc2 , 0 , 0 , rect.Width , rect.Height , dc1 , 0 , 0 , 13369376 ) ;



(5).释放当前屏幕和此Bitmap对象的DC,通过下面代码可以实现:


//释放掉屏幕的DC
g1.ReleaseHdc ( dc1 ) ;
//释放掉Bitmap的DC
g2.ReleaseHdc ( dc2 ) ;



(6).保存Bitmap对象,形成jpg图片:


MyImage.Save ( @”c:\Capture.jpg” , ImageFormat.Jpeg );



当然你也可以根据自己的需要,把屏幕以其他图片的格式来保存,如果你想把图片保存为位图文件,可以把”ImageFormat.Jpeg”改换成”ImageFormat.Bmp”;想把图片保存为Gif文件,就把”ImageFormat.Jpeg”改换成”ImageFormat.Gif”。你可以保存的文件类型大概有十多种,这里就不一一介绍了,当然你也要相应改变保存文件的后缀。


三. 用C#来捕获屏幕的源程序代码(Capture.cs):


了解上面的这些步骤的实现方法,就可以得到用C#捕获屏幕的源程序,如下:


using System ;
using System.Drawing ;
using System.Collections ;
using System.ComponentModel ;
using System.Windows.Forms ;
using System.Data ;
using System.Drawing.Imaging ;
public class Form1 : Form
{
private Button button1 ;
private System.ComponentModel.Container components = null ;


public Form1 ( )
{
//初始化窗体中的各个组件
InitializeComponent ( ) ;
}
// 清除程序中使用过的资源
protected override void Dispose ( bool disposing )
{
if ( disposing )
{
if ( components != null )
{
components.Dispose ( ) ;
}
}
base.Dispose ( disposing ) ;
}
private void InitializeComponent ( )
{
button1 = new Button ( );
SuspendLayout ( ) ;
button1.Location = new System.Drawing.Point ( 64 , 40 ) ;
button1.Name = “button1″ ;
button1.Size = new System.Drawing.Size ( 80 , 32 ) ;
button1.TabIndex = 0 ;
button1.Text = “捕获” ;
button1.Click += new System.EventHandler ( button1_Click ) ;


AutoScaleBaseSize = new System.Drawing.Size ( 6 , 14 ) ;
ClientSize = new System.Drawing.Size ( 216 , 125 ) ;
Controls.Add ( button1 ) ;
MaximizeBox = false ;
MinimizeBox = false ;
Name = “Form1″ ;
Text = “C#捕获当前屏幕!” ;
ResumeLayout ( false ) ;


}
//声明一个API函数
[ System.Runtime.InteropServices.DllImportAttribute ( "gdi32.dll" ) ]
private static extern bool BitBlt (
IntPtr hdcDest , // 目标 DC的句柄
int nXDest ,
int nYDest ,
int nWidth ,
int nHeight ,
IntPtr hdcSrc , // 源DC的句柄
int nXSrc ,
int nYSrc ,
System.Int32 dwRop // 光栅的处理数值
) ;


static void Main ( )
{
Application.Run ( new Form1 ( ) ) ;
}
private void button1_Click ( object sender , System.EventArgs e )
{
//获得当前屏幕的大小
Rectangle rect = new Rectangle ( ) ;
rect = Screen.GetWorkingArea ( this ) ;
//创建一个以当前屏幕为模板的图象
Graphics g1 = this.CreateGraphics ( ) ;
//创建以屏幕大小为标准的位图
Image MyImage = new Bitmap ( rect.Width , rect.Height , g1 ) ;
Graphics g2 = Graphics.FromImage ( MyImage ) ;
//得到屏幕的DC
IntPtr dc1 = g1.GetHdc ( ) ;
//得到Bitmap的DC
IntPtr dc2 = g2.GetHdc ( ) ;
//调用此API函数,实现屏幕捕获
BitBlt ( dc2 , 0 , 0 , rect.Width , rect.Height , dc1 , 0 , 0 , 13369376 ) ;
//释放掉屏幕的DC
g1.ReleaseHdc ( dc1 ) ;
//释放掉Bitmap的DC
g2.ReleaseHdc ( dc2 ) ;
//以JPG文件格式来保存
MyImage.Save ( @”c:\Capture.jpg” , ImageFormat.Jpeg );
MessageBox.Show ( “当前屏幕已经保存为C盘的capture.jpg文件!” ) ;
}
}



四. 总结:


本文虽然是C#在实际情况的一个应用例子,但所包含的内容相对还比较丰富。其中调用Windows的API函数对广大的程序员来说都是一个比较头痛的问题,因为他涉及面比较广,不仅要掌握C#中使用结构、类型转换、安全/不安全代码,受管/不受管代码等许多知识,还要了解API函数的结构等。希望本文对你有所帮助。

2004年02月10日

#region 实现一个验证码的类



public class ValidateCode
{
private Bitmap validateimage;
private Graphics g;
public ValidateCode()
{
validateimage = new Bitmap(120, 30, PixelFormat.Format24bppRgb);
g = Graphics.FromImage(validateimage);
}



public void DrawValidateCode(Page e, string i)
{
g.DrawString(i, new Font(“黑体”,16,FontStyle.Bold),new SolidBrush(Color.White),new PointF(2,4));
g.FillRectangle(new LinearGradientBrush(new Point(0,0), new Point(120,30), Color.FromArgb(0,0,0,0),Color.FromArgb(255,255,255,255)),0,0,120,30);
//validateimage.Save(e.OutputStream, ImageFormat.Jpeg);
g.Save();
MemoryStream ms=new MemoryStream();
validateimage.Save(ms,System.Drawing.Imaging.ImageFormat.Gif);
e.Response.ClearContent();
e.Response.ContentType=”image/gif”;
e.Response.BinaryWrite(ms.ToArray());
e.Response.End();
//validateimage.Save(e.MapPath(“NumImage/ValidateImage.gif”), ImageFormat.Gif);
//e.End();
}
}



#endregion



private void MakeValidateCode()
{
char[] s = new char[]{“0″,”1″, “2″,”3″,”4″,”5″,”6″,”7″,”8″,”9″,”a”
,”b”,”c”,”d”,”e”,”f”,”g”,”h”,”i”,”j”,”k”,”l”,”m”,”n”,”o”,”p”,”q”
,”r”,”s”,”t”,”u”,”v”,”w”,”x”,”y”,”z”,”A”,”B”,”C”,”D”,”E”,”F”,”G”
,”H”,”I”,”J”,”K”,”L”,”M”,”N”,”O”,”P”,”Q”,”R”,”S”,”T”,”U”,”V”,”W”
,”X”,”Y”,”Z”};
string num = “”;
Random r = new Random();
for(int i = 0; i < 5; i++)
{
num += s[r.Next(0, s.Length)].ToString();
}
((LoginUserInfo)Session["LoginUserInfo"]).ValidateNum = num;
TextBox3.Text = “”;
}

using System.Net.Sockets;
using System.IO;
using System.Net;
using System;


class POP
{
 string POPServer;
 string user;
 string pwd;
 public POP(){}
 public POP(string server, string _user, string _pwd)
 {
  POPServer = server;
  user = _user;
  pwd = _pwd;
 }
 private NetworkStream Connect()
 {
  TcpClient sender = new TcpClient(POPServer,110);
  Byte[] outbytes;
  string input;
  NetworkStream ns = null;
  try
  {
   ns = sender.GetStream();
   StreamReader sr = new StreamReader(ns);
   Console.WriteLine(sr.ReadLine() );


 


   input = “user ” + user + “\r\n”;
   outbytes = System.Text.Encoding.ASCII.GetBytes(input.ToCharArray());
   ns.Write(outbytes,0,outbytes.Length) ;
   Console.WriteLine(sr.ReadLine() );


 


   input = “pass ” + pwd + “\r\n”;
   outbytes = System.Text.Encoding.ASCII.GetBytes(input.ToCharArray());
   ns.Write(outbytes,0,outbytes.Length) ;
   Console.WriteLine(sr.ReadLine() );


 


   return ns;
  }
  catch(InvalidOperationException)
  {
   Console.WriteLine(“Could not connect to mail server”);
   return ns;
  }
 }
 public int GetNumberOfNewMessages()
 {
  Byte[] outbytes;
  string input;
  try
  {
   NetworkStream ns = Connect();
   StreamReader sr = new StreamReader(ns);


 


   input = “stat” + “\r\n”; 
   outbytes = System.Text.Encoding.ASCII.GetBytes(input.ToCharArray());
   ns.Write(outbytes,0,outbytes.Length);
   string resp = sr.ReadLine();
   Console.WriteLine(resp);
   //string[] tokens = resp.Split(new Char[] {” “});
   string[] tokens = resp.Split(Convert.ToChar(” “));


   input = “quit” + “\r\n”;
   outbytes = System.Text.Encoding.ASCII.GetBytes(input.ToCharArray());
   ns.Write(outbytes,0,outbytes.Length);
   Console.WriteLine(sr.ReadLine());


   sr.Close();
   ns.Close();
   return Convert.ToInt32(tokens[1]);
  }
  catch(InvalidOperationException)
  {
   Console.WriteLine(“Could not connect to mail server”);
   return 0;
  }
 }
 
 public static void Main()
 {
  string sUserName;
  string sPop3;
  string sPass;
  Console.Write(“请输入POP3地址:”);
  sPop3 = Console.ReadLine();
  Console.Write(“\r\n请输入用户名:”);
  sUserName = Console.ReadLine();
  Console.Write(“\r\n请输入登录密码:”);
  sPass = Console.ReadLine();
  POP pop = new POP(sPop3,sUserName,sPass);
  Console.Write(“New Messages = {0}”, pop.GetNumberOfNewMessages() );
  Console.ReadLine();
 }
}

2004年02月06日

条码(bar code) 条状平行线和中间空白之组合,粘附于产品或集装箱之上,表达有关数据。可以用电子扫描仪读这些数据。 广泛应用的有通用产品编码(UPC)——为美国和加拿大零售商所广泛应用的一种标准及更新一些的代码UCC/EAN-128。 
计算机辅助订货(computer assisted ordering,简称:CAO) 通过使用计算机合成有关产品流转(POS系统所记录)、影响需求的外部因素(如季节变化)、实际库存水平、产品接收和 可以接受的安全贮货水平等方面的信息,为商店订货作准备。这一基于零售系统的技术,在货架存货降至事先确定的水 平以下时,自动产生补充订货。这一技术成功的关键有赖于全面的商店库存和精确的POS扫描数据。CAO通常能减少订贷 方面的成本,提供单品运转和商店这一级别上的库存流转的及时信息。  
收银机(point of sales,简称:POS) 销售信息管理系统,其基本构件是:商品条码、POS收银台系统、后台电脑。也称为单个收银机。 
POP (POINT OF PURCHASE ADVERTISING) 销售点广告:指超市卖场中能促进销售的广告,也称作销售时点的广告。在零售店内将促销讯息,以美工绘制或印刷方式, 张贴或悬挂在商品附近或显著之处,吸引顾客注意力并达成刺激销售之目的。 
EOS (ELECTRONIC ORDERING SYSTEM,简称:电子订货系统) 主要功能是运用于商店的订货管理和盘点。基本构件是:价格卡、掌上型终端机、数据机。 
DM(Direct Mail,简称:快讯商品广告) 又称促销彩页,一般用于超市商品促销的宣传手段,通常使用邮递、夹报、人工发放、店内领取等形式送到消费者手中。 DM促销是超市最有效的促销手段
并板 把两个或两个以上卡板上的商品,有条理地合并在一个卡板上。
拉排面 商品没有全部摆满货架的时侯,利用先进先出原则,将商品向前排列,使排面充盈、丰满。
拾零 捡回顾客遗弃在各角落的零星商品。
端架 货架两端的位置,也是顾客在卖场回游经过频率最高的地方。
先进先出 先进的货物先销售
理货 把凌乱的商品整理整齐。
堆头 即“促销区”,通常用栈板、铁筐或周转箱堆积而成。
码货 堆放商品、或摆放商品。
换档 相连两期快讯产品的更换。
改价(变价) 更改商品的零售价或进货价格。
价格卡 用于标示商品售价并作定位管理的标牌。
栈板 木制放货的卡板,使商品避免直接放在地面上,并利于使用叉车进行搬运商品。
补货 理货员将缺货的商品,依照商品各自规定的陈列位置,定时或不定时地将商品补充 到货架上去的作业。
试吃 对一些促销食品进行现场加工,并让顾客现场品尝。
清货 为清理商品余货,降价处理活动。
促销员 又称:信息员。厂商为了更好销售、宣传其商品,而派驻商场的其本单位的员工。
会员卡 会员资格的凭记。
稽核 为防止顾客遗漏商品,和收银员收款时发生错误,在其离开时对其所购商品的核对。
称重标签 称重商品特用的标签,一般内含商品名称、包装时间、单价、重量、保质期限等。
滞销 指商品销售效果不明显或很难卖出的现象。
畅销 指商品销售效果好或很易卖出的现象。
平销 指商品销售效果不好也不差。
手推车 顾客购物用的小车。
报废 由于变质或破包、损坏而不能销售,需按废品处理的商品。
消磁 在收银过程中对贴记在商品上的防盗码,进行解除磁性的工作。
盘点 定期对店内商品进行清点,以确实掌握该期间的经营绩 效及库存情况。
上架 把商品摆放在货架上。
库存 指尚未销售出去的商品。
促销试品 用来促进销售用的试用(吃)商品。
赠品 为刺激销售,对购买一定量所售商品的顾客, 给予馈赠定量的商品。
订单号码 向供应商要货的每批订货单的编号。
负库存 帐面上的销售量大于帐面上的库存量,通常因为电脑输入的错误、丢失、损坏等所致。
动线 指商场的布局,使顾客自然行走、购物的轨迹。良好的动线规划可诱导顾客在店内顺畅地选购商品, 避免卖场产生死角,提高卖场坪效。动线设计对超市尤其重要。
坪效 指单位面积的销售额。
米效 指在超市货架上,销售面直线长度上的,每米的销售额。
商品周转率 商品平均销售额除以平均库存额。
商品库存周期 商品平均库存额除以平均销售额,以日计算。超市一般用商品库存周期,来控制资金的使用率,加强商品销售时间的控制。


卖场商品管理的相关专业术语
(一) 商品编码规则(店内码)  
(1) 商品编码统一采用13位数字的UPC或EAN码制;  
(2)“店内码”和“原条码”的惟一对应关系;  
(2) 商品若已有原码,仍需赋予一个“店内码”作为索引码。  
(二) 专业术语 
(1) 货架:商场内主要存放商品的区域,它使顾客购物有规可循,体现商场的经营模式,货架可划分为销售区、展示区、存货区。  
(2) 销售单位:卖给顾客的一个商品数量,一个销售单位可有不同数量包装。  
(3) 价格标签:贴于对应商品处,包括:商品编号、商品说明、产地、规格、等级。 
(4) 复合包装:供应商将几个商品放在一起,作为一个销售单位包装。  
(5) 货架头:位于货架两端,用来展示特别商品的销售储存位置。  
(6) 开箱:打开商品外箱,展示出商品销售单位。  
(7) 介刀:必备工具,安全使用。  
介刀的安全使用程序: 
 *介刀露出的刀尖部分要与纸箱的纸板厚度相同,然后旋紧介刀上螺母,固定刀片。  *开箱时原则上要保留箱子上厂家的商标,开箱以简捷明快为主,开线横平竖直即可。  
(8) 位置调换:保证先到货的商品先出售(尤其是食品和鲜货),原有商品往前陈列,新到商品存在后面。  
(9) 整理:商品摆放整齐,货架、地面、商品无尘。  
(10) 卡板:运输、存放、展示商品的可移动木板。 
(11) 展示品:销售样品,应完整、可运转、清洁、安全。  
(12) 路销:在主通道中间,以卡板排列或临时安装货架的方式进行商品销售。  
(13)垂直陈列:同类货品集中垂直陈列于上下多层货架。  
(14)平行陈列:同类货品平行陈列多行于同一层货架。 
(15)前进陈列:(拉排面)  
(16)D.M(DirectMdl):中文译作“直接信函”,以信函方式将促销讯息通知目标顾客。  (17)P.O.P(Pointofpurchase):中文译为“顾客广告”,在零售店内将促销讯息,以美工绘制或印刷方式,张贴或悬挂在商品附近或显眼之处,吸引顾客之注意力并达成刺激销售之目的。  
(18)P.0.S(Pointofsales):销售时点情报管理系统。  
(19)产品生命周期:指任何一项产品均有其寿命,从其上市起,一般可分为导入期、成长期、成熟期、衰退期,各期期间长短受消费环境及竞争之影响。  
(20)商品台账:即商品目录,将每项商品基本资料(如品名、品号、规格、单位、成本、售价、供应商等)详细整理成册称之。  
(21)端架陈列:指利用整排货架的两端,作变化性的陈列,一般陈列的作法为:
1) 大量陈列;
2) 低价值;
3) 季节感;
4)广告促销。  
(22)关连陈列:指据某项目的,而将相关连之商品陈列在同一地区或附近。  
(23)棚板:系指在货价内或冷藏(冻)柜内,放置商品之横隔板。  
(24)价格带:指在商店内销售同一项产品,其卖价上限到下限之间。  
(25)比较性陈列:把相同商品,依不同数量子以分类,然后陈列在一起,供顾客选择。  
(26)黄金线:指商品陈列时,最容易让顾客看到或拿到之区域,一般指肩膀以下至腰部以上之区域,高度约在0.85—1.20米左右,可陈列对店铺利益贡献较佳之商品。  
(27)ABC:将商品依畅销排行(由第一名排至最后一名),计算出每一项商品营业构成比及累计构成比,而以累计构成比为衡量标准,在累计构成比在80%以前之商品属A级品,累计构成比在81%- 95%之商品属B级品,累计构成比在96%以后之商品属C级品。A 级品可列为重点管理、陈列面扩大、不可缺货。C级品则列为淘汰对象。  
(28)棚割表:日语名词,中文译为“陈列配置表”。即把商品的排面在货架上作一个最有效的分配,以书面表格规划出来。  
(29)价格卡:放置于货架或冷冻(藏)柜棚板前缘或沟槽内之小卡片,价格卡上注明货号、品名、售价等,可供顾客购物参考及陈列位置管理之用。  
(30)大陈列量:对称为堆箱陈列或山积陈列,在卖场辟出一个空间或将端架拆除,将单一商品或2—3品项的商品作量化陈列。  
(31)来客数:指店内收银机所统计之某一端时间交易客数。  
(32)客单价:指由店内收银机所统计之某一段期间总营业额除以该期间之总来客数,得出平均每人购买金额。  
(33)陈列定位管理:依照陈列配置图,将商品陈列位置固定,以便于辨识并做好陈列定位管理。  
(34)耗损宰:指商品在买进卖出过程中,因管理不当或疏忽所造成之损失,其损失金额占营业额之比例。  
(35)SP(SalesPromotion):既“促销”之意。  
(36)80—20法则:系重点管理之原则,其意义为:只要掌握住事物的重点(即其中最重要的20%)即可产生大部分的功效(即成果的80%)。例如:商店内80%的业绩系由20%的品项所达成。  
(37)货号:为商品依类别所编之号码。  
(38)条码:货品上以粗细线条标示供光学扫描器读取货品资料。  
(39)陈列:货品柜设之方式。  
(40)端架:货架尽头,可供特别展示或陈列促销商品之用。  
(41)毛利:售价减成本。  
(42) 日平均售量:D.M.S(DailyMeanSales)单项货品日平均售量数。  
(43)周转率:对某一类别销货的进度,由此来判别采购商品是否正确,及追加作业是否正常,及库存数量是否正常。
(44)建议订单:0.P.L(OrderPropsalList)电脑计算出每项货品应续订数量之报表。
(45)永续订单:生鲜、日配类商品,货到才确认完成订单,也可用尺码商品。
(46)紧急订单:紧急缺货时,采手写订货传真(FAX)给厂商,此订单越少越好.
(47)栈板:陈列器材,商品存放及地面隔离之用。

2004年02月05日


Sample Image - TreeWeb.gif


Introduction


There are not so many to say about this article. It simply offers an implementation for an ASP.NET custom tree control.

Almost three years ago I was trying to implement a tree control in raw asp (http://www.codeproject.com/asp/treecontrol.asp). At that time I realized how many things are missing the web developers that decided to implement their applications using Microsoft Technologies comparing with their colleagues that are using Java Technologies.

No longer after that I heard for the first time about the new technology (.NET) that Microsoft wants to have. From then I was trying in my free available time (which is not so much) to read about .NET and to keep track with the news about it. When I gathered enough information about .NET and the support it offers (not only) for the web applications developers on Microsoft platforms, I understood that finally they could compete with the Java web applications developers. With ASP.NET Microsoft jumped “tree big steps” ahead in supporting the web applications on Windows platforms.

UML Diagram


The diagram describes the basic architecture of the control. 


UML Diagram


iiuga.Web.UI.TreeWeb


TreeWeb class.

Properties


























Elements Gets the elements collection of the tree.
CollapsedElementImage Gets or sets the URL to the image that is showen for a collapsed element.
ExpandedElementImage Gets or sets the URL to the image that is showen for an expanded element.
CheckBoxes Gets or sets whether check boxes are displayed next to the tree elements in the treeweb control.
Target Gets or sets the default target for all tree elements action.
ImageList Gets the collection of images urls for the TreeWeb control.
DefaultElementCssClass Gets or sets the default CssClass for the tree elements.
DefaultElementCssClassOver Gets or sets the default CssClass for the tree elements when OnMouseOver client event occure. [Not Implemented]

Methods

























#ctor TreeWeb constructor
RaisePostBackEvent (IPostBackEventHandler.RaisePostBackEvent) Raise control specific events on post back.
LoadXML Loads the tree structure from a XML file. [Not Implemented]
SaveXML Saves the structure of the tree in a XML structure. [Not Implemented]
Render Render the TreeWeb control.
SaveViewState (IStateManager.SaveViewState) Saves the changes of TreeWeb’s view state to an Object.
LoadViewState (IStateManager.LoadViewState) Loads the TreeWeb’s previously saved view state.
TrackViewState (IStateManager.TrackViewState) Instructs the TreeWeb to track changes to its view state.

Events







Expand Occurs when an tree element is expanded.
Collapse Occurs when an tree element is collapsed.

iiuga.Web.UI.TreeElement


TreeElement class.

Properties



























































ID Gets the ID of the TreeElement.
Text Gets or sets the text to be displayed for the TreeElement.
Elements Gets the ElementsCollection for the TreeElement.
Parent Gets the TreeElement parent object for the current element.
TreeWeb Gets the TreeWeb object.
IsExpanded Gets whether the element is expanded or not.
HasElements Gets whether the element has or not children elements.
Level Gets the level where the element is in the tree.
IsChecked Gets whether the element is checked or not. [Not Implemented]
Key Gets or sets specific data for the tree element.
NavigateUrl Gets or sets the action for the tree element.
Target Gets or sets the target of the tree element action.
CheckBox Gets or sets whether a check box is displayed next to the current element.
ToolTip Gets or sets the ToolTip display text for the current element.
Enabled Gets or sets whether the current element is enabled or not.
CssClass Gets or sets the default CssClass for an element.
CssClassOver Gets or sets the default CssClass for the element when OnMouseOver client event occure. [Not Implemented]
ImageIndex Gets or sets the image index to be showen before the element in the tree.
IsTrackingViewState

Methods




























#ctor TreeElement constructor.
#ctor TreeElement constructor.
#ctor TreeElement constructor.
Expand Expand current element, it has effect only if it has elements.
Collapse Collapse current element, it has effect only if it has elements.
FindElement Finds a specified child element of current tree element.
SaveViewState (IStateManager.SaveViewState) Saves the changes of TreeElement’s view state to an Object.
LoadViewState (IStateManager.LoadViewState) Loads the TreeElement’s previously saved view state.
TrackViewState (IStateManager.TrackViewState) Instructs the TreeElement to track changes to its view state.

Using the code


… as any other web server control.

//
// build the tree structure inside the web form
//

<%@ Register TagPrefix=”iiuga” Namespace=”iiuga.Web.UI”
Assembly=”TreeWebControl” %>

<iiuga:treeweb id=”_treeWeb” runat=”server” CollapsedElementImage=”plus.jpg”
ExpandedElementImage=”minus.jpg”>
<ImageList>
<iiuga:ElementImage ImageUrl=”icon1.gif”></iiuga:ElementImage>
</ImageList>
<Elements>
<iiuga:treeelement text=”Your Online Radio Stations”
CssClass=”Sample3_Header”>
</iiuga:treeelement>
<iiuga:treeelement text=”Classic Rock & Oldie”
ToolTip=”Classic Rock & Oldie”>
<Elements>
<iiuga:treeelement text=”All Rock” ImageIndex=”0″ ToolTip=”AllRock”>
</iiuga:treeelement>
<iiuga:treeelement text=”Classic Rock” ImageIndex=”0″
ToolTip=”ClassicRock”></iiuga:treeelement>
<iiuga:treeelement text=”Elvis Rocks!” ImageIndex=”0″
ToolTip=”ElvisRocks!”></iiuga:treeelement>
<iiuga:treeelement text=”Oldies”
NavigateURL=”javascript:alert(‘Oldies’);”></iiuga:treeelement>
</Elements>
</iiuga:treeelement>
<iiuga:treeelement text=”Classical” ToolTip=”Classical”
CssClass=”Sample3_ElementNode”>
<Elements>
<iiuga:treeelement text=”20th Century”
ToolTip=”20thCentury”></iiuga:treeelement>
<iiuga:treeelement text=”Opera” ImageIndex=”0″
ToolTip=”Opera”></iiuga:treeelement>
<iiuga:treeelement text=”Top Classical”
ToolTip=”TopClassical”></iiuga:treeelement>
</Elements>
</iiuga:treeelement>
</Elements>
</iiuga:treeweb>


 OR

//
// dynamically add elements inside the tree
//

protected iiuga.Web.UI.TreeWeb _treeWeb;

int _elementIndex = _treeWeb.Elements.Add( “New TreeElement” );
_treeWeb.Elements[_elementIndex].CssClass = “CssClass”;
_treeWeb.Elements[_elementIndex].Key = “Key”;
_treeWeb.Elements[_elementIndex].ToolTip = “New TreeElement ToolTip”;


… at the end


The control is far from being complete, some of its methods are not yet implemented and are nice features that can be included. But how it is now covers the normal needs of any website developer, it is a base for somebody who wants to extends its functionality and is an example for everyone who starts to develop custom server controls. For getting new versions of the TreeWeb control, for reporting bugs or for asking me any questions please feel free to go to my webpage and do it. Thanks.

Copyright


Notes:


  • The ClickCounter DLL/code requires the JavaScriptBuilder DLL.
  • The ClickCounter control may be added to (and used from) the VS.NET toolbar but has no related icon.
  • The example page can be dropped into any Web Forms project if the DLLs are included within the application’s /bin folder.
  • The DLLs above have been generated using the .NET Framework v1.1 but the code should work in v1.0

Introduction


When you start writing WebControls or even UserControls in ASP.NET, you will quickly discover the need to register pieces of JavaScript with the container page.


Doing so is reasonably easy, using the API included as part of the Page object, specifically:
























IsClientScriptBlockRegistered Has a given piece of ClientScript been registered?
RegisterClientScriptBlock Register a piece of ClientScript
IsStartupScriptRegistered Has a given piece of StartupScript been registered?
RegisterStartupScript Register a piece of StartupScript.
RegisterArrayDeclaration Register an element in a global array which can be accessed by scripts.
RegisterHiddenField Register a hidden field for use within JavaScript (useful for postback data).
RegisterOnSubmitStatement Registers some JavaScript to be executed in the onsubmit event of the form.

The intent of this article is not to cover these individually or in detail, that may be done in another article.


This article and the downloadable class libraries are intended to address specific problems common to RegisterClientScriptBlock, RegisterStartupScript and RegisterOnSubmitStatement and to offer a remarkably simple solution.


As an overall theme, the problems involve the age-old development issues of maintenance vs. efficiency. This article attempts to maximize both in an environment that encourages neither.


Problem 1: String Handling


Each of the above methods accepts a piece of JavaScript code in string format as an argument. But building a string using the ASP.NET string object can be a horrible waste of resources. Consider the three best options:



  1. Simply concatenating strings using the + operator or even String.Concat will create a new string for each concatenation, using massive resources when building an entire slice of JavaScript code.
  2. String.Format or StringBuilder.AppendFormat can be powerful and more efficient, but tend to make JavaScript code within C# (or indeed VB.NET) less readable. It also poses a problem with opening/closing code blocks (“{” / “}”) against formatting place-markers (“{0}”).
  3. Repeated calls to StringBuilder.Append would be the most efficient option, but that can lead to painfully unmanageable code.

Take this simple piece of JavaScript, for example (we will refer back to this throughout the article):

<script language=“javascript”>
<!–
function MyWebControl_AlertText ()
{
alert(“MyWebControl”);
}
// –>
</script>

All this script would do is throw up a message box containing the name of the WebControl. It would be registered by the control, to avoid multiple occurrences, but the function name and literal text must be overridable by inheritors, in case they decide to change the script.


So to create this code using option 1 (string + string), you would need to write the following code:

string script = “<script language=\”javascript\“>” + Environment.NewLine
+ “<!–” + Environment.NewLine
+ “function “ + FunctionName + ” ()” + Environment.NewLine
+ “{“ + Environment.NewLine
+ “\talert(\” + ControlName + “);” + Environment.NewLine
+ “}” + Environment.NewLine
+ “// –>” + Environment.NewLine
+ “</script>”;

This is all quite readable, but the CLR is going to create as many as 19 string objects, which are all going to sit around until the Garbage Collector gets around to picking them up. On a busy website, this could be a serious problem.


Let’s have a look at option 2 (Formatting strings via StringBuilder).

StringBuilder sb = new StringBuilder();
sb.AppendFormat(“<script language=\”javascript\“>{0}”, Environment.NewLine);
sb.AppendFormat(“<!–{0}”, Environment.NewLine);
sb.AppendFormat(“function {0} (){1}”, FunctionName, Environment.NewLine);
sb.Append(“{“);
sb.Append(Environment.NewLine);
sb.AppendFormat(“\talert(\”{0}\“);{1}”, ControlName, Environment.NewLine);
sb.Append(“}”);
sb.Append(Environment.NewLine);
sb.AppendFormat(“// –>{0}”, Environment.NewLine);
sb.Append(“</script>”);
string script = sb.ToString();

Formatting strings this way is not a bad option, it is certainly a fair compromise between the other two. It is still readable, though it was nicer when strings were placed where they should be; it is certainly more efficient than concatenating strings, but it’s not as efficient as repeated calls to Append.


So let’s take a look at that third and final option:

StringBuilder sb = new StringBuilder();
sb.Append(“<script language=\”javascript\“>”);
sb.Append(Environment.NewLine);
sb.Append(“<!–”);
sb.Append(Environment.NewLine);
sb.Append(“function “);
sb.Append(FunctionName);
sb.Append(” ()”);
sb.Append(Environment.NewLine);
sb.Append(“{“);
sb.Append(Environment.NewLine);
sb.Append(“\talert(\”“);
sb.Append(ControlName);
sb.Append(“
\“);”);
sb.Append(Environment.NewLine);
sb.Append(“}”);
sb.Append(Environment.NewLine);
sb.Append(“// –>”);
sb.Append(Environment.NewLine);
sb.Append(“</script>”);
string script = sb.ToString();

This is definitely the ideal option for a marketable server control, using StringBuilder to maximum efficiency, but just imagine for a moment coming back to a complex piece of JavaScript, built like this, some six months after you’ve written it. This is not a pretty image.


Problem 2: Differing Styles


Of all the above styles, I would almost certainly choose option 2. You might opt for another one entirely, for reasons that are very acceptable but do not fit my way of thinking.


Likewise, you may choose not to use Environment.NewLine, preferring \r\n or even just \n (most browsers don’t really care) for its simplicity and efficiency. Personally, I would rather stick nails in my head than deal with streams of escape codes. Even the tabs I would use for indenting can be sore on the eyes after a while.


There are many other ways the above pieces of code could be improved or degraded, depending on your point of view. Again, it comes down to maintenance vs. efficiency.


Every decision you make is going to affect anyone picking up your code later (and in a work environment, this will almost certainly happen, no matter how much we all like to pretend it will not). The effect could be good or bad and at the time you are developing your control, you cannot possibly know.


Problem 3: Comments and Whitespace


JavaScript development always leaves you with a predicament when it comes to whitespace and inline comments.


Both are a waste of bandwidth and will rarely even be seen. Sometimes you may not even want the end-user to see the comments you have used in your JavaScript, or even read the code without an immense amount of effort.


On the other hand, if you choose not to use them then reading your own code can be a daunting prospect.


All of this is particularly pertinent when you are hoping to sell a server control to a web space service provider which may then be reused by dozens of amateur web developers.


A Simple Solution to Three Problems


What we really need is a class that encapsulates the StringBuilder, for the sake of efficiency, but allows us different options designed more specifically for a script-writing environment.


We also need a class that limits our options and makes it clear to any developer (even one unfamiliar with the class), what we are aiming to achieve. If we can duplicate the best features of all our earlier options then that would remove the need for different styles of JavaScript building.


Finally, we need a class with which we can easily switch from highly manageable code to highly efficient code (as we all commonly do when compiling DEBUG and RELEASE versions of software).


If we can implement just a tiny bit of debugging into that class, because JavaScript is so hard to debug, wouldn’t that be great?


And so, enter the JavaScriptBuilder class.


Step 1: Create the Class

using System;
using System.Text;

namespace CP.WebControls
{
public class JavaScriptBuilder
{
private StringBuilder sb = new StringBuilder();
private int currIndent = 0;
private int openBlocks = 0;
private bool format = false;

public JavaScriptBuilder()
{
}

public JavaScriptBuilder(bool Formatted)
{
format = Formatted;
}

// Script handling code here

}
}


There is nothing unusual about the basic structure of the class.


Apart from System, the only .NET Framework namespace we need is System.Text, for the StringBuilder class.


We need a StringBuilder object, created when the JavaScriptBuilder is instantiated. The rest of the private members handle the formatting, and whether there is going to be any. This latter question must be answered when we create the object because it is used throughout, the rest is handled as we go along.


Step 2: Add Lines and Retrieve the Result

public void AddLine(params string[] parts)
{
// Append parts of the line to StringBuilder individually
// – much more efficient than sb.AppendFormat
foreach (string part in parts)
sb.Append(part);

// Add a new line
sb.Append(Environment.NewLine);
}

public override string ToString()
{
// Add the <script> tags and some comment blocks, so that
// browsers that don’t support scripts will not crash horribly
return String.Format(
“<script language=\”javascript\“>{0}<!–{0}{1}{0}// –>{0}</script>”,
Environment.NewLine,
sb
);
}

The only trick here is to learn as many lessons from our first problem definition as possible.


What we truly want is the power and readability of string concatenation, combined with the efficiency of StringBuilder.Append(). Using C#’s params keyword we can do just that.


We can also add the open and close sequence for every piece of JavaScript in the ToString() function, because they should be the same in every block of script we register.


These two bits of code alone allow us to use the following code to produce our initial simple script block:

JavaScriptBuilder jsb = new JavaScriptBuilder(true);
jsb.AddLine(“function “, FunctionName, ” ()”);

jsb.AddLine(“{“);
jsb.AddLine(“\talert(\”“, ControlName, “\“);”);
jsb.AddLine(“}”);

string script = jsb.ToString();


Already much simpler and more readable than any of our initial three options and we have not lost any efficiency at all over our best-case option.


Step 3: Track the Indentation

public int Indent
{
get { return currIndent; }
set { currIndent = value; }
}
public void OpenBlock()
{
AddLine(“{“);
currIndent++;
openBlocks++;
}
public void CloseBlock()
{
// Check that there is at least one block open
if (openBlocks < 1)
throw new InvalidOperationException(
“JavaScriptBuilder.CloseBlock() called when no blocks open”
);

currIndent–;
openBlocks–;
AddLine(“}”);
}

public void AddLine(params string[] parts)
{
// Open line with tabs
for (int i=0; i < currIndent; i++)
sb.Append(“\t”);

// Append parts of the line to StringBuilder individually
// – much more efficient than sb.AppendFormat
foreach (string part in parts)
sb.Append(part);

// Add a new line
sb.Append(Environment.NewLine);
}


This code allows the developer to open and close blocks using the JavaScriptBuilder class and not have to remember how many blocks are open at any given time to insert their own tabs. It also means that you do not have to deal with streams of escape codes at the beginning of each line, which for me was a significant benefit.


Another, not so obvious, advantage of this is that you can move a piece of code from inside a block to the outside (or vice versa) and not have to worry about adjusting the number of tabs on each line.


The more observant reader is probably asking themselves why we would need to keep track of both indentation and the number of open blocks. There are situations where you might want to indent a piece of code without opening a new block, for example if one line of code is stretched across a number of lines of text.


So we need to expose the Indent property to the calling program without breaking the validation line we used in CloseBlock(). We can also include a similar validation in ToString(), so that the calling program can only convert the script to a string once all code blocks have been closed. We do not, however, care if the calling program has left indents active, because it will not make the script fail.


Note that openBlocks is not exposed in any way, it is handled entirely internally.


Going back yet again to our earlier simple script, it can now be written as easily as:

JavaScriptBuilder jsb = new JavaScriptBuilder(true);
jsb.AddLine(“function “, FunctionName, ” ()”);

jsb.OpenBlock();
jsb.AddLine(“alert(\”“, ControlName, “\“);”);
jsb.CloseBlock();

string script = jsb.ToString();


Step 4: Handle the Formatting Flag

public void AddLine(params string[] parts)
{
// Open line with tabs, where formatting is set
if (format)
for (int i=0; i < currIndent; i++)
sb.Append(“\t”);

// Append parts of the line to StringBuilder individually
// – much more efficient than sb.AppendFormat
foreach (string part in parts)
sb.Append(part);

// Append a new line where formatting is set or a space
// where it isn’t
if (format)
sb.Append(Environment.NewLine);
else
if (parts.Length > 0)
sb.Append(” “);
}

public void AddCommentLine(params string[] CommentText)
{
if (format)
{
// Open the line with tab indent
for (int i=0; i < currIndent; i++)
sb.Append(“\t”);

// … and a comment marker
sb.Append (“// “);

// Append all the parts of the line
foreach (string part in CommentText)
sb.Append(part);

// Throw in a new line
sb.Append(Environment.NewLine);
}
}


The changes to AddLine() mean that tabs and new line will only be added where the format flag is set. If the flag is not set then we simply separate each line of code with a single space. There is also a new method AddCommentLine() which allows us to add a comment only when the format flag is set.


To demonstrate, let’s add a single comment to our simple script block:

JavaScriptBuilder jsb = new JavaScriptBuilder(true);
jsb.AddLine(“function “, FunctionName, ” ()”);

jsb.OpenBlock();
jsb.AddCommentLine(“A message box showing the Control name”);
jsb.AddLine(“alert(\”“, ControlName, “\“);”);
jsb.CloseBlock();

string script = jsb.ToString();


Currently, this JavaScriptBuilder will produce the following script block:

<script language=“javascript”>
<!–
function MyWebControl_AlertText ()
{
// A message box showing the Control name
alert(“MyWebControl”);
}
// –>
</script>

But if you change true to false (or allow it to default) in the constructor, the script block is unformatted as follows:

<script language=“javascript”>
<!–
function MyWebControl_AlertText () { alert(“MyWebControl”); }
// –>
</script>

This saves a few bytes of bandwidth on every page that uses your control. But with a simple one-word change to your code, you can return the script to its original state, should you need to edit or debug it.


One easy way of handling this is to write the constructor as follows:

#if DEBUG
JavaScriptBuilder jsb = new JavaScriptBuilder(true);
#else
JavaScriptBuilder jsb = new JavaScriptBuilder();
#endif

This then allows us to test the control using neat, readable JavaScript and, without a single line change in the code, create an efficient release version of the control.


Example: The ClickCounter Control


To show fully the power of the JavaScriptBuilder, we should create a control with a more complex script block.



The ClickCounter control will display some text along with a click counter. Each time the control is clicked, it will increment the counter and retain the initial text.


The code for this control can be downloaded at the top of the article.


Note: this could more easily be handled by using two <span> tags and only adjusting the contents of the second, but that would not suit the purposes of this article as the JavaScript would only be a couple of lines.


The control itself is simple enough, two ViewState-enabled properties (.Text and .InitialValue) with Render() overridden. I suspect that if you have read this far, you already know how to do that. This article is only of interest to Control Developers.


The script is registered as follows:

[
DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)
]
protected virtual string IncrementScriptName
{
get
{
return “ClickCounter_IncrementValue”;
}
}

protected override void Render(HtmlTextWriter output)
{
if (!Page.IsStartupScriptRegistered(IncrementScriptName))
Page.RegisterStartupScript(IncrementScriptName, IncrementScript);

// standard rendering code here
}


The IncrementScriptName property is designed to allow inheritors to override the script name along with the script text and, where a page includes both types of controls, both scripts can be included in the generated page without overriding the more complicated Render() method.


Now take a look at the IncrementScript property code, which is the code more relevant to this article.

[
DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)
]
protected virtual string IncrementScript
{
get
{
#if DEBUG
JavaScriptBuilder jsb = new JavaScriptBuilder(true);
#else
JavaScriptBuilder jsb = new JavaScriptBuilder();
#endif
jsb.AddCommentLine(“Splits the inner text” +
” into Text (txt) and Counter (c) parts,”);
jsb.AddCommentLine(“increments (c) and” +
” joins them back together again.”);
jsb.AddLine(“function “, IncrementScriptName, “(elmt)”);

jsb.OpenBlock(); // function (elmt)
jsb.AddCommentLine(“Initialize variables”);
jsb.AddLine(“var inner = elmt.innerText;”);
jsb.AddLine(“var c = 0;”);
jsb.AddLine(“var txt = \”\“;”);

jsb.AddLine();

jsb.AddCommentLine(“Run through inner text string”);
jsb.AddLine(“for (idx = 0; idx < inner.length; idx++)”);

jsb.OpenBlock(); // for (idx…)
jsb.AddCommentLine(“Split string into text and counter parts”);
jsb.AddLine(“c = parseInt( inner.substring (idx, inner.length + 1) );”);
jsb.AddLine(“txt = inner.substring(0, idx);”);

jsb.AddLine();

jsb.AddCommentLine(“If we have a number, get out of the loop”);
jsb.AddLine(“if ( ! isNaN( c ) )”);

jsb.OpenBlock(); // if ( ! isNaN( c ) )
jsb.AddLine(“break;”);
jsb.CloseBlock(); // if ( ! isNaN( c ) )

jsb.CloseBlock(); // for (idx…)

jsb.AddLine();
jsb.AddCommentLine(“Increment counter”);
jsb.AddLine(“c++;”);
jsb.AddCommentLine(“Rebuild the string and put it in the inner text”);
jsb.AddLine(“elmt.innerText = txt + \” \” + c;”);
jsb.CloseBlock(); // function (elmt)

return jsb.ToString();
}
}


This generates the following piece of JavaScript when compiled in DEBUG mode:

<script language=“javascript”>
<!–
// Splits the inner text into Text (txt) and Counter (c) parts,
// increments (c) and joins them back together again.
function ClickCounter_IncrementValue(elmt)
{
// Initialize variables
var inner = elmt.innerText;
var c = 0;
var txt = “”;

// Run through inner text string
for (idx = 0; idx < inner.length; idx++)
{
// Split string into text and counter parts
c = parseInt( inner.substring (idx, inner.length + 1) );
txt = inner.substring(0, idx);

// If we have a number, get out of the loop
if ( ! isNaN( c ) )
{
break;
}
}

// Increment counter
c++;
// Rebuild the string and put it in the inner text
elmt.innerText = txt + ” “ + c;
}
// –>
</script>


This is quite readable within the control itself and very readable in the generated page. Meanwhile it is achieved with all the efficiency of repeated calls to StringBuilder.Append() and without a single escape character or Environment.NewLine in sight.


However, it does generate a lot of unnecessary code for the end user. They will rarely look at the code and again, if 1000 pages include your control and are accessed 1000 times a day, that’s 1Mb of bandwidth per extra byte of code every day.


The release version of the control will have a much shorter piece of code:

<script language=“javascript”>
<!–
function ClickCounter_IncrementValue(elmt) { var inner = elmt.innerText; var c =
0; var txt = “”; for (idx = 0; idx < inner.length; idx++) { c = parseInt(
inner.substring (idx, inner.length + 1) ); txt = inner.substring(0, idx); if (
! isNaN( c ) ) { break; } } c++; elmt.innerText = txt + ” “ + c; }
// –>
</script>

Totally unreadable, but the functionality has not changed. This new version is considerably shorter with just under 400 bytes saved. That totals as much as 400Mb of bandwidth on your 1000 pages accessed 1000 times a day. Imagine the effect on a really complicated piece of JavaScript.


Summary


JavaScriptBuilder can be included in your code as a .cs (C#) class file or linked in from a very small (16Kb) DLL. It can be used to create JavaScript code that is readable in a generated page and easily maintained in the control’s source, but without any loss of efficiency in terms of either server memory or bandwidth.


Forget about choosing between maintenance and efficiency, choose both.

Note: As with anything available on CodeProject, it is free to use, but it would be nice to hear from you if you decide to use it. If you have any ideas for improvements, I would like to hear that too.


Screenshot


Introduction


I find a lot of .NET programmers often don’t have much experience or knowledge of Flash and it’s capabilities. One of the exciting features of Flash MX was that flash files could very easily load external MP3 files and control their playback. I decided that I would implement some of my Flash skills into making a control to sit in my Visual Studio Toolbox that I could easily drag and drop into any project and not have to worry about implementing the resource files. One of the features I worked on was having the Flash file self-contained within the DLL, and having the control intelligently deposit a copy of the Flash file into the appropriate directory, without the programmer having to worry about implementation or setup. Of course, I also liked the idea of using the property panel of Visual Studio to configure my Flash file in a no fuss, error-free manner. So I strived to make the control adaptable, with properties for console UI, looping, streaming and etcetera.


Using the PseudoMP3 server control


Of course, if your using an IDE like Visual Studio, you don’t really need to know how to register or what the property names are. But for the sake of clarity…


The very first thing you need is to register the tag prefix.

<%@ Register TagPrefix=”pseudoengine” Namespace=”PseudoEngine”
Assembly=”PseudoMp3″ %>

Then you can create the control like so…

<pseudoengine:PseudoMP3
id=”PseudoMP31″
runat=”server”
Src=”http://www.myDomain.com/myMp3.mp3″
Stream=”False”
AutoStart=”False”
Loop=”False”
Console=”True”>
</pseudoengine:PseudoMP3>

Properties explanation



  • Src: URL location of the MP3 file.
  • Stream: Boolean value to indicate whether MP3 file should be streamed. (Otherwise file is preloaded)
  • AutoStart: Boolean value to indicate whether file should start when page loads. (Guaranteed to annoy or your money back)
  • Loop: Boolean value to indicate whether file should play itself again when it ends.
  • Console: Boolean value to indicate whether to display the UI.

What’s in the source files


Everything. The Flash source files, Visual Studio solution, a test harness web app and all the burritos you can eat. Well, maybe not the burritos.


Conclusion


Since I’ve finished this control, I’ve actually thought of a few extensions I could implement. It wouldn’t be that hard to have the control generate an XML document describing a play list and feed it to the Flash file. You could also add properties to control the initial volume, start position of the track and specific number of loops to have the MP3 playback. Even maybe detect the ID3 metadata and feed the Flash file with the correct title of the song to display. Anyway, I’m bored with this project, so on to the next one.


Introduction


For those out there who have added Macromedia Flash Movies to your dynamic webpages you no doubt found it frustrating to make the movie itself dynaimic. That is, dynamic variables and properties; or even the movie itself dynamic like a rotator. And even more frustrating what if you wanted default html in place just in case the flash plugin is not installed or you are going the route of enforcing that a certain version is installed through javascript, and in fact javascript has been disabled. Well, although this custom asp.net control does not sing and dance, it does make these tasks extreamly easy to manage.


The FlashMovieControl wraps up all properties that can be pushed into a flash movie, (i.e. scale, width, height, windowmode…the list goes on. And does it all with great designer support, which proved to be a valuable learning experience for me. The current version of this control is currently up-tod-ate with the latest properties and output rendering of Macromedia 2004 Flash MX. You will find that the rendered content that is sent to the browser is exact to what is published directly from the Macromedai MX studio.



Features



  • Dynamically send variables to your flash movie.
public class FlashMovieTest {
protected Osmosis.Web.UI.Controls.FlashMovie FlashMovie1;

private void function AddMovieVariables() {
this.FlashMovie1 = new Osmosis.Web.UI.Controls.FlashMovie();
this.FlashMovie1.MovieVariables.Add(“MyVar1″,“MyValue1″);
this.FlashMovie1.MovieVariables.Add(“MyVar2″,“MyValue2″);
}
}
————————————-
Renders as:
<object classid= …. >
<PARAM NAME=movie VALUE=“MyMovie.swf?MyVar1=MyValue1&MyVar2=MyValue2″> ….



  • Easily add html content just in case the flash plugin is unavailable on the client. (Only available with ClientScriptVersionDection). When the control is rendered all nessecessary javascript code is rendered for version detection including the html content to render just in case no plugin is available. Notice that the html code is wrapped up for you in the nessesary document.write(”) statements automatically. A huuuuuuuuuuuge time saver.
public class FlashMovieTest {
protected Osmosis.Web.UI.Controls.FlashMovie FlashMovie1;

private void function AddNoFlashPluginContent() {
this.FlashMovie1 = new Osmosis.Web.UI.Controls.FlashMovie();
this.FlashMovie1.FlashOutputType =
Osmosis.Web.UI.FlashOutputType.ClientScriptVersionDection;
this.FlashMovie1.NoFlashContainer.Controls.Add(new Table());

}
}
————————————-
Renders as:
if ( MM_FlashCanPlay ) { …

} else{
document.write(‘<table border=“0″ ID=“Table1″>’);
document.write(”);
document.write(‘</table>’);
}



  • Almost identical to adding content to the NoFlashContainer you can also add html just in case javascript is disabled and you are using ClientScriptVersionDection. When the control is rendered all nessecessary javascript code is rendered for version detection. But what if the user’s browser doesn’t support javascript, you can add webcontrols programmically that will render themselves within a <noscript></noscript> html block.
public class FlashMovieTest {
protected Osmosis.Web.UI.Controls.FlashMovie FlashMovie1;

private void function AddNoFlashPluginContent() {
this.FlashMovie1 = new Osmosis.Web.UI.Controls.FlashMovie();
this.FlashMovie1.FlashOutputType =
Osmosis.Web.UI.FlashOutputType.ClientScriptVersionDection;
this.FlashMovie1.NoScriptContainer.Controls.Add(new Table());

}
}
————————————-
Renders as:
<NOSCRIPT>
<table border=“0″ ID=“Table1″></table>
</NOSCRIPT>


What are the FlashOutputType’s for? (Osmosis.Web.UI.FlashOutputType)



  • ClientScriptVersionDection: Tells the FlashMovieControl to render the nessesary javascript code to perform plugin version detection on the version that you specify. Also rendered in this mode is any NoScript/NoPlugin content that you assigned to the NoScriptContainer and NoFlashContainers.
  • FlashOnly: Tells the FlashMovieControl to only render the nessesary html to embed your movie into the webpage. Basically everything but the client script for detecting the plugin version.
  • SWFVersionDetection: This one’s a bit odd. It is the latest ouput type used in Macromedia’s Flash studio 2004. It uses an swf movie to detect if flash is installed. What? it uses a Flash Movie to detect if Flash is installed on the client. It didn’t make sense to me either. But in Flash MX 2004 when you publish with this flag set it will generate an swf movie (version 4 only) that will direct you to your intended movie. Use this output method with the FlashMovieControl properties: SWF_
    ///Example of using SWFVersionDetection
    this.FlashMovie1.FlashOutputType =
    Osmosis.Web.UI.FlashOutputType.SWFVersionDetection;
    this.FlashMovie1.MovieName = “MyMacromediaGeneratedDetectionMovie.swf”;

    this.FlashMovie1.SWF_DetectionAltUrl = “IfNotDetectedUrl.aspx”;
    this.FlashMovie1.SWF_DetectionContentUrl = “IfDetectedUrl.aspx”;


  • All other output types are not supported but default to FlashOnly. I added them into the enumeration for rememberance to add in future releases. Speciifically FSCommand. Which will probably be next.

Get me started using the FlashMovieControl

Make sure to reference the .dll in your project.
In the solution explorer locate your project and right click on the References node. Select Add Reference. Browse for the .dll file downloaded from CodeProject and click OK.

To use with the designer
When your webform is in design view open up the Toolbox and right click. Select Add/Remove Items. Browse for the .dll file downloaded from CodeProject and click OK. Now the FlashMovieControl is added to your Toolbox. Just drag it onto you page and open up your properties window.

One thing to keep in mind is to always set the MovieName property. This is the url of the actual .swf file. If you forget to add the actual movie your browser will hang because it is trying to locate the file that is non-existent. In a future release I will set the control to throw an exception if no movie has been set.

Below is an example of using the FlashMovieControl with ClientScriptVersionDetection as the FlashOutputType.
using System;
using System.Collections;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Web;
using System.Web.SessionState;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.HtmlControls;

namespace FlashTest
{

public class FlashMovieControlTest : System.Web.UI.Page
{
protected Osmosis.Web.UI.Controls.FlashMovie FlashMovie1;

private void Page_Load(object sender, System.EventArgs e)
{
this.FlashMovie1 = new Osmosis.Web.UI.Controls.FlashMovie();

///Set the output type and Movie
this.FlashMovie1.FlashOutputType =
Osmosis.Web.UI.FlashOutputType.ClientScriptVersionDection;
///Always make sure you have a valid movie
///or your browser will hang.
this.FlashMovie1.MovieName = “MyMovie.swf”;

/// Set the plugin version to check for
this.FlashMovie1.MajorPluginVersion = 7;
this.FlashMovie1.MajorPluginVersionRevision = 0;
this.FlashMovie1.MinorPluginVersion = 0;
this.FlashMovie1.MinorPluginVersionRevision = 0;

///Set some other properties
this.FlashMovie1.MovieHeight = “400px”;
this.FlashMovie1.MovieWidth = “200px”;
this.FlashMovie1.AutoLoop = false;
this.FlashMovie1.AutoPlay = true;
this.FlashMovie1.FlashHorizontalAlignment =
Osmosis.Web.UI.FlashHorizontalAlignment.Center;
this.FlashMovie1.FlashVerticalAlignment =
Osmosis.Web.UI.FlashVerticalAlignment.Top;
this.FlashMovie1.HtmlAlignment =
Osmosis.Web.UI.FlashHtmlAlignment.Right;
this.FlashMovie1.UseDeviceFonts = false;
this.FlashMovie1.WindowMode =
Osmosis.Web.UI.FlashMovieWindowMode.Transparent;
this.FlashMovie1.ShowMenu = false;
this.FlashMovie1.MovieQuality =
Osmosis.Web.UI.FlashMovieQuality.AutoHigh;
this.FlashMovie1.MovieScale =
Osmosis.Web.UI.FlashMovieScale.NoScale;

/// Add some variables
this.FlashMovie1.MovieVariables.Add(“MyVar1″,“MyValue1″);
this.FlashMovie1.MovieVariables.Add(“MyVar2″,“MyValue2″);
this.FlashMovie1.MovieVariables.Add(“MyVar3″,“MyValue3″);

///Set the NoScript and NoFlash content.
///In most situations where
///html will be displayed the content is the same for both
this.FlashMovie1.NoFlashContainer.Controls.Add(
this.GetDefaultHtmlContent());
this.FlashMovie1.NoScriptContainer.Controls.Add(
this.GetDefaultHtmlContent());
}

private HtmlTable GetDefaultHtmlContent(){

HtmlTable tbl = new HtmlTable();
HtmlTableRow tr = new HtmlTableRow();
HtmlTableCell td = new HtmlTableCell();
Label lbl = new Label();
lbl.Text = “This is my default content”;

td.Controls.Add(lbl);
tr.Cells.Add(td);
tbl.Rows.Add(tr);
return tbl;
}

#region Web Form Designer generated code
override protected void OnInit(EventArgs e)
{
//
// CODEGEN: This call is required by the ASP.NET
// Web Form Designer.
//
InitializeComponent();
base.OnInit(e);
}

/// <summary>
/// Required method for Designer support – do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
this.Load += new System.EventHandler(this.Page_Load);

}
#endregion
}
}

——————————————————————-
Renders As……
——————————————————————-
<SCRIPT LANGUAGE=JavaScript1.1>
<!–
var MM_contentVersion = 7;
var plugin = (navigator.mimeTypes && navigator.mimeTypes[
"application/x-shockwave-flash"]) ?
navigator.mimeTypes["application/x-shockwave-flash"].enabledPlugin : 0;
if ( plugin ) {
var words = navigator.plugins["Shockwave Flash"].description.split(” “);
for (var i = 0; i < words.length; ++i)
{
if (isNaN(parseInt(words[i])))
continue;
var MM_PluginVersion = words[i];
}
var MM_FlashCanPlay = MM_PluginVersion >= MM_contentVersion;
}
else if (navigator.userAgent && navigator.userAgent.indexOf(“MSIE”)>=0
&& (navigator.appVersion.indexOf(“Win”) != -1)) {
document.write(‘<SCR’ + ‘IPT LANGUAGE=VBScript\> \n’);
document.write(‘on error resume next \n’);
document.write(‘MM_FlashCanPlay = (
IsObject(CreateObject(“ShockwaveFlash.ShockwaveFlash.” &
MM_contentVersion)))\n’);
document.write(‘</SCR’ + ‘IPT\> \n’);
}
if ( MM_FlashCanPlay ) {
document.write(‘<OBJECT classid=
“clsid:d27cdb6e-ae6d-11cf-96b8-444553540000″‘);
document.write(‘ codebase=
“http://fpdownload.macromedia.com/pub/shockwave/cabs/
flash/swflash.cab#version=7,0,0,0″
‘);
document.write(‘ ID=“FlashMovie1″ WIDTH=“200px”
HEIGHT=“400px” ALIGN=“right”>’);
document.write(‘ <PARAM NAME=movie
VALUE=“MyMovie.swf?MyVar1=MyValue1&MyVar2=MyValue2&MyVar3=MyValue3″>
<PARAM NAME=loop VALUE=false /> <PARAM NAME=menu VALUE=false />
<PARAM NAME=scale VALUE=noscale /> <PARAM NAME=wmode VALUE=transparent />
<PARAM NAME=salign VALUE=T /> <PARAM NAME=quality VALUE=AutoHigh/>
<PARAM NAME=bgcolor VALUE=#ffffff/> <PARAM NAME=“allowScriptAccess”
value=“SameDomain” /> ‘);
document.write(‘ <EMBED
src=“MyMovie.swf?MyVar1=MyValue1&MyVar2=MyValue2&MyVar3=MyValue3″
loop=“false” menu=“false” scale=“noscale” wmode=“transparent”
salign=“T” quality=“autohigh” bgcolor=“#ffffff”
allowScriptAccess=“SameDomain” ‘);
document.write(‘ swLiveConnect=FALSE WIDTH=“200px”
HEIGHT=“400px” NAME=“MyMovie.swf” ALIGN=“right”‘);
document.write(‘ TYPE=“application/x-shockwave-flash”
PLUGINSPAGE=“http://www.macromedia.com/go/getflashplayer”>’);
document.write(‘ </EMBED>’);
document.write(‘ </OBJECT>’);
} else{
document.write(‘<table>’);
document.write(‘ <tr>’);
document.write(‘ <td><span>This is my default content</span></td>’);
document.write(‘ </tr>’);
document.write(‘</table>’);
}
//–>
</SCRIPT>
<NOSCRIPT>
<table ID=“Table1″>
<tr>
<td><span>This is my default content</span></td>
</tr>
</table>
</NOSCRIPT>

HTML View example
——————

<OSMOSIS:FLASHMOVIE id=FlashMovie1 runat=”server”
MovieName=”MyMovie.swf” AutoLoop=”False” AutoPlay=”False”
MovieScale=”NoScale” MovieQuality=”AutoLow” FlashHorizontalAlignment=”Right”
FlashVerticalAlignment=”Top” MovieHeight=”222″ MovieWidth=”33″
WindowMode=”Transparent”></Osmosis:FlashMovie>


Sample Image - PanelBar.jpg


Introduction


The PanelBar control is a rich UI web control. It can be used to build an Outlook-style application. I’m providing this control as a sample for building ASP.NET custom controls. Click here for an online demo.


Using the control


The PanelBar has full design support. So, using the control is fairly simple. Just install the component by adding the PanelBar.dll to the list of components in Visual Studio. NET. Drag an instance of the PanelBar component on a form and set the design-time properties. The PanelBar has an items property which gives access to the collection of PanelBarItems. The appearance of each button can be set by using the Text and ImageUrl properties. The PanelBar component also has an ImageUrl property that is used for providing a default image for the buttons.


About the code


The code is very well commented. The following classes are defined:



  • ScrollPanel: implements a scrollable Panel, used as a base class for the PanelBar control
  • PanelBar: implements the PanelBar control
  • PanelBarItem: implements the PanelBar buttons (consists of a LinkButton and an ImageButton)
  • PanelBarControlBuilder: implements the control builder for the PanelBar
  • PanelBarControlDesigner: implements the control designer for the PanelBar

    原文地址:http://www.codeproject.com/aspnet/panelbar.asp

Dropdownlist无刷新的例子。xml.
例如文本验证!
下面是一个单选按纽前台不刷新的例子.有好的就往上贴
<script language=”javascript”>
   function SetButton()   
   {
    if(document.all.rdoByHuman.checked==true)
    {
     document.all.cboHrPut.disabled = “”;
     document.all.cboAnswerHr.disabled = “”;
     
          
     document.all.cboGroup.disabled = true;
     
    }
    if(document.all.rdoByGroup.checked==true)
    {
     document.all.cboHrPut.disabled = true;
     document.all.cboAnswerHr.disabled = true;
          
     document.all.cboGroup.disabled = “”;
     
     
    
    }
   }
  </script>


 
<asp:RadioButton id=”rdoByHuman” onclick=”SetButton();” runat=”server” GroupName=”TotalGroup” Checked=”True” Text=”按人员”></asp:RadioButton>
e.Item.Attributes.Add(“onmouseOver”,”this.style.backgroundColor=’#dee3e7′”);//鼠标移上去的颜色
   e.Item.Attributes.Add(“onmouseOut”,”this.style.backgroundColor=’white’”);
   e.Item.Cells[2].Attributes.Add(“onmouseOver”,”this.style.backgroundColor=’red’”);
   e.Item.Cells[2].Attributes.Add(“onmouseOut”,”this.style.backgroundColor=’white’”);//指定某列的颜色
   e.Item.Cells[3].Style["cursor"]=”hand”;
   e.Item.Cells[4].Attributes.Add(“onclick”,”alert(‘你惦记的ID 是:”+e.Item.Cells[4].Text+”‘);”);//指定显示字段
   e.Item.Cells[1].Attributes.Add(“title”,”‘红孩是未解决的,蓝精灵是已解决的!’”+e.Item.Cells[0].Text.ToString());//显示title
   e.Item.Cells[0].Attributes.Add(“onclick”,”window.open(‘xinxiForm.aspx?idmain=”+e.Item.Cells[0].Text+”‘,”,’ToolBar=no,width=260,height=200′);”);


屏闭一些键:


function KeyDown(){   //屏蔽鼠标右键、Ctrl+n、shift+F10、F5刷新、退格键
   //alert(“ASCII代码是:”+event.keyCode);
  if ((window.event.altKey)&&
      ((window.event.keyCode==37)||   //屏蔽 Alt+ 方向键 ←
       (window.event.keyCode==39))){  //屏蔽 Alt+ 方向键 →
     alert(“不准你使用ALT+方向键前进或后退网页!”);
     event.returnValue=false;
     }
  if ((event.keyCode==8)  ||                 //屏蔽退格删除键
      (event.keyCode==116)||                 //屏蔽 F5 刷新键
      (event.keyCode==112)||                 //屏蔽 F1 刷新键
      (event.ctrlKey && event.keyCode==82)){ //Ctrl + R
     event.keyCode=0;
     event.returnValue=false;
     }
  if ((event.ctrlKey)&&(event.keyCode==78))   //屏蔽 Ctrl+n
     event.returnValue=false;
  if ((event.shiftKey)&&(event.keyCode==121)) //屏蔽 shift+F10
     event.returnValue=false;
  if (window.event.srcElement.tagName == “A” && window.event.shiftKey) 
      window.event.returnValue = false;  //屏蔽 shift 加鼠标左键新开一网页
  if ((window.event.altKey)&&(window.event.keyCode==115)){ //屏蔽Alt+F4
      window.showModelessDialog(“about:blank”,”",”dialogWidth:1px;dialogheight:1px”);
      return false;}
}
1. oncontextmenu=”window.event.returnvalue=false”   将彻底屏蔽鼠标右键 
2. <body onselectstart=”return false”>         取消选取、防止复制 
3. onpaste=”return false”               不准粘贴 
4. oncopy=”return false;” oncut=”return false;”    防止复制;防止剪切
5. <link rel=”Shortcut Icon” href=”favicon.ico”> IE地址栏前换成自己的图标 
6. <link rel=”Bookmark” href=”favicon.ico”>     可以在收藏夹中显示出你的图标 
7. <input style=”ime-mode:disabled”>             关闭输入法


8. 永远都会带着框架 
<script language=”javascript”><!– 
 if (window==top)
  top.location.href=”frames.htm”; file://frames.htm为框架网页 
// –></script>


9. 防止被人frame 
<SCRIPT LANGUAGE=javascript><!–  
 if (top.location!=self.location)
  top.location=self.location; 
// –></SCRIPT> 


10. <noscript><iframe src=*.html></iframe></noscript>  网页将不能被另存为


使用鼠标拖动的层
<BODY BGCOLOR=”#FFFFFF”>
<div onmousedown=”style.cursor=’move’;startMove(this)” onmouseup=”style.cursor=’auto’” style=”border:1px solid #AAAAAA;background-color:#EEEEEE;width:300;height:200;position;text-align:center;”>可以使用鼠标拖动</div>
<script language=javascript>
function startMove(objDiv)
{
 document.attachEvent(“onmousemove”,moveDiv);
 document.attachEvent(“onmouseup”,endMove);
 document.attachEvent(“onselectstart”,selectNo);
 document["moveDiv"] = objDiv;
 document["startX"] = event.x;
 document["startY"] = event.y;
 document["oldX"] = objDiv.getBoundingClientRect().left;
 document["oldY"] = objDiv.getBoundingClientRect().top;
}
function moveDiv()
{
 //try{
 var obj = document["moveDiv"];
 if(obj)
 {
  var l = document["oldX"];//obj.getBoundingClientRect().left;//obj.style.left// = 100//(event.x-document["startX"]);
  var t = document["oldY"];//obj.getBoundingClientRect().top;//obj.style.top// = 100//(event.y-document["startY"]);
  obj.style.position = “absolute”;
  obj.style.left = l + (event.x-document["startX"]);
  obj.style.top = t + (event.y-document["startY"]);
 }
 //}catch(e){endMove();}
}
function endMove()
{
 document.detachEvent(“onmousemove”,moveDiv);
 document.detachEvent(“onmouseup”,endMove);
 document.detachEvent(“onselectstart”,selectNo);
 document["moveDiv"] = null;
 document["startX"] = null;
 document["startY"] = null;
}
function selectNo()
{ return false; }
</script>
</BODY>


/* 异步,动态的加载网页xml数据 */
//实际运用有更改
function GetXml(objContainer,id){
 var XmlHttp=new ActiveXObject(“Microsoft.XMLHTTP”)
 objContainer.innerHTML=StateXML(Config.loading)
 objContainer.send=”true”
 XmlHttp.onreadystatechange=function(){
  if(XmlHttp.readyState==4){
   if(XmlHttp.status==200){
   var Xmldoc=XmlHttp.responseXML
    if(Xmldoc.documentElement.hasChildNodes())
    objContainer.innerHTML=Xmldoc.transformNode(xsldoc)
    else
    objContainer.innerHTML=StateXML(Config.loading)
   }
   else
   objContainer.innerHTML=StateXML(Config.unavaible)
  }
 }
 XmlHttp.open(“get”,Config.Service+’?id=’+id+’&tem’+Math.random(),true)
 XmlHttp.send()
}