WebSurfer's Home

Filter by APML

GridView, ListView でデータがない時もヘッダを表示

by WebSurfer 18. March 2026 18:21

ASP.NET Web Forms アプリの GirdView、ListView で、レコードが格納されてないデータソースがバインドされた時も、下の画像のようにヘッダ行を表示する方法を備忘録として書いておきます (デフォルトでは表示されません)。

GridView, ListView でデータがない時もヘッダを表示

以下に GridView と ListView のそれぞれの場合について書きます。上の画像を表示するのに使ったコードをこの記事の下の方に載せておきますのでそちらも参考にしてください。

(1) GridView

SqlDataSource のようなデータソースコントロールと組み合わせて使っていて、SqlDataSource がデータベースに SELECT クエリを投げても WHERE 句の条件が一致しないなどの理由でレコードを取得できない場合を考えます。

レコードを取得できなくても、SqlDataSource は空のデータソースを生成し、自動的にそれを GridView にデータバインドします。その場合は以下のように設定すれば上の画像のように表示されます。

データソースコントロールを使わないで自力でコードを書いて DataTable や List<T> を作成し、それを GridView の DataSource プロパティに設定して DataBind メソッドでバインドしている場合、ヘッダを表示するには空のデータソースを作ってバインドする必要がありますので注意してください。上の画像の GridView2 がそれで、コードはこの記事の下の方に載せましたので見てください。


(2) ListView

ListView には GridView の EmptyDataText のような便利なプロパティは存在しません。ListView.EmptyDataTemplate プロパティにデータが空の時表示したい html コードを書きます。

Visual Studio のデザイナを使って ListView のコードを生成させると、そのコードには EmptyDataTemplate が含まれるはずです。その中にヘッダと No data was returned. というメッセージ行の html コードを書いた結果が上の ListView の画像です。そのコードは下に載せましたので見てください。


上の画像を表示したコードは以下の通りです。

.aspx.cs

using System;
using System.Collections.Generic;

namespace WebForms1
{
    public partial class WebForm2 : System.Web.UI.Page
    {
        protected void Page_Load(object sender, EventArgs e)
        {
            if (!Page.IsPostBack)
            {
                // GridView2 はデータソースコントロールを使わない
                var data = new List<Hotel>();
                GridView2.DataSource = data;
                GridView2.DataBind();
            }
        }
    }

    public class Hotel
    {
        public int Id { get; set; }
        public string Name { get; set; }
        public string Address { get; set; }
        public decimal RoomCharge { get; set; }
    }
}

.aspx

<%@ Page Language="C#" AutoEventWireup="true"
    CodeBehind="WebForm2.aspx.cs"
    Inherits="WebForms1.WebForm2" %>

<!DOCTYPE html>

<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
    <title></title>
    <style type="text/css">
        table.style1 {
            border-style: solid;
            border-width: 2px;
            border-color: Silver;
            border-collapse: collapse;
        }

            table.style1 th {
                border-style: solid;
                border-width: 2px 1px 2px 1px;
                border-color: Silver;
                padding: 5px 8px;
                background-color: #6699FF;
                color: #FFFFFF;
            }

            table.style1 td {
                border-style: solid;
                border-width: 1px;
                border-color: Silver;
                padding: 2px 8px;
            }
    </style>
</head>
<body>
    <form id="form1" runat="server">
        <asp:SqlDataSource ID="SqlDataSource1" runat="server"
            ConnectionString="<%$ ConnectionStrings:NorthwindConnectionString %>"
            ProviderName="<%$ ConnectionStrings:NorthwindConnectionString.ProviderName %>"
            SelectCommand="SELECT [CustomerID], [CompanyName], [Address], [City], 
                    [PostalCode], [Country] FROM [Customers] WHERE CustomerID='NoID'">
        </asp:SqlDataSource>

        <h3>GirdView</h3>

        <asp:GridView ID="GridView1" runat="server"
            AutoGenerateColumns="False"
            DataKeyNames="CustomerID"
            ShowHeaderWhenEmpty="True"
            DataSourceID="SqlDataSource1"
            EmptyDataText="No data was returned.">
            <Columns>
                <asp:BoundField DataField="CustomerID" HeaderText="CustomerID"
                    ReadOnly="True" SortExpression="CustomerID" />
                <asp:BoundField DataField="CompanyName" HeaderText="CompanyName"
                    SortExpression="CompanyName" />
                <asp:BoundField DataField="Address" HeaderText="Address"
                    SortExpression="Address" />
                <asp:BoundField DataField="City" HeaderText="City"
                    SortExpression="City" />
                <asp:BoundField DataField="PostalCode" HeaderText="PostalCode"
                    SortExpression="PostalCode" />
                <asp:BoundField DataField="Country" HeaderText="Country"
                    SortExpression="Country" />
            </Columns>
        </asp:GridView>

        <h3>GridView2</h3>
        <asp:GridView ID="GridView2" runat="server" 
            EmptyDataText="No data was returned." 
            ShowHeaderWhenEmpty="True"></asp:GridView>

        <h3>ListView</h3>

        <asp:ListView ID="ListView1" runat="server"
            DataKeyNames="CustomerID"
            DataSourceID="SqlDataSource1">
            <EmptyDataTemplate>
                <table class="style1">
                    <tr>
                        <th>CustomerID</th>
                        <th>CompanyName</th>
                        <th>Address</th>
                        <th>City</th>
                        <th>PostalCode</th>
                        <th>Country</th>
                    </tr>
                    <tr>
                        <td colspan="6">No data was returned.</td>
                    </tr>
                </table>
            </EmptyDataTemplate>
            <ItemTemplate>
                <tr style="">
                    <td>
                        <asp:Label ID="CustomerIDLabel" runat="server"
                            Text='<%# Eval("CustomerID") %>' />
                    </td>
                    <td>
                        <asp:Label ID="CompanyNameLabel" runat="server"
                            Text='<%# Eval("CompanyName") %>' />
                    </td>
                    <td>
                        <asp:Label ID="AddressLabel" runat="server"
                            Text='<%# Eval("Address") %>' />
                    </td>
                    <td>
                        <asp:Label ID="CityLabel" runat="server"
                            Text='<%# Eval("City") %>' />
                    </td>
                    <td>
                        <asp:Label ID="PostalCodeLabel" runat="server"
                            Text='<%# Eval("PostalCode") %>' />
                    </td>
                    <td>
                        <asp:Label ID="CountryLabel" runat="server"
                            Text='<%# Eval("Country") %>' />
                    </td>
                </tr>
            </ItemTemplate>
            <LayoutTemplate>
                <table id="itemPlaceholderContainer" runat="server" class="style1">
                    <tr runat="server" style="">
                        <th runat="server">CustomerID</th>
                        <th runat="server">CompanyName</th>
                        <th runat="server">Address</th>
                        <th runat="server">City</th>
                        <th runat="server">PostalCode</th>
                        <th runat="server">Country</th>
                    </tr>
                    <tr id="itemPlaceholder" runat="server">
                    </tr>
                </table>
            </LayoutTemplate>
        </asp:ListView>
    </form>
</body>
</html>

Tags: , , ,

ASP.NET

FriendlyUrls を有効にしておくとページの静的メソッドが呼び出せない

by WebSurfer 27. September 2024 13:50

Visual Studio 2019 / 2022 のテンプレートを使って作成する Web Forms アプリのプロジェクトでは、Microsoft.AspNet.FriendlyUrls という NuGet パッケージがインストールされ、デフォルトで有効になるように設定されていますが、デフォルトの設定のままではクライアントスクリプトでページの静的メソッドが呼び出せないという話を書きます。

FriendlyUrls NuGet パッケージ

いろいろ説明すると長くなるのでまず最初に解決策を書いておきます。

自動生成されて App_Start フォルダに配置されている RouteConfig.cs で、以下のコードの通りリダイレクトモードが Permanent に設定されていますが、解決策はそれを Off に変更することです。

using Microsoft.AspNet.FriendlyUrls;
using System.Web.Routing;

namespace WebForms3
{
    public static class RouteConfig
    {
        public static void RegisterRoutes(RouteCollection routes)
        {
            var settings = new FriendlyUrlSettings();

            // デフォルトは Permanent
            // settings.AutoRedirectMode = RedirectMode.Permanent;

            // Off に変更
            settings.AutoRedirectMode = RedirectMode.Off;

            routes.EnableFriendlyUrls(settings);
        }
    }
}

Off にするのは SEO 的に好ましくないかもしれません。しかし、FriendlyUrls を使って拡張子 .aspx なしの url で呼び出せるようにしたい、かつクライアントスクリプトでページの静的メソッドを呼び出せるようにしたいのであれば、他に方法はなさそうです。


以下にどういうメカニズムになっているかなどの説明を書いておきます。興味があれば読んでください。

(1) FriendlyUrls とは何か

ASP.NET Web Forms アプリの場合、ブラウザからページを呼び出す際、url に https://.../default.aspx というように目的のページのファイル名を拡張子を付けて指定する必要があります。

FriendlyUrls を利用すると、一番最初にブラウザから拡張子 .aspx 付きで要求した場合は、デフォルトのリダイレクトモードの設定 Permanent では 301 Moved Permanently 応答が返ってきて、拡張子 .aspx 無しの url にリダイレクトされます。

下の Fiddler の画像の #9 を見てください。青枠で示したように Default.aspx を要求すると、赤枠で示したように 301 Moved Permanently 応答が返ってきて Default にリダイレクト指示が出ています。

FriendlyUrls NuGet パッケージ

ブラウザはリダイレクト指示を受けて Default を要求します。上の Fiddler の画像の #14 がそれです。Default を要求されたサーバー側では、FriendlyUrls がそれを Default.aspx にルーティングしてくれるので、Default.aspx が応答として返されます。

リダイレクトの際の HTTP 応答は 301 Moved Permanently なので、ブラウザは Default.aspx ⇒ Default に恒久的に移ったという情報をキャッシュします。なので2 回目以降は、たとえブラウザのアドレスバーに Default.aspx と入力して要求をかけても、ブラウザからは Default という url で要求が出ます。

(2) ページの静的メソッドとは何か

ページの静的メソッドとは、ASP.NET Web Forms のページ (.aspx.cs) に WebMethodAttribute 属性を付与して配置した public static メソッドで、AJAX を利用してクライアントスクリプトから呼び出すことができるものです。

jQuery ajax や fetch 等を使ったクライアントスクリプトで JSON 形式のデータを送信して JSON 形式の応答を受けるという Web API 的な使い方ができるので、ASP.NET Web Forms アプリでは利用価値は高いと思います。

詳しくは、先の記事「ASP.NET AJAX でページの静的メソッド呼び出し」を見てください。

(3) FriendlyUrls のリダイレクトモード

リダイレクトモードは Permanent, Temporary, Off のいずれかに設定でき、設定によって以下のように動きが異なります。

  • Permanent の場合、ブラウザから Default.aspx というように拡張子 .aspx 付きで要求を受けたときは 301 Moved Permanently 応答が返され、Default にリダイレクトされるようになっています。
  • Temporary の場合は 302 Found 応答でリダイレクトされる以外は Permanent と同じになります。
  • Off の場合はリダイレクトされません。Default.aspx で要求を受けるとそのまま Default.aspx が返されます。Default で要求を受けると、FriendlyUrls によって Default.aspx にルーティングされ、Default.aspx が返されます。

(4) リダイレクトモードが Permanent / Temporary の時の動作

リダイレクトモードが Permanent で、以下のように jQuery ajax を使って WebForm1.aspx ページにある静的メソッド MyWebMethod を呼び出したとします。

function CallWebMthod(productId) {
    $.ajax({
        type: "POST",
        url: "/WebForm1.aspx/MyWebMethod",
        contentType: "application/json; charset=utf-8",
        data: `{ "id": ${productId} }`
    }).done(response => {
        
        // ・・・中略・・・

    });
}

その応答を Fiddler で応答を見ると以下の通りとなっていました。これはリダイレクトモードが Temporary の場合も同じです。

Fiddler で見た応答

要求が WebForm1.aspx と拡張子 .aspx が付いているのでリダイレクト応答を返す動きになるはずですが、要求を受けてリダイレクト応答を返すまでのプロセスのどこかで 401 Unauthorized となり (赤枠部分)、認証プロセスに入ろうとしたが失敗したのでその旨応答を返した (青枠部分) ということのように見えます。プロセスのどこでどういう理由でそうなるかなど詳しいことは分かりません。

ちなみに、上の画像の青枠部分の JSON 文字列は、jQuery ajax のコードの変数 response に、JavaScript オブジェクトにデシリアライズした形で受け取ることができます。

(5) リダイレクトモードが Off の時の動作

FriendlyUrls が有効なまま静的メソッド MyWebMethod を呼び出せるようにするには、上に書いたように RoutConfig.cs のコードのリダイレクトモードを Off に変更してやります。

そうすれば上の jQuery のコードの /WebForm1.aspx/MyWebMethod のように拡張子 .aspx が付いた url の要求を受けてもリダイレクト応答を返すためのプロセスが動くことはなく、即 WebForm1.aspx ページの静的メソッド MyWebMethod が呼ばれて期待通り MyWebMethod の応答が返ってきます。

注: url: "/WebForm1/MyWebMethod" とすると (.aspx 拡張子を削除すると)、リダイレクトモード Permanent, Temporary, Off いずれの設定でも WebForm1.aspx ページ本体が応答として返ってきます。それは FriendlyUrls により WebForm1 は WebForm1.aspx にルーティングされ、MyWebMethod はサーバーに渡すパラメータと解釈され、WebForm1.aspx ページ本体が返すべきリソースと判断されるからだと思います。

(6) 検証に使ったサンプルコード

この記事を書くにあたって検証用に作成した WebForm1.aspx.cs、WebForm1.aspx のコードを載せておきます。以下の画像が実行結果で、LinkButton クリックで jQuery ajax を使って静的メソッド MyWebMethod を呼び出し、その応答を GridView の下に表示しています。

WebMethod の呼び出し

WebForm1.aspx.cs

using System;
using System.Web.Services;

namespace WebForms3
{
    public partial class WebForm1 : System.Web.UI.Page
    {
        protected void Page_Load(object sender, EventArgs e)
        {

        }

        [WebMethod]
        public static string MyWebMethod(int id)
        {
            return $"WebMethod called with id={id}";
        }

        protected string SetOnClientClick(int id)
        {
            return $"CallWebMthod({id}); return false;";
        }
    }
}

WebForm1.aspx

<%@ Page Language="C#" AutoEventWireup="true"
    CodeBehind="WebForm1.aspx.cs"
    Inherits="WebForms3.WebForm1" %>

<!DOCTYPE html>

<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
    <title></title>
    <script src="Scripts/jquery-3.7.0.js"></script>
    <script type="text/javascript">
        function CallWebMthod(productId) {
            $.ajax({
                type: "POST",
                url: "/WebForm1.aspx/MyWebMethod",
                contentType: "application/json; charset=utf-8",
                data: `{ "id": ${productId} }`
            }).done(response => {
                $("#result").empty;
                // .NET 3.5 で追加された d パラメータの処置。
                if (response.hasOwnProperty('d')) {
                    $("#result").text(response.d);
                }

                // リダイレクトモードが Parmanent の時、上の url
                // を要求するとサーバー内で 401 エラーとなって下
                // の応答が返ってくる:
                // {"Message":"認証に失敗しました。" ...}
                if (response.hasOwnProperty('Message')) {
                    $("#result").text(response.Message);
                }
            });
        }
    </script>
</head>
<body>
    <form id="form1" runat="server">
        <asp:SqlDataSource ID="SqlDataSource1"
            runat="server"
            ConnectionString="<%$ ConnectionStrings:NORTHWINDConnectionString %>"
            SelectCommand="SELECT TOP(10) [ProductID], [ProductName] FROM [Products]">
        </asp:SqlDataSource>

        <asp:GridView ID="GridView1"
            runat="server"
            AutoGenerateColumns="False"
            DataKeyNames="ProductID"
            DataSourceID="SqlDataSource1">
            <Columns>
                <asp:BoundField DataField="ProductID"
                    HeaderText="ProductID"
                    InsertVisible="False"
                    ReadOnly="True"
                    SortExpression="ProductID" />
                <asp:BoundField DataField="ProductName"
                    HeaderText="ProductName"
                    SortExpression="ProductName" />
                <asp:TemplateField HeaderText="Click to call WebMethod">
                    <ItemTemplate>
                        <asp:LinkButton ID="LinkButton1"
                            runat="server"
                            OnClientClick='<%# SetOnClientClick((int)Eval("ProductID")) %>'>
                    LinkButton
                        </asp:LinkButton>
                    </ItemTemplate>
                </asp:TemplateField>
            </Columns>
        </asp:GridView>

        <div id="result"></div>
    </form>
</body>
</html>

Tags: , , , ,

ASP.NET

ListView に thead と tbody 追加

by WebSurfer 1. April 2024 13:11

ASP.NET Web Forms アプリ用の ListView コントロールからレンダリングされる html ソースの table 要素内に thead, tbody 要素を追加する方法を書きます。

Visual Studio のデザイナを使って ListView のテーブル形式のコードを自動生成させると aspx ファイルに以下のような LayoutTemplate が生成されます。まず、それに赤枠と青枠で示したような thead, tbody 要素を追記します。

aspx ファイル

さらに、自動生成されたコードでは親の table 要素に runat="server" 属性が付与されているのでそれを削除する必要があります。上の画像のコメントを見てください。

runat="server" 属性が付与されているとその要素はサーバーコントロールになります。親の table 要素がサーバーコントロールになると、ASP.NET が html ソースをレンダリングする際に追加した thead, tbody 要素は削除されてしまいますので。

結果の html ソースは以下のようになります。

結果の html ソース

runat="server" 属性を削除するとサーバー側のコードでその要素を操作(例えばプロパティを動的に設定するなど)できなくなりますが、それ以外の不都合はないと思います。

GridView の例は別の記事「GridView と thead, tbody, tfoot」に書きましたのでそちらを見てください。

Tags: , ,

ASP.NET

About this blog

2010年5月にこのブログを立ち上げました。主に ASP.NET Web アプリ関係の記事です。ブログ2はそれ以外の日々の出来事などのトピックスになっています。

Calendar

<<  July 2026  >>
MoTuWeThFrSaSu
293012345
6789101112
13141516171819
20212223242526
272829303112
3456789

View posts in large calendar