模拟Windows Listview的HTC组件

本文属spanzhang原创,其blog地址为:http://blog.youkuaiyun.com/spanzhang。引用或转贴请注明出处,谢谢!!
在开发B/S应用时,经常需要以列表的形式显示数据,但HTML的TABLE却显得很不够用。这里给出的是我自己的Listview模拟控件,它主要有如下几个特点:
1、Header和Columns都可以灵活配置;
2、Columns可以拖动;
3、支持单选和多选模式;
4、支持鼠标双击(引发事件)。

效果图如下:

HTC组件源代码如下(htc_listView.htc):
<!--

作者:张友邦
时间:2004-12-01
描述:表格控件

-->

<!--
接口定义
-->
<public:component>
<public:property name="description"    value="Span ListView Behavior" />
<public:property name="version"     value="1.0.0.0" />

<public:attach  event="oncontentready"   onevent="init()"   />
<public:attach  event="onselectstart"   onevent="cancelSelect()" />
<public:attach  event="onresize"    onevent="resize()"   />
<public:attach  event="onmousemove"    onevent="mouseMove()"  />
<public:attach  event="onmouseup"    onevent="mouseUp()"   />

<public:property name="headImage"    value="/spanClient/htc_listView.htc.res/headerBg.gif"  />
<public:property name="splitterImage"   value="/spanClient/htc_listView.htc.res/headerSplit.gif" />
<public:property name="defStyle1"    value="background-color: #DADFF1" />
<public:property name="defStyle2"    value="background-color: #B9C1DD" />
<public:property name="defStyle11"    value="background-color: #000000; color: #FFFFFF" />
<public:property name="defStyle22"    value="background-color: #000064; color: #FFFFFF" />
<public:property name="singleSelect"    value="true" />
<public:property name="freezeCols"    value="false" />

<public:property name="header"  />
<public:property name="columns"  />
<public:property name="dataTable" />
<public:property name="rows"   />
<public:property name="selectedRows" />

<public:method  name="addColumn" />
<public:method  name="addRow"  />
<public:method  name="delRow"  />
<public:method  name="selectRow" />
<public:method  name="clearSelect" />

<public:event  name="onSelChange"    id="selChange" />
<public:event  name="onWantEdit"    id="wantEdit" />
<public:event  name="onHeadClick"    id="headClick" />
</public:component>

<!--
组件实现
-->
<script language="javascript">
var headerWidth = 0//表头宽度
var bodySpan = null//体对象
var downType = -1//鼠标点下去的类型,点在不同地方会有不同的类型
var objSizeItem = null//拖动线

//列对象
function column(colHeader, splitter, style1, style2, style11, style22)
{
this.colHeader =
colHeader;
this.splitter =
splitter;

this.style1 = style1; //第一种未选中样式

if (this.style1 == null) this.style1 = defStyle1;
this.style2 = style2; //第二种未选中样式

if (this.style2 == null) this.style2 = defStyle2;
this.style11 = style11; //第一种选中状态样式

if (this.style11 == null) this.style11 = defStyle11;
this.style22 = style22; //第一种选中状态样式

if (this.style22 == null) this.style22 = defStyle22;
}

//内部函数,事件oncontentready,初始化

function init()
{
var rs = element.children[0
].recordset;

//基本属性

columns   = new Array();
selectedRows
= new
Array();
element.style.overflow
= "hidden"
;

//构造表头

header = document.createElement("SPAN");
with
(header.style)
{
  width 
= "2048"
;
  height
= 20
;
  backgroundImage
= "url(" + headImage + ")"
;
  cursor
= "default"
;
}
element.insertAdjacentElement(
"beforeEnd"
, header);

//构造BODY

bodySpan = document.createElement("SPAN");
with
(bodySpan.style)
{
  overflow
= "auto"
;
  cursor
= "default"
;
}
bodySpan.attachEvent(
"onscroll"
, bodyScroll);
element.insertAdjacentElement(
"beforeEnd"
, bodySpan);

dataTable
= document.createElement("TABLE"
);
with
(dataTable)
{
  cellSpacing
= 0
;
  cellPadding
= 0
;
  width
= 1
;
  borderColor
= "#305D03"
;
}
bodySpan.insertAdjacentElement(
"beforeEnd"
, dataTable);
rows
=
dataTable.rows;

//拖动线

if (freezeCols == 'false')
{
  objSizeItem
= window.document.createElement("DIV"
) ;
 
with
(objSizeItem.style)
  {
   backgroundColor
= "black"
;
   cursor
= "col-resize"
;
   position
= "absolute"
;
   border
= "none";//"solid 0px" ;

   width = "1px" ;
   zIndex
= 3000
;
   visibility
= "hidden"
;
  }
  window.document.body.insertAdjacentElement(
"beforeEnd"
, objSizeItem);
}

//插入所有的列

var style1, style2, style11, style22;
for (var i = 0; rs != null && !rs.EOF; ++
i)
{
 
try {style1 = new String(rs('style1'));} catch(e) {style1 =
defStyle1;}
 
if (style1 == 'null') style1 =
defStyle1;
 
try {style2 = new String(rs('style2'));} catch(e) {style2 =
defStyle2;}
 
if (style2 == 'null') style2 =
defStyle2;
 
try {style11 = new String(rs('style11'));} catch(e) {style11 =
defStyle11;}
 
if (style11 == 'null') style11 =
defStyle11;
 
try {style22 = new String(rs('style22'));} catch(e) {style22 =
defStyle22;}
 
if (style22 == 'null') style22 =
defStyle22;

  addColumn(rs('width'), rs('text'), style1, style2, style11, style22);

  rs.moveNext();
}

//调整布局

resize();
}

//方法,创建列

function addColumn(colWidth, colContent, style1, style2, style11, style22)
{
var colIndex =
columns.length;
columns[colIndex]
= new
column
(
  document.createElement(
"SPAN"
),
  document.createElement(
"SPAN"
),
  style1,  style2,
  style11, style22
);

//列标题栏

columns[colIndex].colHeader.innerHTML = colContent;
with
(columns[colIndex].colHeader.style)
{
  width
=
colWidth;
  height
= 20
;
  textAlign
= "center"
;
  backgroundImage
= "url(" + headImage + ")"
;
  overflow
=
'hidden';
  headerWidth
+= parseInt(colWidth) + 4
;
}
columns[colIndex].colHeader.onclick
= new Function("var eventObject = createEventObject();eventObject.colIndex = "

 
+ colIndex + ";headClick.fire(eventObject);"
);
header.insertAdjacentElement(
"beforeEnd"
, columns[colIndex].colHeader);

//分隔条

with (columns[colIndex].splitter)
{
  position
= "absolute"
;
 
if (freezeCols == 'false
')
   onmousedown
= new Function("splitterDown(" + colIndex + ")"
);
}
with
(columns[colIndex].splitter.style)
{
  left
= headerWidth - 4
;
  width
= 4
;
  height
= 20
;
 
if (freezeCols == 'false
')
   cursor
= "col-resize"
;
  backgroundImage
= "url(" + splitterImage + ")"
;
}
header.insertAdjacentElement(
"beforeEnd"
, columns[colIndex].splitter);

//调整dataTable的宽度

dataTable.width = headerWidth - 2;
}

//内部函数,事件onresize

function resize()
{
with
(bodySpan.style)
{
  width
=
parseInt(element.clientWidth);
  height
= parseInt(element.clientHeight) - 20
;
}
}

//内部函数,体滚动事件onscroll

function bodyScroll()
{
header.style.marginLeft
= (-
window.event.srcElement.scrollLeft) ;
}

//取消选择

function cancelSelect()
{
with
(window.event)
{
  cancelBubble
= true
;
  returnValue
= false
;
}
return false
;
}

var downSpliterIndex = -1
;
var
eventX1;
var
eventX2;

//内部函数,分隔条点下

function splitterDown(index)
{
with
(window.event)
{
  eventX1
=
parseInt(clientX);
  cancelBubble
= true
;
  returnValue
= false
;
}
element.setCapture();

downSpliterIndex
=
index;
downType
= 1
;
with
(objSizeItem.style)
{
  top
=
element.offsetTop;
  height
=
element.offsetHeight;
  posLeft
= window.event.clientX - 3
;
  visibility
= "visible"
;
}
}

//内部函数,事件onmousemove

function mouseMove()
{
switch (true
)
{
 
case downType == 1
:
   objSizeItem.style.posLeft
= window.event.clientX - 2
;
 
break
;

 
default
:
 
break
;
}
}

//内部函数,事件onmouseup

function mouseUp()
{
if (downType == -1
)
 
return
;
 
with
(window.event)
{
  eventX2
=
parseInt(clientX);
  cancelBubble
= true
;
  returnValue
= false
;
}

switch (true
)
{
 
case downType == 1
:
  
var dx = eventX2 -
eventX1;
  
var colWidth =
parseInt(columns[downSpliterIndex].colHeader.style.width);
  
if (colWidth + dx < 1) dx = 1-
colWidth;
   colWidth
+=
dx;
   columns[downSpliterIndex].colHeader.style.width
=
colWidth;
   dataTable.width
= parseInt(dataTable.width) +
dx;

  
for (var i = 0; i < rows.length; ++
i)
   {
   
//setRowCss(rows[i]);

    for (var j = 0; j < columns.length; ++j)
    {
    
if (j == 0
)
      rows[i].children[j].style.width
= parseInt(columns[j].colHeader.style.width) + 2
;
    
else

      rows[i].children[j].style.width
= parseInt(columns[j].colHeader.style.width) + 4;
    }
   }

   setColWidth();

   header.style.marginLeft
= (-
bodySpan.scrollLeft);
 
break
;

 
default
:
 
break
;
}

element.releaseCapture();
objSizeItem.style.visibility
= "hidden"
;
downType
= -1
;
}

//行的事件:click

function clickRow(rowIndex)
{
if (!window.event.ctrlKey || singleSelect == 'true
')
{
 
for (var i = 0; i < selectedRows.length; ++
i)
  {
  
if (selectedRows[i].rowIndex >= 0
)
    selectRow(selectedRows[i].rowIndex,
false
);
  }
  selectedRows.length
= 0
;
}

if (rowIndex >= 0
)
  selectRow(rowIndex,
!
rows[rowIndex].selected);
}

//内部函数,设置一行选种样式

function setRowCss(row, selected)
{
if (selected == null
)
  selected
=
row.selected;
if (selected == null
)
  selected
= false
;

if (!
selected)
{
 
if (row.rowIndex % 2 == 0
)
  {
  
for (var i = 0; i < row.children.length; ++
i)
    row.children[i].style.cssText
=
columns[i].style1;
  }
 
else

  {
  
for (var i = 0; i < row.children.length; ++i)
    row.children[i].style.cssText
=
columns[i].style2;
  }
}
else

{
 
if (row.rowIndex % 2 == 0)
  {
  
for (var i = 0; i < row.children.length; ++
i)
    row.children[i].style.cssText
=
columns[i].style11;
  }
 
else

  {
  
for (var i = 0; i < row.children.length; ++i)
    row.children[i].style.cssText
=
columns[i].style22;
  }
}

for (var i = 0; i < row.children.length; ++
i)
{
 
if (i == 0
)
   row.children[i].style.width
= parseInt(columns[i].colHeader.style.width) + 2
;
 
else

   row.children[i].style.width
= parseInt(columns[i].colHeader.style.width) + 4;
}

row.selected
=
selected
}

//方法实现,设置某一行的选种状态

function selectRow(rowIndex, selected)
{
var row =
rows[rowIndex];
if (row == null
)
 
return
row;

if
(selected)
{
  setRowCss(row,
true
);

 
if (selectedRows.length > 0 && selectedRows[selectedRows.length - 1].rowIndex ==
rowIndex);
 
else

  {
   selectedRows[selectedRows.length]
= row;
   selChange.fire();
  }
}
else

{
  setRowCss(row,
false);
}

return
row;
}

//方法实现

function clearSelect()
{
clickRow(
-1
);
}

//内部函数

function setColWidth()
{
if (rows.length <= 0
)
 
return
;
 
var tableWidth = 0
;
for (var i = 0; i < columns.length; ++
i)
{
 
if (i == 0
)
   columns[i].colHeader.style.width
= rows[0].children[i].clientWidth - 2
;
 
else

   columns[i].colHeader.style.width
= rows[0].children[i].clientWidth - 4;

  tableWidth
+= parseInt(columns[i].colHeader.style.width) + 4
;
}
dataTable.width
= tableWidth - 2
;
}

//方法实现

function addRow(rowData)
{
var row =
dataTable.insertRow();
row.onmousedown
= new Function("clickRow(" + row.rowIndex + ")"
);
row.ondblclick
= new Function("var eventObject = createEventObject();eventObject.rowIndex = "

 
+ row.rowIndex + ";wantEdit.fire(eventObject);"
);

for (var i = 0; i < rowData.length; ++
i)
{
 
var td =
row.insertCell();
  td.innerHTML
=
rowData[i];
}
setRowCss(row,
false
);

//setColWidth();


return
row;
}

Array.prototype.remove
= function
(dx)
{
if(isNaN(dx)||dx>this.length){return false
;}
this.splice(dx, 1
);
}

//方法实现

function delRow(rowIndex)
{
dataTable.deleteRow(rowIndex);

//重新格式化

for (var i = 0; i < rows.length; ++i)
{
  rows[i].onmousedown
= new Function("clickRow(" + i + ")"
);
  rows[i].ondblclick
= new Function("wantEdit.fire(" + i + ")"
);
  setRowCss(rows[i],
false
);
}

//如果删除了已经选中的

var k = -1;
for (var i = 0; i < selectedRows.length; ++
i)
{
 
if (selectedRows[i].rowIndex == -1
)
   k
=
i;
 
else

   setRowCss(selectedRows[i],
true);
}
if (k >= 0
)
  selectedRows.remove(k);
}
</script>

测试文件(test_htc_listView.aspx)如下:
<%@ Page language="c#" %>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" >
<HTML>
<HEAD>
 
<title>test_htc_listView</title>
 
<meta name="GENERATOR" Content="Microsoft Visual Studio .NET 7.1">
 
<meta name="CODE_LANGUAGE" Content="C#">
 
<meta name="vs_defaultClientScript" content="JavaScript">
 
<meta name="vs_targetSchema" content="http://schemas.microsoft.com/intellisense/ie5">
 
<script>
   
function init()
    {
  lv1.addColumn
  (
  
200
,
  
"Added"
,
  
"color:green;BORDER-BOTTOM: green 1px dashed;BACKGROUND-COLOR: #EED881"
,
  
"color:green;BORDER-BOTTOM: green 1px dashed;BACKGROUND-COLOR: #EFE6C1"
,
  
"color:white;BORDER-BOTTOM: green 1px dashed;BACKGROUND-COLOR: black"
,
  
"color:white;BORDER-BOTTOM: green 1px dashed;BACKGROUND-COLOR: black"

  );
    }
   
   
function addData()
    {
 
var data = new
Array();
  data[
1] = "kasdkf<br>kaskd"
;
  data[
2] = "Liuk<br>as"
;
  data[
3] = "com<br>asss"
;
  data[
4] = "FF"
;
  data[
5] = "gaGG"
;

 
for (var i = 0; i < 5; ++
i)
  {
   data[
0] = 'A ' + i +
'';
   lv1.addRow(data);
  }

  data[
1] = "HH"
;
  data[
2] = "FF"
;
  data[
3] = '<span style="overflow:hidden">KKKKKSSSS呵呵</span>
';
  data[
4] = "rr"
;
  data[
5] = "kk"
;
 
for (var i = 0; i < 5; ++
i)
  {
   data[
0] = '<span style="width:100%;height:100%;text-align:center">G ' + i + '</span>
';
  
var row =
lv1.addRow(data);
  }
 
  lv1.clearSelect();
  lv1.selectRow(
0, true
);
    }

   
function
delCur()
    {
 
var oldSel = lv1.selectedRows[0
].rowIndex;
 
while (lv1.selectedRows.length > 0
)
   lv1.delRow(lv1.selectedRows[
0
].rowIndex);
  lv1.selectRow(oldSel,
true
);
    }
   
   
function
wantEdit()
    {
  alert(event.rowIndex
+ "行想被编辑!"
);
    }

   
function
headClick()
    {
  alert(
"用户点击了第“" + event.colIndex + "”列,你可以排序,也可以什么都不干!"
);
    }
 
</script>

</HEAD>
<body MS_POSITIONING="GridLayout" bgcolor="menu" style="FONT-SIZE: 9pt; OVERFLOW: hidden"
  onload
="init();">
 
<span id="lv1" style="BORDER-RIGHT: thin inset; BORDER-TOP: thin inset; LEFT: 0px; BEHAVIOR: url(/spanClient/htc_listView.htc); BORDER-LEFT: thin inset; WIDTH: 100%; BORDER-BOTTOM: thin inset; TOP: 0px; HEIGHT: 80%; BACKGROUND-COLOR: white"
   singleSelect
='false' onwantedit="wantEdit()" onheadclick="headClick()" freezeCols='false'>
  
<xml>

   
<columns>
    
<column width="50" text="col1" style1="background-color: #DADFF1;" style2="background-color: #B9C1DD;"
      style11
="background-color: #DADFF1;" style22="background-color: #B9C1DD;" />
    
<column width="100" text="col2" style1="background-color: green;" style2="background-color: green;" />
    
<column width="100" text="col3" />
    
<column width="100" text="col4" />
    
<column width="100" text="col5" />
   
</columns>
  
</xml>
 
</span>
 
<br>
 
<br>
 
<input type="button" value="增加" style="WIDTH:60px;HEIGHT:23px" onclick="addData();">
 
&nbsp;&nbsp;&nbsp;&nbsp; <input type="button" value="删除" style="WIDTH:60px;HEIGHT:23px" onclick="delCur();">
</body>
</HTML>

另外,还有两张图片:                      
 headerBg.gif:以及headerSplit.gif:。   
尽管这样反白,也不知道你能不能看清楚。      

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值