【ASPNET】ASP.NET VB GridViewのヘッダーに行を追加する(修正版)

ASP.NET VB GridViewのヘッダーに行を追加する際に新たなことが分かったので、追加でシステム開発備忘録します。

以前にASP.NET VB GridViewのヘッダーに行を追加するという記事をシステム開発備忘録しました。
その際に、PostBackでおかしくなる旨の記述もしていたのですが、今回の修正で治るかもしれません。

前回の追加コーディングは。以下の通りです。

Private Sub GridView1_RowDataBound(sender As Object, e As GridViewRowEventArgs) Handles GridView1.RowDataBound
 
If e.Row.RowType = DataControlRowType.Header Then
  Dim row As GridViewRow = New GridViewRow(-1, -1, DataControlRowType.Header, DataControlRowState.Normal)
  For i As Integer = 0 To 11
    Dim cell1 As TableCell = New TableCell()
    cell1.Text = i.tostring + "月"
    row.Cells.Add(cell1)
  Next
  row.RowType = DataControlRowType.Header
  Me.GridView1.Controls(0).Controls.AddAt(1, row)
endif

今回、GridViewにtheadタグ、tbodyタグを追加したいと思い以下のコーディングを追加しました。

Private Sub GridView1_RowCreated(sender As Object, e As GridViewRowEventArgs) Handles GridView1.RowCreated

  Select Case (e.Row.RowType)
    Case DataControlRowType.Header
      e.Row.TableSection = TableRowSection.TableHeader  'theadを出力
    Case DataControlRowType.DataRow
      e.Row.TableSection = TableRowSection.TableBody    'tbodyを出力
    Case DataControlRowType.Footer
      e.Row.TableSection = TableRowSection.TableFooter  'tfootを出力
  End Select
End Sub

このコーディングを追加した結果、上で追加したレコードはTheadに入らずに、Tbodyに入っていました。
環境が異なるので今回は検証をしていませんので、あくまでも推測ですが、先回のPostBack時の不具合はこのことが原因と考えられます。
適切にthead部に入ってヘッダー行と認識されて居れば、PostBack時の処理で通常のレコードと見なされることは無いのではないかと思います。

そこで今回の結果を踏まえ、もう少し突っ込んで調べた結果、
TableSection
なるプロパティが有ることがわかりました。
TableSectionプロパティは、Table コントロールに配置されている TableRow オブジェクトの場所を取得または設定するのに使用されます。
使用できるテーブルの位置については、以下の通りです。

テーブル行セクション 表示モード
TableHeader ヘッダー行
TableBody テーブルの本体
TableFooter フッター行

TableSectionプロパティを設定する箇所は適当で良いと思いますが、私は直線に入れました。

row.TableSection = System.Web.UI.WebControls.TableRowSection.TableHeader
Me.GridView1.Controls(0).Controls.AddAt(1, row)

TableSection プロパティをTableHeaderに設定してヘッダーレコードを追加すると、正常にthead部に入ってくれました。

以上、ASP.NET VB GridViewのヘッダーに行を追加する(修正版)のシステム開発備忘録でした。

【ASP.NET】Grid_Viewのヘッダーなどをコピーして使用する際の注意

javascriptやjqueryを利用して、テーブルのヘッダーを固定させる時に、少し嵌ったのでシステム開発備忘録として残しておきます。

経緯はGrid_Viewのヘッダーを固定するプログラムをjqueryで書きました。
Grid_Viewのヘッダーは段組みがされて居て、その中にtableを使用している少し複雑なヘッダーでした。

丸っとコピーして使用しようとしたのですが、どうしてもズレてしまいます。
Grid_Viewのヘッダーをコピーを幾度か繰り返すプログラムを書いたところ、1回目と2回目でthのwidthに差が出ていることがわかりました。

これが何を意味するのか、良く分かりません。
一通り読込んだ後で、何らかの調整が入り差が出ているのかなぁ?程度で。。。
$(document).readyイベントでプログラムを書いていますので、DOMツリーの構築が終わった時点で実行されます。
DOMツリーの構築とは、ページ内容の解析とレンダリングが終わったと言うことらしいです。
では、レンダリングとは。。。

まずは、読み込んだHTMLを解析してDOMツリーを生成します。
構築と生成の違いも今一つわりませんが。。。要素を作ったと思って良いのかな?

次に、読み込んだCSSを解析してCSSの構造体を生成します。

更に、DOMツリーとCSSの構造体から画面表示に必要なレンダーツリー(Render Tree)を構築します。

その後、レンダーツリーが持つ各DOM要素の位置を決定します。レイアウトって言って良いのかな?

最後に、レイアウトに基づいて描画されます。

先ほど、$(document).readyイベントは「DOMツリーの構築が終わった時点で実行されます」と呪文のような言葉を知ってるだけで、DOMツリーの構築を良く知りません。
ググってみると次のようなことが書いてあるページも。。。『そこで利用するのがreadyイベントです。ポイントはページ全体(画像などを含む)の読込ではなく、ページ構成(DOM要素)を読み込んだ時点で処理するため、ページを表示する前に様々な準備ができる点です。』
と言うことは、DOMツリーの構築は『ページを表示する前に様々な準備ができる点』と有るように、レイアウトを行う前までのような。。。

だとすると、GridViewはtable-layoutをautoにしているため、読込み後に調整が入ったんだろうと思って居ましたがそれで良いのかも。
ただ実際のブラウザがどのように調整をしているかを知らないため、良く分からないのですが、、、

で、差が出るのを読込み後に調整が入ったんだろうと思いつつ、なぜズレるかわからないまま、調整をしながらデバッグしていたところ、Grid_Viewのcellpaddingが‐1に設定されているを発見しました。
実際、このGridViewのcellpaddingを0にしたところ、差がかなりなくなりました。
若干の誤差は出ているものの、妥協できる程度の差でした。

javascriptやjqueryを利用して、GridViewのヘッダーを固定させる時は、コピー先のテーブルとGridViewのcellpaddingが合致しているかをチェックしてみる価値は有るような気がします。
cellpaddingを‐1にすると、cellpaddingが追加されず、cellpaddingを0にするとcellpadding=0がテーブルタグに追加されます。

以上、javascriptやjqueryを利用して、テーブルのヘッダーを固定させる時に、少し嵌ったシステム開発備忘録でした。

【ASP.NET】ASP.NET 2015で空のtrが勝手に消される不具合

ASP.NET 2015の不具合と確定していないのですが、ASP.NET 2015で空のtrが勝手に消される不具合に遭遇しました。
なので一応、システム開発備忘録しておきます。

具体的な不可解な現象を引き起こしたコードは以下の通りです。

<table>
  <thead>
    <tr><tr>
  <thead>
<table>

  ↓ ※確実にセーブしてもどのタイミングかわかりませんが、以下のようになります。

<table>
  <thead>
  <thead>
<table>

空のtrが不要なタグと判断されたのかな?
実際、今までこのようなケースが無かったので、ASP.NET 2015の不具合かどうかもわかりません。
が、数回同じ現象が起きたので、条件によって引き起こされているのかな?

今回は下のような、動的にth要素をコピーするプログラムを書いていたので、

$(this).clone(true).appendTo($("#tableH > thead > tr");

空のtr(td要素を持たない)をhtmlに書き込んでいました。
静的なhtmlでは空のtr(td要素を持たない)は意味ないですからね。。。

で不具合の対策は、いろいろ考えるのは面倒なので、次のように変更しました。

$("#tableH > thead").append("<tr></tr>");
$(this).clone(true).appendTo($("#tableH > thead > tr");

今回は、空のtr(td要素を持たない)が消えてなくなる(削除される?)とは思って居なかったので、不具合を探すのに少し戸惑いました。
こんなケースも有るんだなぁ・・・程度の備忘録です。

以上、ASP.NET 2015で空のtrが勝手に消される不具合報告でした。

【css,jquery】htmlテーブルのヘッダーを固定する方法案

テーブルヘッダーと左端を固定して縦スクロール横スクロールに対応させる方法

テーブルヘッダーを縦スクロール及び横スクロールともに固定する方法のシステム開発備忘録です。

一つ前のシステム備忘録でCSSのみ(幅を動的に取得するためにはjQueryが必要)でテーブルヘッダーを縦スクロールする方法を投稿しましたが、今回は横スクロールにも対応するシステム開発備忘録です。

テーブルのヘッダーを固定する要望は多いと思いますが、横スクロールをするためにはcssだけでは辛いですので、jQueryを使って、面倒なプログラミングをなるべく簡単にする案です。

テーブルヘッダーを固定する案はいろいろ考案できるので、その中の一案として考えています。
今回は、事前に用意するのは、大枠となるdivタグと子要素として固定部table、水平ヘッダー部div,table、垂直ヘッダー部div,table、データ部div,tableです。
htmlを貼っておくので詳細はそこで確認してください。

イメージとしては、下の表の配置です。
データ部のdivのスクロールバーの移動量を水平ヘッダー部div、垂直ヘッダー部divに反映させ、ヘッダーを固定するイメージです。

固定部水平ヘッダー部
垂直ヘッダー部データ部

また、テーブルは通常のテーブルをデータ部に作成します。
id=”tableD”としています。
jqueryでは、この普通に作られたテーブルのヘッダー部を水平ヘッダー部にコピーします。
また、テーブルの一番左端の列を垂直ヘッダー部にコピーします。
コピーされたヘッダーと一番左端の列は非表示にしています。

あとはスクロールイベントを捉えて、移動量を反映させる単純な仕組みです。

尚、厄介なのはテーブルの幅がブラウザで自動調整されていると微妙に誤差ができ、ずれが生じます。罫線とかちゃんと幅を計算してないのも原因かも……

きっちり計算すればずれは発生しないのかもしれませんが、ブラウザーの種類やバージョンの違いでも誤差が発生する可能性が有るので、この辺りは使用する環境で使用できない案になる可能性も有ります。

まぁ、実際のプログラムに使用する場合はもう少し考慮が必要な部分も有りますので、あくまでも参考までに。

htmlは以下の通りです。

<html>
<head>
  <script src="http://code.jquery.com/jquery-latest.min.js"></script>
  <style type="text/css">
  /* 基本のテーブル定義 */
  #tableT, #tableH, #tableV, #tableD {
    border: 1px solid #000000;
    border-collapse: collapse;
    table-layout: fixed;
    font-size: 16px;
  }

  #tableD td {
    border: 1px solid #000000;
    height: 16px;
  }

  #tableH th, #tableD th {
    border: 1px solid #000000;
    font-size: 16px;
  }

  #tableD th {
    background-color: #FFBB88;
    color: #000000;
  }

  #tableD tr:nth-child(odd) td {
    background-color: #C8C8E8;
    color: #000000;
  }

  #tableD tr:nth-child(even) td {
    background-color: #E8E8FF;
    color: #000000;
  }

  #header_h {
    position: absolute;
    top: 0px;
    overflow-x: hidden;
    overflow-y: hidden;
  }

  #header_v {
    position: absolute;
    left: 0px;
    overflow-x: hidden;
    overflow-y: hidden;
  }

  #data {
    position: absolute;
    overflow-x: scroll;
    overflow-y: scroll;
    width: 270px;
    height: 150px;
  }
</style>
<script type="text/javascript">
  $(document).ready(function () {
    //固定部を作成、tableDの一番左のヘッダーを取得、固定部に張り付ける 
    //水平ヘッダー部を作成
    var fF=0;
    $("#tableD > thead > tr > th").each(function (i) {
      if (fF == 0) {
        $(this).clone(true).appendTo($("#tableT > thead > tr"));
        StyleCopyB($("#tableT > thead > tr > th:first"), $(this));
        fF = 1; 
      } else {
        $(this).clone(true).appendTo($("#tableH > thead > tr"));
        StyleCopyB($("#tableH > thead > tr > th:last"), $(this));
      }
    });

    //非表示化
    $("#tableD > thead > tr").hide();

    //垂直ヘッダー部を作成
    $("#tableD > tbody > tr").each(function (i) {
      $("#tableV > tbody").append("<tr></tr>");
      $(this).children().eq(0).clone(true).appendTo($("#tableV > tbody > tr:last-child"));
      $(this).children().eq(0).hide();
      StyleCopyB($("#tableV > tbody > tr:last-child > td"), $(this).children().eq(0));
    });

    //垂直ヘッダーのwidthを固定部に設定 
    StyleCopyA($("#tableT > thead > tr > th:first"),$("#tableV > tbody > tr:first > td"));

    //データ部のwidthを水平ヘッダーに設定
    $("#tableH > thead > tr > th").each(function (i) {
      $(this).width($("#tableD > tbody > tr").children().eq(i+1).width());
    });

    //DIVheader-hとDIVdataのleftを設定
    $("#header_h").css("left", $("#tableT").width() + "px");
    $("#data").css("left", $("#tableT").width() + "px");

    //DIVheader-vとDIVdataのtopを設定
    $("#header_v").css("top", $("#tableT").height() + "px");
    $("#data").css("top", $("#tableT").height() + "px");

    //
    $("#header_h").width($("#data").width()-17);//17前後要微調整、垂直スクロールバーの幅....※求め方は不明
    $("#tableH").width($("#tableD").width() - $("#tableT").width());
 
    //DIVheader-vのheightをDIVdataのheightを設定
    $("#header_v").height($("#data").height() - 17);//17前後要微調整は水平スクロールバーの高さ....※17の求め方は不明

    //-------------------------------------------------------------------------------------------------
    $("#data").on( 'scroll', function () {
      $("#header_h").scrollLeft($("#data").scrollLeft()); // データ部のスクロールをヘッダに反映 
      $("#header_v").scrollTop($("#data").scrollTop());   // データ部のスクロールをヘッダに反映
    });
    //-------------------------------------------------------------------------------------------------

    function StyleCopyA($copyTo, $copyFrom) {
      $copyTo.css("width", $copyFrom.css("width"));
    }

    function StyleCopyB($copyTo, $copyFrom) {
      $copyTo.css("height", $copyFrom.css("height"));
      $copyTo.css("font-size", $copyFrom.css("font-size"));
      $copyTo.css("background-color", $copyFrom.css("background-color"));
      $copyTo.css("vertical-align", $copyFrom.css("vertical-align"));
      $copyTo.css("border-top-width", $copyFrom.css("border-top-width"));
      $copyTo.css("border-top-color", $copyFrom.css("border-top-color"));
      $copyTo.css("border-top-style", $copyFrom.css("border-top-style"));
      $copyTo.css("border-left-width", $copyFrom.css("border-left-width"));
      $copyTo.css("border-left-color", $copyFrom.css("border-left-color"));
      $copyTo.css("border-left-style", $copyFrom.css("border-left-style"));
      $copyTo.css("border-right-width", $copyFrom.css("border-right-width"));
      $copyTo.css("border-right-color", $copyFrom.css("border-right-color"));
      $copyTo.css("border-right-style", $copyFrom.css("border-right-style"));
      $copyTo.css("border-bottom-width", $copyFrom.css("border-bottom-width"));
      $copyTo.css("border-bottom-color", $copyFrom.css("border-bottom-color"));
      $copyTo.css("border-bottom-style", $copyFrom.css("border-bottom-style"));
    }
  });
</script>
</head>

<body>
  <div style="position: relative;">
    <!--固定部-->
    <table id="tableT" style="position:absolute;left:0px;top:0px;">
      <thead>
        <tr></tr>
      </thead>
    </table>

    <!--水平ヘッダー部-->
    <div id="header_h">
      <table id="tableH">
        <thead>
          <tr></tr>
        </thead>
      </table>
    </div>

    <!--垂直ヘッダー部-->
    <div id="header_v">
      <table id="tableV">
        <tbody></tbody>
      </table>
    </div>

    <!--データ部-->
    <div id="data">
      <table id="tableD" style="width:430px;">
        <thead>
          <tr>
            <th>固定項目</th>
            <th>項目1</th>
            <th>項目2</th>
            <th>項目3</th>
          </tr>
        </thead>
        <tbody>
          <tr>
            <td style="width:120px;">項目名称A</td>
            <td style="width:130px;">スライド項目A1</td>
            <td style="width:60px;">項目A2</td>
            <td style="width:120px;">スライド項目A3</td>
          </tr>
          <tr>
            <td>項目名称B</td>
            <td>スライド項目B1</td>
            <td>項目B2</td>
            <td>スライド項目B3</td>
          </tr>
          <tr>
            <td>項目名称C</td>
            <td>スライド項目C1</td>
            <td>項目C2</td>
            <td>スライド項目C3</td>
          </tr>
          <tr>
            <td>項目名称D</td>
            <td>スライド項目D1</td>
            <td>項目D2</td>
            <td>スライド項目D3</td>
          </tr>
          <tr>
            <td>項目名称E</td>
            <td>スライド項目E1</td>
            <td>項目E2</td>
            <td>スライド項目E3</td>
          </tr>
          <tr>
            <td>項目名称F</td>
            <td>スライド項目F1</td>
            <td>項目F2</td>
            <td>スライド項目F3</td>
          </tr>
          <tr>
            <td>項目名称G</td>
            <td>スライド項目G1</td>
            <td>項目G2</td>
            <td>スライド項目G3</td>
          </tr>
          <tr>
            <td>項目名称H</td>
            <td>スライド項目H1</td>
            <td>項目H2</td>
            <td>スライド項目H3</td>
          </tr>
          <tr>
            <td>項目名称I</td>
            <td>スライド項目I1</td>
            <td>項目I2</td>
            <td>スライド項目I3</td>
          </tr>
          <tr>
            <td>項目名称J</td>
            <td>スライド項目J1</td>
            <td>項目J2</td>
            <td>スライド項目J3</td>
          </tr>
          <tr>
            <td>項目名称K</td>
            <td>スライド項目K1</td>
            <td>項目K2</td>
            <td>スライド項目K3</td>
          </tr>
          <tr>
            <td>項目名称L</td>
            <td>スライド項目L1</td>
            <td>項目L2</td>
            <td>スライド項目L3</td>
          </tr>
          <tr>
            <td>項目名称M</td>
            <td>スライド項目M1</td>
            <td>項目M2</td>
            <td>スライド項目M3</td>
          </tr>
        </tbody>
      </table>
    </div>
  </div>
</body>
</html>

以上、テーブルヘッダーを縦スクロール及び横スクロールともに固定する方法のシステム開発備忘録でした。

更新2020/06/13

以前載せていたコードが上手く動作しないことを発見しました。
原因は古いjQueryを参照しようとしていてGoogleapis.com から取得できない事でした。
下のバージョンがNGの読み込みです。

<script src="//ajax.googleapis.com/ajax/libs/jquery/1.11.0/jquery.min.js"></script>

修正したjQueryの参照先を下に示しておきます。

<script src="http://code.jquery.com/jquery-latest.min.js"></script>

修正前のコードを参照してjQueryが動かなかった方が居られましたら、この場を借りてお詫びいたします。申し訳ありませんでしたm(_ _)m

それから、ブロックエディタに変わってから記載していたHTMLコードが変な風に表示されていました。それを修正しました。

【CSS】テーブルヘッダーを縦スクロールで固定する方法

テーブルヘッダーを縦スクロールで固定する方法のシステム開発備忘録です。
テーブルのヘッダーを固定する要望は多いと思いますが、何かと面倒なプログラミングになってしまいます。

テーブルヘッダーを固定する方法

そこで、今回は主にCSSとjQueryでテーブルのヘッダーを固定する方法を調査テストしたので、その備忘録です。

1)テーブルのヘッダー部、ボディー部、フッター部をfloat: left;します。
  floatにすることでoverflowが有効になります。
  ヘッダー部、ボディー部、フッター部と横に伸びて行く事になります。
  そこで後程、折り返しが付くようにtdもしくはthの幅をjqueryにて求めてセットします。
2)ヘッダー部の高さを決めます。
  今回のテストでは200pxにしています。
  また、スクロールバーが表示されるように設定します。
3)th,tdの高さや幅を要件沿って設定します。
  jqueryで立ち上げ時に幅を求めます。
  幅が動的に変更されるような場合は再度設定する必要があります。
4)Jqueryでテーブル幅を設定します。
  今回は各thの幅+border幅を足して行きます。
  最後にテーブルのborder幅とスクロール幅を足しました。
  調整が必要な場合も有るようですので、動かして確かめてください。

コードを下に示します。

<html>
<head>
  <script src="http://code.jquery.com/jquery-latest.min.js"></script>
  <style>
    /*thead,tbody,tfoot*/
    #tableA > thead,
    #tableA > tbody,
    #tableA > tfoot {
      float: left; /*overflowを有効化*/
    }
 
    #tableA > tbody {
      height: 200px;
      overflow: auto;
    }
 
    /*th,td*/
    #tableA th,
    #tableA td {
      height: 50px;
    }
    .cA{
      width:60px;
    }
    .cB{
      width:50px;
    }
    .cC{
      width:90px;
    }
    .cD{
      width:60px;
    }
    .cE{
      width:40px;
    }
    .cF{
      width:40px;
    }
    .cG{
      width:40px;
    }
 
    /*border*/
    #tableA {
      border: solid 1px black;
    }
 
    #tableA > thead > tr > th {
      border: solid 1px blue;
      background-color:lightblue;
    }
 
    #tableA > tbody > tr > td{
      border: solid 1px blue;
    }
    #tableA > tfoot > tr > td {
      border: solid 1px blue;
    }
  </style>
 
  <script type="text/javascript">
 
    $(document).ready(function () {
   //thの幅の合計を求める
      var w=0;
      $("#tableA > thead > tr >th").each(function (i) {
        w=w+$(this).width()+2;
      });
      $("#tableA").width(w + 16+2);
      $("#tableA > thead").width(w + 16+2);
      $("#tableA > tbody").width(w + 16+2);
      $("#tableA > tfoot").width(w + 16+2);
 
      $("#tableA > tr").width(w);

    });
  </script>
</head>
<body>
    <table id="tableA" cellspacing="0" cellpadding="0" style="border-collapse:collapse;">
      <caption>縦スクロール</caption>
      <thead>
        <tr>
          <th class="cA">A</th>
          <th class="cB">B</th>
          <th class="cC">C</th>
          <th class="cD">D</th>
          <th class="cE">E</th>
          <th class="cF">F</th>
          <th class="cG">G</th>
        </tr>
      </thead>
      <tbody>
        <tr>
          <td class="cA">A1</td>
          <td class="cB">B1</td>
          <td class="cC">C1</td>
          <td class="cD">D1</td>
          <td class="cE">E1</td>
          <td class="cF">F1</td>
          <td class="cG">G1</td>
        </tr>
        <tr>
          <td class="cA">A2</td>
          <td class="cB">B2</td>
          <td class="cC">C2</td>
          <td class="cD">D2</td>
          <td class="cE">E2</td>
          <td class="cF">F2</td>
          <td class="cG">G2</td>
  </tr>
        <tr>
          <td class="cA">A3</td>
          <td class="cB">B3</td>
          <td class="cC">C3</td>
          <td class="cD">D3</td>
          <td class="cE">E3</td>
          <td class="cF">F3</td>
          <td class="cG">G3</td>
 </tr>
        <tr>
          <td class="cA">A4</td>
          <td class="cB">B4</td>
          <td class="cC">C4</td>
          <td class="cD">D4</td>
          <td class="cE">E4</td>
          <td class="cF">F4</td>
          <td class="cG">G4</td>
 </tr>
        <tr>
          <td class="cA">A5</td>
          <td class="cB">B5</td>
          <td class="cC">C5</td>
          <td class="cD">D5</td>
          <td class="cE">E5</td>
          <td class="cF">F5</td>
          <td class="cG">G5</td>
 </tr>
        <tr>
          <td class="cA">A6</td>
          <td class="cB">B6</td>
          <td class="cC">C6</td>
          <td class="cD">D6</td>
          <td class="cE">E6</td>
          <td class="cF">F6</td>
          <td class="cG">G6</td>
 </tr>
        <tr>
          <td class="cA">A7</td>
          <td class="cB">B7</td>
          <td class="cC">C7</td>
          <td class="cD">D7</td>
          <td class="cE">E7</td>
          <td class="cF">F7</td>
          <td class="cG">G7</td>
 </tr>
      </tbody>
    </table>
</body>
</html>

縦スクロールだけで有れば割と簡単にテーブルヘッダーの固定ができます。
以上、テーブルヘッダーを縦スクロールで固定する方法のシステム開発備忘録でした。

修正2020/06/12

以前載せていたコードが上手く動作しないことを発見しました。
原因は古いjQueryを参照しようとしていてGoogleapis.com から取得できない事でした。
下のバージョンがNGの読み込みです。

<script src="//ajax.googleapis.com/ajax/libs/jquery/1.11.0/jquery.min.js"></script>

修正したjQueryの参照先を下に示しておきます。

<script src="http://code.jquery.com/jquery-latest.min.js"></script>

修正前のコードを参照してjQueryが動かなかった方が居られましたら、この場を借りてお詫びいたします。申し訳ありませんでしたm(_ _)m