javascriptでTableの行、列の幅を変更するjavascriptを縦横幅を動的に変更可能なテーブルをjavascriptで作ってみるで作成しました。今度はExtender化してみます。dllもダウンロードできるようにしましたので、ためしに使ってくれるとうれしいです。
動作確認環境は次の通り
- 確認したブラウザIE 7.0
- 開発環境:Visual Studio 2008 Professional (英語版)
- .NETのバージョン3.5
今回,Extenderを作成するのに参考にさせてもらったページ
Web サーバー コントロールへの ASP.NET AJAX クライアント動作の追加
http://msdn.microsoft.com/ja-jp/library/bb386403.aspx
MS AJAX LibでAJAX対応コントロールを開発しよう(後編)
http://www.atmarkit.co.jp/fdotnet/ajaxlib/ajaxlib04/ajaxlib04_01.html
1. プログラムの作成
空のソリューションをTableResizeExtenderという名前で作成し、プロジェクトをプロジェクトのテンプレートにASP.NET AJAX Server Control Extenderを選択して、ComponentGeek.TableResizeExtenderという名前で作成しました。
既定で作成されるファイル名をjavascriptファイルをTableResizeBehavior.js,リソースファイルをTableResizeBehavior.resx,ExtenderをTableResizeExtender.csにリネームして、ファイルを編集します。
TableResizeExtender.csを次のように編集します。リサイズ中のテーブルにスタイルシートを設定できるようにResizingCssClassというプロパティを追加しています。
using System;
using System.Collections.Generic;
using System.Configuration;
using System.Linq;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Web.UI.HtmlControls;
using System.Xml.Linq;
namespace ComponentGeek.TableResizeExtender
{
[TargetControlType(typeof(Control))]
public class TableResizeExtender : ExtenderControl
{
public TableResizeExtender()
{
}
public string ResizingCssClass { get; set; }
protected override IEnumerable<ScriptDescriptor> GetScriptDescriptors(Control targetControl)
{
ScriptBehaviorDescriptor descriptor;
descriptor = new ScriptBehaviorDescriptor("ComponentGeek.TableResizeBehavior", targetControl.ClientID);
if (!string.IsNullOrEmpty(this.ResizingCssClass))
{
descriptor.AddProperty("resizingCssClass", this.ResizingCssClass);
}
return new ScriptBehaviorDescriptor[] { descriptor };
}
protected override IEnumerable<ScriptReference> GetScriptReferences()
{
ScriptReference reference = new ScriptReference("ComponentGeek.TableResizeExtender.TableResizeBehavior.js", this.GetType().Assembly.FullName);
return new ScriptReference[] { reference };
}
}
}
TableResizeBehavior.jsは次のように編集します。中身自体は縦横幅を動的に変更可能なテーブルをjavascriptで作ってみるで作成したないようとほぼ同じでが、対象table要素はIDではなく、要素として保持するように変更しています。また、イベントへの登録方法もMicrosoft AJAXライクな方法に変更しています。今回は_resizingCssClassというメンバを追加して、リサイズ実行時にスタイルシートを設定できるようにしています。
/// <reference name="MicrosoftAjax.js"/>
Type.registerNamespace("ComponentGeek");
ComponentGeek.TableResizeBehavior = function(element){
ComponentGeek.TableResizeBehavior.initializeBase(this, [element]);
this._table; // 対象のテーブルID
this._target; // サイズ変更対象セル
this._startX; // 幅変更開始X座標
this._endX; // 幅変更終了X座標
this._startY; // 高さ変更開始Y座標
this._endY; // 高さ変更開始X座標
this._xResizing = false; // 幅変更中フラグ
this._yResizing = false; // 高さ変更中フラグ
this._resizeThreashold = 8; // 最小変更幅
this._edgeThreshold = 8; // 幅,高さ変更カーソル表示閾値
this._sizeThreshold = 20; // 最小列,幅高さ
this._vBarID = "vBarID"; // 変更時に表示する縦棒のID
this._hBarID = "hBarID"; // 変更時に表示する横棒のID
this._xResizeCursorVisible = false; // 幅の変更カーソル表示フラグ
this._yResizeCursorVisible = false; // 高さの変更カーソル表示フラグ
this._resizingCssClass = ""; // サイズ変更時のCss
this._originalCssClass = "";
}
ComponentGeek.TableResizeBehavior.prototype = {
initialize : ComponentGeek$TableResizeBehavior$initialize,
dispose : ComponentGeek$TableResizeBehavior$dispose,
_onMouseDown : ComponentGeek$TableResizeBehavior$_onMouseDown,
_onMouseUp : ComponentGeek$TableResizeBehavior$_onMouseUp,
_onMouseMove : ComponentGeek$TableResizeBehavior$_onMouseMove,
createBar : ComponentGeek$TableResizeBehavior$createBar,
clear : ComponentGeek$TableResizeBehavior$clear,
getFirstColumn : ComponentGeek$TableResizeBehavior$getFirstColumn,
get_resizingCssClass : ComponentGeek$TableResizeBehavior$get_resizingCssClass,
set_resizingCssClass : ComponentGeek$TableResizeBehavior$set_resizingCssClass
}
function ComponentGeek$TableResizeBehavior$initialize(){
ComponentGeek.TableResizeBehavior.callBaseMethod(this, 'initialize');
// TABLEタグのみサポート
if(this.get_element().tagName == "TABLE") this._table = this.get_element();
else{
var tables = this.get_element().getElementsByTagName("TABLE");
// 一番最初のテーブルを対象とする
if(tables.length > 0) this._table = tables[0];
else return;
}
this._originalCssClass = this._table.className;
$addHandler(this._table, "mousedown", Function.createDelegate(this, this._onMouseDown));
$addHandler(this._table, "mouseup", Function.createDelegate(this, this._onMouseUp));
$addHandler(this._table, "mousemove", Function.createDelegate(this, this._onMouseMove));
this.createBar();
}
function ComponentGeek$TableResizeBehavior$dispose(){
$clearHandlers(this.get_element());
ComponentGeek.TableResizeBehavior.callBaseMethod(this, 'dispose');
}
function ComponentGeek$TableResizeBehavior$get_resizingCssClass(){
return this._resizingCssClass;
}
function ComponentGeek$TableResizeBehavior$set_resizingCssClass(value){
if(this._resizingCssClass !== value)
{
this._resizingCssClass = value;
this.raisePropertyChanged('resizingCssClass');
}
}
function ComponentGeek$TableResizeBehavior$createBar(){
/// <summary>イベント登録メソッド</summary>
if(this._table){
$addHandler(this._table, "mousedown", function(e){ self.MouseDown(e);});
$addHandler(this._table, "mouseup", function(e){ self.MouseUp(e);});
$addHandler(this._table, "mousemove", function(e){ self.MouseMove(e);});
this._table.runtimeStyle.tableLayout = "fixed"; // リサイズ可能にする
}
this.CreateBar();
}
function ComponentGeek$TableResizeBehavior$getFirstColumn(tbl, cellIndex){
///<summary>ヘッダセル取得</summary>
// テーブルの1行目のセルを取得する
var headerCell = tbl.rows(0).cells(cellIndex);
return headerCell;
}
function ComponentGeek$TableResizeBehavior$createBar(){
///<summary>変更バー作成</summary>
// リサイズ時に表示する縦横バー作成
// 横幅変更バー作成
var elem = $get(this._vBarID);
if(!elem){
elem = document.createElement("span");
elem.id = this._vBarID;
elem.style.position = "absolute";
elem.style.top = "0";
elem.style.left = "0";
elem.style.height = "0";
elem.style.width = "2";
elem.style.background = "silver";
elem.style.borderLeft = "1px solid black";
elem.style.display = "none";
document.body.appendChild(elem);
}
// 縦幅変更用バー作成
elem = $get(this._hBarID);
if(!elem){
elem = document.createElement("hr");
elem.id = this._hBarID;
elem.style.position = "absolute";
elem.style.top = "0";
elem.style.left = "0";
elem.style.height = "2";
elem.style.width = "0";
elem.style.background = "silver";
elem.style.borderLeft = "1px solid black";
elem.style.display = "none";
document.body.appendChild(elem);
}
}
function ComponentGeek$TableResizeBehavior$clear(){
// プロパティをクリア
var elem = $get(this._vBarID);
if(elem){
elem.runtimeStyle.display = "none";
}
elem = $get(this._hBarID);
if(elem){
elem.runtimeStyle.display = "none";
}
this._target = null;
this._startX = null;
this._endX = null;
this._startY = null;
this._endY = null;
this._xResizing = false;
this._yResizing = false;
}
function ComponentGeek$TableResizeBehavior$_onMouseDown(e){
// マウスダウンイベント
// 変更カーソル表示中の場合はリサイズ開始
var vBar = $get(this._vBarID);
if(!vBar) return;
var hBar = $get(this._hBarID);
if(!hBar) return;
if(!this._table) return;
if(this._xResizeCursorVisible){
this._target = e.target;
this._startX = e.clientX;
this._xResizing = true;
this._table.setCapture();
var pos = getElementPosition(this._table);
vBar.runtimeStyle.top = pos.top;
vBar.runtimeStyle.left = e.clientX + document.documentElement.scrollLeft;
if(this._table.parentElement.offsetHeight < this._table.offsetHeight){
vBar.runtimeStyle.height = this._table.parentElement.offsetHeight;
}else{
vBar.runtimeStyle.height = this._table.offsetHeight;
}
vBar.runtimeStyle.display = "inline";
this._table.className = this._resizingCssClass;
}else if(this._yResizeCursorVisible){
this._target = e.target;
this._startY = e.clientY;
this._yResizing = true;
this._table.setCapture();
var pos = getElementPosition(this._table);
hBar.runtimeStyle.top = e.clientY + document.documentElement.scrollTop;
//hBar.runtimeStyle.left = this._table.parentElement.offsetLeft + document.documentElement.scrollLeft + document.body.offsetLeft;
hBar.runtimeStyle.left = pos.left;
if(this._table.parentElement.offsetWidth < this._table.offsetWidth){
hBar.runtimeStyle.width = this._table.parentElement.offsetWidth;
}else{
hBar.runtimeStyle.width = this._table.offsetWidth;
}
hBar.runtimeStyle.display = "inline";
this._table.className = this._resizingCssClass;
}
}
function ComponentGeek$TableResizeBehavior$_onMouseMove(e){
// マウス移動イベント
// マウスが列の左端,下端に近づくとカーソル変更
if(this._xResizing || this._yResizing){
var vBar = $get(this._vBarID);
if(!vBar) return;
var hBar = $get(this._hBarID);
if(!hBar) return;
// 変更バー移動
if(this._xResizing){
vBar.runtimeStyle.left = e.clientX + document.documentElement.scrollLeft;
document.selection.empty();
}else if(this._yResizing){
hBar.runtimeStyle.top = e.clientY + document.documentElement.scrollTop;
document.selection.empty();
}
return;
}
if(e.offsetX >= (e.target.offsetWidth - this._edgeThreshold)){
this._xResizeCursorVisible = true;
e.target.runtimeStyle.cursor = "e-resize";
}else if(e.offsetY >= (e.target.offsetHeight - this._edgeThreshold)){
this._yResizeCursorVisible = true;
e.target.runtimeStyle.cursor = "s-resize";
}else{
this._xResizeCursorVisible = false;
this._yResizeCursorVisible = false;
if(e.target.style.cursor){
e.target.runtimeStyle.cursor = e.target.style.cursor;
}else{
e.target.runtimeStyle.cursor = "";
}
}
}
function ComponentGeek$TableResizeBehavior$_onMouseUp(e){
// マウスアップイベント
// サイズ変更中の場合は変更を行う
if(this._target == null) return;
this._endX = e.clientX;
this._endY = e.clientY;
if(!this._table) return;
if(this._xResizing){
// 幅変更中
if(this._startX == null) return;
// 新しい幅
var newWidth = this._target.offsetWidth + (this._endX - this._startX);
// 閾値以上の場合はサイズを適用,そうでない場合、最小幅設定
if(newWidth > this._sizeThreshold){
// ヘッダセル取得
var head = this.getFirstColumn(this._table, this._target.cellIndex);
if(head) head.style.width = newWidth;
}else{
// ヘッダセル取得
var head = this.getFirstColumn(this._table, this._target.cellIndex);
if(head) head.style.width = this._sizeThreshold;
}
}else if(this._yResizing){
// 縦幅変更中
if(this._startY == null) return;
// 新しい高さ
var newHeight = this._target.offsetHeight + (this._endY - this._startY);
var rowSpan = this._target.rowSpan;
for(var i = 0;i<rowSpan;++i){
var row = this._table.rows(this._target.parentElement.rowIndex + i);
for(var j=0;j<row.cells.length;++j){
if((newHeight/rowSpan) > this._sizeThreshold){
row.cells(j).style.height = newHeight/rowSpan;
}else{
row.cells(j).style.height = this._sizeThreshold;
}
}
}
}
this._table.releaseCapture();
this.clear();
this._table.className = this._originalCssClass;
}
ComponentGeek.TableResizeBehavior.registerClass('ComponentGeek.TableResizeBehavior', Sys.UI.Behavior);
if (typeof(Sys) !== 'undefined') Sys.Application.notifyScriptLoaded();
/// 位置指定要素ではない要素の位置を取得する汎用関数
function getElementPosition(elem){
var offsetTrail = elem;
var offsetLeft = 0;
var offsetTop = 0;
while(offsetTrail){
offsetLeft += offsetTrail.offsetLeft;
offsetTop += offsetTrail.offsetTop;
offsetTrail = offsetTrail.offsetParent;
}
if(navigator.userAgent.indexOf("Mac") != -1 &&
typeof document.body.leftMargin != "undefined"){
offsetLeft += document.body.leftMargin;
offsetTop += document.body.topMargin;
}
return {left:offsetLeft, top:offsetTop};
}
TableResizeBehavior.resxは編集対象ではないので、そのままにして、最後に、AssemblyInfo.cs(プロジェクトのPropertiesを展開すると表示されます)の末尾に既定で作成される、WebResourceアトリビュートを編集して完了です。
[assembly: WebResource("ComponentGeek.TableResizeExtender.TableResizeBehavior.js", "text/javascript")]
[assembly: ScriptResource("ComponentGeek.TableResizeExtender.TableResizeBehavior.js",
"ComponentGeek.TableResizeExtender.TableResizeBehavior", "ComponentGeek.TableResizeExtender.Resource")]
ビルドしてエラーが発生しなければOKです。
2. Extenderを使う
WebSiteプロジェクトを作成して、作成したExtenderプロジェクトを参照に追加すれば、aspxファイル編集時にToolboxにTableResizeExtenderが表示されるので、それをTableやGridViewに付与すればOKです。以下に作成例(Default.aspx)を示します。例ではGridViewとTableに対してTableResizeExtenderを設定しています。
<%@ Page Language="C#" AutoEventWireup="true" CodeFile="Default.aspx.cs" Inherits="_Default" %>
<%@ Register Assembly="ComponentGeek.TableResizeExtender" Namespace="ComponentGeek.TableResizeExtender"
TagPrefix="cc1" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
<title>Untitled Page</title>
<style type="text/css">
.Resizing
{
background-color: Gray;
}
</style>
</head>
<body>
<form id="form1" runat="server">
<asp:ScriptManager runat="server" ID="ScriptManager1" />
<div>
<asp:Panel runat="server" ID="Panel1">
<asp:Table ID="Table2" runat="server" BorderWidth="2px">
<asp:TableRow ID="TableRow1" runat="server">
<asp:TableCell ID="TableCell1" runat="server">ヘッダ1</asp:TableCell>
<asp:TableCell ID="TableCell2" runat="server">ヘッダ2</asp:TableCell>
<asp:TableCell ID="TableCell3" runat="server">ヘッダ3</asp:TableCell>
</asp:TableRow>
<asp:TableRow ID="TableRow2" runat="server">
<asp:TableCell ID="TableCell4" runat="server">セル1</asp:TableCell>
<asp:TableCell ID="TableCell5" runat="server">セル2</asp:TableCell>
<asp:TableCell ID="TableCell6" runat="server">セル3</asp:TableCell>
</asp:TableRow>
<asp:TableRow ID="TableRow3" runat="server">
<asp:TableCell ID="TableCell7" runat="server">セル4</asp:TableCell>
<asp:TableCell ID="TableCell8" runat="server">セル5</asp:TableCell>
<asp:TableCell ID="TableCell9" runat="server">セル6</asp:TableCell>
</asp:TableRow>
</asp:Table>
</asp:Panel>
<cc1:TableResizeExtender ID="TableResizeExtender2" runat="server" TargetControlID="Table2" />
<%-- <cc1:TableResizeExtender ID="TableResizeExtender2" runat="server" TargetControlID="Panel1" /> -->
</div>
<asp:GridView ID="GridView1" runat="server">
</asp:GridView>
<cc1:TableResizeExtender ID="TableResizeExtender1" runat="server" TargetControlID="GridView1"
ResizingCssClass="Resizing" />
</form>
</body>
</html
説明は以上です。問題点、指摘点、疑問点とうございましたら、連絡ください。
3. おみやげ
ソリューションの内容は掲載したまんまなのでdllだけダウンロードできます。こちらからダウンロードして下さいませ。