WebSurfer's Home

トップ > Blog 1   |   ログイン
APMLフィルター

SqlDataSource を使わず GridView をソート

by WebSurfer 2018年10月22日 13:15

SqlDataSource などのデータソースコントロールを使わないで GridView の各列にソート機能を実装するにはどうすればいいかということを書きます。

GridView の Sorting

元は MSDN Forum のスレッド「SqlDataSourceを使わずに、GridViewの並べ替えができますでしょうか?」の話です。

MSDN ライブラリ GridView Class の「データへのバインド」のセクションに書いてありますが、GridView はデータソースコントロール(SqlDataSource, ObjectDataSource など)と連携して並べ替え、更新、削除、およびページング機能を提供しています。

逆に言えば、データソースコントロールを使わないと、ソート、更新、削除、およびページング機能が必要な場合、全て自力でコードを書いて実装することになります。

以下に、例としてソート機能を自力で GridView に実装する方法を書きます。

ソートを行うために GridView と SqlDataSource の中でどのような操作が行われているかは不明ですが、おそらく、取得したデータから DataView を作って、その Sort プロパティ に SortExpression を設定し、ソートした結果を GirdView に表示していると思われます。

そのあたりの処理を自力でコードを書いて実装してみます。

昇順に並べ替えるだけなら、GridView.Sorting イベントのハンドラで、DataView の Sort プロパティを引数の GridViewSortEventArgs オブジェクトから取得できる SortExpression に設定するだけで可能です。

SqlDataSource + GridView を使った場合、同じ LinkButton のクリックを繰り返すと ASC / DESC が切り替わりますが、そこまで同じにしようとするとちょっと面倒です。

SqlDataSource + GridView でそれをどのように実現しているか分かりませんが、GridView の属性に CurrentSortField, CurrentSortDir というのを追加し、それに前回クリックされた LinkButton の SortExpression およびその時 ASC or DESC どちらだったかの情報を保持することを考えてみました。

そのソースコードを以下にアップしておきます。このコードの実行結果が上の画像の GridView です。ヘッダの Name を 2 回クリックして降順に並べ替えています。

.aspx.cs ファイル

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Data;

public partial class _0055_GridViewSorting : System.Web.UI.Page
{
    // データソース用の DataView を作成
    protected DataView CreateDataSource()
    {
        DataTable dt = new DataTable();
        DataRow dr;

        dt.Columns.Add(new DataColumn("ID", typeof(Int32)));
        dt.Columns.Add(new DataColumn("Name", typeof(string)));
        dt.Columns.Add(new DataColumn("Type", typeof(string)));
        dt.Columns.Add(new DataColumn("Price", typeof(Int32)));
        dt.Columns.Add(new DataColumn("Qty", typeof(Int32)));
        dt.Columns.Add(new DataColumn("Amount", typeof(Int32)));
        dt.Columns.Add(new DataColumn("CatID", typeof(Int32)));
        dt.Columns.Add(new DataColumn("Note", typeof(string)));
        dt.Columns.Add(new DataColumn("Disc", typeof(bool)));
        dt.Columns.Add(new DataColumn("Date", typeof(DateTime)));

        for (int i = 0; i < 15; i++)
        {
            dr = dt.NewRow();
            dr["ID"] = i;
            dr["Name"] = "Product Name_" + i.ToString();
            dr["Type"] = "Product Type " + (100 - i).ToString();
            dr["Price"] = 123000 * (i + 1);
            dr["Qty"] = (i + 1) * 20;
            dr["Amount"] = 123000 * (i + 1) * (i + 1);
            dr["CatID"] = 100 - i;
            dr["Note"] = "Note_" + i.ToString();
            dr["Disc"] = (i % 2 == 0) ? true : false;
            dr["Date"] = DateTime.Now.AddDays(i);
            dt.Rows.Add(dr);
        }
        return new DataView(dt);
    }

    protected void Page_Load(object sender, EventArgs e)
    {
        if (!Page.IsPostBack)
        {
            GridView1.DataSource = CreateDataSource();
            GridView1.DataBind();
        }
    }

    protected void GridView1_Sorting(object sender, 
                                      GridViewSortEventArgs e)
    {
        GridView gv = (GridView)sender;
        DataView view = CreateDataSource();
        string exp = e.SortExpression;

        // 同じ LinkButton をクリックした場合 ASC / DESC を切り
        // 替えるための処理。
        // GridView の属性に CurrentSortField, CurrentSortDir 
        // を追加し、それに前回クリックされた LinkButton の 
        // SortExpression およびその時 ASC or DESC どちらだった
        // かの情報を保持。
        if (gv.Attributes["CurrentSortField"] != null &&
            gv.Attributes["CurrentSortDir"] != null)
        {
            if (exp == gv.Attributes["CurrentSortField"])
            {
                if (gv.Attributes["CurrentSortDir"] == "ASC")
                {
                    exp = exp + " DESC";
                    gv.Attributes["CurrentSortDir"] = "DESC";
                }
                else
                {
                    exp = exp + " ASC";
                    gv.Attributes["CurrentSortDir"] = "ASC";
                }
            }
            else
            {
                gv.Attributes["CurrentSortField"] = exp;
                exp = exp + " ASC";
                gv.Attributes["CurrentSortDir"] = "ASC";
            }
        }

        view.Sort = exp;
        gv.DataSource = view;
        gv.DataBind();
    }
}

.aspx ファイル

<%@ Page Language="C#" AutoEventWireup="true" 
    CodeFile="0055-GridViewSorting.aspx.cs" 
    Inherits="_0055_GridViewSorting" %>

<!DOCTYPE html>

<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
    <title></title>
</head>
<body>
    <form id="form1" runat="server">
        <asp:GridView ID="GridView1" runat="server" 
            AllowSorting="True"
            CurrentSortField=""
            CurrentSortDir=""
            onsorting="GridView1_Sorting">
        </asp:GridView>
    </form>
</body>
</html>

上のコードを見ると、自力で一行もコードを書く必要のない SqlDataSource を使う方が正解と思えます。何らかの理由で SqlDataSource を使えない場合でも、上に紹介した MSDN Forum の記事に書いてあるように ObjectDataSource を使うという手段もあります。

Tags: ,

ASP.NET

「常にコピーする」の意味

by WebSurfer 2018年9月29日 13:32

下の画像の「常にコピーする」の「常に」の意味が、Visual Studio 2010 と Visual Studio 2015(以下、VS2010、VS2015 と書きます)では違っているという話を書きます。

出力ディレクトリにコピー

どう違うかと言うと、VS2010 ではソースコードを変更してリビルドしてから実行した場合にコピーされますが、VS2015 ではソースコードは一切変更しなくても実行するたびコピーされます。SQL Server Express のユーザーインスタンスだけでなく LocalDB の場合も同じです。

それを知らないと上記の機能を利用したアプリの開発の際、期待した結果にならなくて焦るかもしれません。何を隠そう自分がそうだったのですが。(笑) どういうことだったかと言うと以下の通りです。

開発中のアプリで DB に INSERT, DELETE, UPDATE 等の操作をしてから一旦アプリを閉じた後、DB に反映された結果を見るために、再度アプリを実行して DB の内容を表示するということがあると思います。

そういう場合、VS2010 では編集結果が表示されますが、VS2015 では編集前の元のファイルがコピーされてそれを見ることになるので、アプリが期待通り動いてないと勘違いして焦るということです。(笑)

話としては以上なのですが、それだけではブログの記事としては書き足りない気がしましたので、少し追加情報を書いておきます。

普通に SQL Server に接続してアプリを開発しようとすると、SQL Server の既定のインスタンス(または、名前付きインスタンス)に接続することになります。

既定のインスタンス(または、名前付きインスタンス)を使えるようにするには SQL Server 側の設定がいろいろ面倒ですし、そこがクリアできても Visual Studio の Express 版からでは接続できないという問題もありました。

なので、SQL Server の Express 版のユーザーインスタンスや LocalDB を使って、アプリを起動する都度 .mdf ファイルをアタッチして接続するというファイルベースの開発を行う機能が提供されています。

その際「出力ディレクトリにコピー」の設定で、出力ディレクトリに .mdf ファイルをコピーを作成してそれをアタッチする設定にすることができます。

出力ディレクトリとは Windows Forms アプリの場合は .exe ファイルが出力されるフォルダ、ASP.NET Web アプリの場合は App_Data フォルダになります。接続文字列では |DataDirectory| と指定されます。

その「出力ディレクトリにコピー」のデフォルトが「常にコピーする」になります。

「常にコピーする」の他に「コピーしない」「新しい場合はコピーする」というオプションがあります。詳しくは、@IT の記事「Visual Studio 2005でデータベースの更新が反映されない場合には?」を見てください。

VS2005 は上の記事によると「再度アプリケーションをデバッグ実行すると、そのDBファイルは上書きされてしまう」とのことです。VS2008、VS2013、VS2017 等ではどうなるか不明です。

Tags: ,

DevelopmentTools

VS2015 でユーザーインスタンス利用

by WebSurfer 2018年9月24日 13:57

SQL Server Express のユーザーインスタンスを利用して、Visual Studio Community 2015 の TableAdapter 構成ウィザードで型付 DataSet + TableAdapter を自動生成する際の注意点を書いておきます。

Visual Studio 2010 Professional と同じ手順で作業すると、作業の過程で以下のように、"ファイル xxx.mdf の自動的に名前が付けられたデータベースをアタッチできませんでした。同じ名前のデータベースが既に存在するか、指定されたファイルを開けないか、UNC 共有に配置されています。" というエラーが出て先に進めません。

エラーメッセージ

原因はここまでの作業で生成された接続文字列に User Instance=True が付与されないからです。

接続文字列には AttachDbFilename でアタッチする .mdf ファイルは指定されるのですが、その場合 User Instance=True で明示的にユーザーインスタンスを使うようにする必要があるようです。

(想像ですが、User Instance=True が付与されていないので、既定の(または名前付き)インスタンスにアタッチしようとして、権限の問題で失敗していると思われます。先の記事「DB のアタッチ」参照)

TableAdapter 構成ウィザードで作業を進めていく際、「接続の追加」ダイアログで[詳細設定(V)...]ボタンをクリックすると「詳細プロパティ」ダイアログが表示されますので、そこでユーザーインスタンスを使用するように設定する必要があります。

詳細プロパティ

「詳細プロパティ」で[Data Source]を LocalDB から SQL Server Express に変更するところは気が付きましたが、もう一つ[User Instance]がデフォルトで False になっていて、これを True に変更する必要があることに気が付きませんでした。

ちなみに、Visual Studio 2010 Professional では「詳細プロパティ」の[User Instance]は True に設定されています。

「詳細プロパティ」の設定後、先に進んで TableAdapter 構成ウィザードに戻ったときに接続文字列を確認できます。

接続文字列

上の画像のように User Instance=True となっていれば OK です。後は従前の手順で進めていけば問題なく型付 DataSet + TableAdapter が完成するはずです。

ユーザーインスタンスはすでに非推奨になっていて、最近の開発には LocalDB を利用するので、このような問題に悩むケースはほとんどなさそうですが、忘れないよう備忘録として残しておくことにしました。(笑)

Tags: ,

ADO.NET

About this blog

2010年5月にこのブログを立ち上げました。その後 ブログ2 を追加し、ここは ASP.NET 関係のトピックス、ブログ2はそれ以外のトピックスに分けました。

Calendar

<<  2018年10月  >>
30123456
78910111213
14151617181920
21222324252627
28293031123
45678910

View posts in large calendar