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だけダウンロードできます。こちらからダウンロードして下さいませ。
さんのコメント: さんのコメント: