none
SqlDataSourceを使わずに、GridViewの並べ替えができますでしょうか?

    質問

  • Visual Studio 2015(vb)、SQL Server2014 でWEBシステム構築を習得している最中です。
    言い回しなど不適切な点がございましたらご指摘ください。


    画面(*.aspx)はデザインなどの最低限にしたいのと、コード関係は*.vbに書きたいことから、
    sqlDataSourceコントロールを画面に配置せず、vb側でDataTableにデータを持ってきて、
            gvw1.DataSource = dtbDataTable1
            gvw1.DataBind()
    と記述し、画面にグリッドビューを配置するところまではできましたが、
    グリッドビューの項目名のところをクリックすると、
    「GridView 'gvw1' はハンドルされていないイベント Sorting を送出しました」
    とエラーになってしまいました。

    調べて見ると、下記
    https://msdn.microsoft.com/ja-jp/library/hwf94875(v=vs.100).aspx
    に、「GridView コントロールはデータ ソース コントロールと対話し、データが選択されたときに SortExpression をデータ ソースに渡すことで、
    並べ替えられたデータを要求できます。」とありました。
    これは、グリッドビューの機能で並べ替えをさせるにはデータソースコントロール(SqlDataSourceなど)を配置しないとできない、という意味でしょうか?

    データソースコントロールを配置せずに行う方法はありますでしょうか?

    今後、Gridviewの部分をjsGidなどのライブラリを使用することも検討しておりまして、
    なるべく仕組みを簡単に(処理系のコードはvb側で統一)させておきたいとも思っております。
    2016年9月6日 4:38

回答

  • 【追伸2】

    たびたびの追伸ですみません。

    データを表示してソート可能にするだけなら、型付 DataSet / DataTable + TableAdapter を作らなくても可能です。

    質問者さんの最初の質問にあった dtbDataTable1 という DataTable を作るソースがあるなら、それを別のクラスファイルに移して(Web サイトプロジェクトならそれを App_Code フォルダに配置)、それを ObjectDataSource から呼び出して DataTable を取得することもできます。

    そうすれば「aspx側にSQL文が記述されるのに抵抗」というところを解決できますし、自分では一切ソーティングのためのコードは書く必要がなくなります。

    例えば、以下のような SQL Server からデータを取得して DataTable を作成するクラスファイルを作って、

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Web;
    using System.Data;
    using System.Data.SqlClient;
    using System.Web.Configuration;
    
    public class NorthwindProductsDataTable
    {
    	public DataTable CreateDataTable()
        {
            string connString = WebConfigurationManager.
                ConnectionStrings["NORTHWINDConnectionString"].ConnectionString;
            string query = "SELECT * FROM Products";
            DataTable table = new DataTable();
    
            using (SqlConnection conn = new SqlConnection(connString))
            {
                SqlDataAdapter adapter = new SqlDataAdapter();
                adapter.SelectCommand = new SqlCommand(query, conn);
                adapter.Fill(table);
                return table;
            }
        }
    }

    それを以下のように ObjectDataSource に設定すれば、

    <%@ Page Language="C#" AutoEventWireup="true" 
        CodeFile="0162-GridViewSorting2.aspx.cs" 
        Inherits="_0162_GridViewSorting2" %>
    
    <!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></title>
    </head>
    <body>
        <form id="form1" runat="server">
        <asp:ObjectDataSource ID="ObjectDataSource1" runat="server" 
            SelectMethod="CreateDataTable" 
            TypeName="NorthwindProductsDataTable">
        </asp:ObjectDataSource>
        <asp:GridView ID="GridView1" runat="server" 
            AllowSorting="True" 
            DataSourceID="ObjectDataSource1">
        </asp:GridView>
        </form>
    </body>
    </html>

    以下の画像のようなソート可能な GridView を作成できます(下の画像は ProductName を降順でソートした結果)。

    2016年9月6日 11:21

すべての返信

  • 追加で質問なのですが、
    https://msdn.microsoft.com/ja-jp/library/hwf94875(v=vs.100).aspx
    こちらによると、項目名がクリックされるとSortイベントが発生し、「並べ替えパラメータを含む選択クエリまたは選択メソッドを実行」とありますが、
    再度データ(10000件をselectしてきているのであれば、再度10000件)を取得してくるのでしょうか?
    始めに取得したデータに対し、単に並べ替えをするだけと考えていたのですが…


    2016年9月6日 4:49
  • GridView と SqlDataSource の中でどのような操作が行われているかは見えないので想像ですが、たぶん、取得したデータから DataView を作って、その Sort プロパティ に SortExpression を設定し、ソートした結果を GirdView に表示しているようです。

    なので、そのあたりを自力で実装すれば可能だと思います。(未検証です。今時間がないので後で時間ができたら検証してみます)

    その前に、

    > 画面(*.aspx)はデザインなどの最低限にしたいのと、コード関係は*.vbに書きたいことから、
    > sqlDataSourceコントロールを画面に配置せず、vb側でDataTableにデータを持ってきて、

    そうしなければならない意味が理解できませんが、単純に SqlDataSource を .aspx ファイルに入れたくないという理由だけなら、.aspx.vb で SqlDataSource を動的に生成してそれを使うという手もありそうですが?

    それもダメなのか教えてください。

    2016年9月6日 5:01
  • > 再度データ(10000件をselectしてきているのであれば、再度10000件)を取得してくるのでしょうか?

    SqlDataSource と GridView を使った場合はそうです。

    プロファイラで見ると分かりますが、ヘッダの LinkButton をクリックすると、SqlDataSource の SelectCommand に設定されたクエリが毎回そのまま投げられます。

    2016年9月6日 5:08
  • SurferOnWww様、ご回答ありがとうございました。

    >なので、そのあたりを自力で実装すれば可能だと思います。(未検証です。今時間がないので後で時間ができたら
    >検証してみます)

    自分で 「select ~ ~order by ~」のSQL文を記述し再度データを取得してきてデータバインドする、といった認識で
    あっていますでしょうか?

    >そうしなければならない意味が理解できませんが、単純に SqlDataSource を .aspx ファイルに入れたくないという
    >理由だけなら、.aspx.vb で SqlDataSource を動的に生成してそれを使うという手もありそうですが?

    これは、最初に参考にしたプログラムがsqlDataSourceを使っておらず、使わない方法で表示まではできたので、
    並べ替えのためにsqlDataSourceコントロールを使うものなのか?この状態で実装できないのか?と思ってでした。
    また、基本的な参考書等も参考にしていますが、sqlDataSourceコントロールを配置していく手順の中でSQL文を
    作成しており、aspx側にSQL文が記述されるのに抵抗があったためです。

    教えてくださった、「動的に生成する方法」を試してみたいと思います!
    コントロールを動的に生成するのが初めてで、検索してみていますがうまくひっかからず(aspにsqlDataSourceを
    記述しているものばかり検索されます)、大変申し訳ないのですが、キーワードでいいので教えていただけますでしょうか?


    2016年9月6日 5:50
  • 横から失礼します。SqlDataSourceを使うのが簡単ですが、必ずしも使う必要はなく、以下のようなことも実現できますので、違う観点からご紹介しておきます。

    >gvw1.DataSource = dtbDataTable1

    と書かれていますので、ここにSQLでソートされたDataTableを再バインドすればソートが実現できます。
    もしくは、DataViewをバインドするようにし、このDataViewの並び順を変化させるようにしても良いと思います。
    具体的な方法については以下を参考にしてみて下さい。

    (参考)
    How to allow sorting of a gridview?
    http://stackoverflow.com/questions/5352300/how-to-allow-sorting-of-a-gridview


    ★良い回答には回答済みマークを付けよう! MVP - .NET  http://d.hatena.ne.jp/trapemiya/

    2016年9月6日 6:30
    モデレータ
  • > 自分で 「select ~ ~order by ~」のSQL文を記述し再度データを取得してきて
    > データバインドする、といった認識であっていますでしょうか?

    いえ、そうではなくて、私の提案は、一番最初のレスで書いたように「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 どちらだったかの情報を保持することを考えてみました。

    以下に検証に使ったソースをアップしておきます。(C# ですが読めますよね? 分からなければ質問してください)

    .aspx

    <%@ Page Language="C#" AutoEventWireup="true" 
        CodeFile="0162-GridViewSorting.aspx.cs" 
        Inherits="_0162_GridViewSorting" %>
    
    <!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></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>

    .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 _0162_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("CategoryID", typeof(Int32)));
            dt.Columns.Add(new DataColumn("Note", typeof(string)));
            dt.Columns.Add(new DataColumn("Discontinued", typeof(bool)));
            dt.Columns.Add(new DataColumn("DateTime", typeof(DateTime)));
    
            for (int i = 0; i < 25; 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["CategoryID"] = 100 - i;
                dr["Note"] = "Note_" + i.ToString();
                dr["Discontinued"] = (i % 2 == 0) ? true : false;
                dr["DateTime"] = 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();
        }
    }

    やっぱり、どう考えても SqlDataSource とか ObjectDataSource を使うのが正解だと思います。
    2016年9月6日 8:15
  • 【追伸】

    > sqlDataSourceコントロールを配置していく手順の中でSQL文を
    > 作成しており、aspx側にSQL文が記述されるのに抵抗があったためです。

    質問者さんの感覚では抵抗があるかもしれませんが、SqlDataSource を使うならそれが普通のやり方で、普通のやり方に従ってやるのが開発工数や保守工数の面でベストだと自分は思うのですが。

    どうしても「aspx側にSQL文が記述されるのに抵抗」があるなら、以下の記事にあるように型付 DataSet / DataTable + TableAdapter を作って、それと ObjectDataSource を組み合わせて使うという手もあります。

    Microsoft Visual Studio 2005による Webアプリケーション構築技法
    7.6 データコンポーネント機能によるデータアクセスコンポーネントの開発
    http://www.atmarkit.co.jp/fdotnet/bookpreview/vs2005webapp_07/vs2005webapp_07_01.html

    検討してみてください。


    > 教えてくださった、「動的に生成する方法」を試してみたいと思います!

    「aspx側にSQL文が記述されるのに抵抗」というところを解決するだけでいいのなら、その方向に進むのではなく、上に紹介した記事の方向に進むことをお勧めします。

    2016年9月6日 8:32
  • 【追伸2】

    たびたびの追伸ですみません。

    データを表示してソート可能にするだけなら、型付 DataSet / DataTable + TableAdapter を作らなくても可能です。

    質問者さんの最初の質問にあった dtbDataTable1 という DataTable を作るソースがあるなら、それを別のクラスファイルに移して(Web サイトプロジェクトならそれを App_Code フォルダに配置)、それを ObjectDataSource から呼び出して DataTable を取得することもできます。

    そうすれば「aspx側にSQL文が記述されるのに抵抗」というところを解決できますし、自分では一切ソーティングのためのコードは書く必要がなくなります。

    例えば、以下のような SQL Server からデータを取得して DataTable を作成するクラスファイルを作って、

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Web;
    using System.Data;
    using System.Data.SqlClient;
    using System.Web.Configuration;
    
    public class NorthwindProductsDataTable
    {
    	public DataTable CreateDataTable()
        {
            string connString = WebConfigurationManager.
                ConnectionStrings["NORTHWINDConnectionString"].ConnectionString;
            string query = "SELECT * FROM Products";
            DataTable table = new DataTable();
    
            using (SqlConnection conn = new SqlConnection(connString))
            {
                SqlDataAdapter adapter = new SqlDataAdapter();
                adapter.SelectCommand = new SqlCommand(query, conn);
                adapter.Fill(table);
                return table;
            }
        }
    }

    それを以下のように ObjectDataSource に設定すれば、

    <%@ Page Language="C#" AutoEventWireup="true" 
        CodeFile="0162-GridViewSorting2.aspx.cs" 
        Inherits="_0162_GridViewSorting2" %>
    
    <!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></title>
    </head>
    <body>
        <form id="form1" runat="server">
        <asp:ObjectDataSource ID="ObjectDataSource1" runat="server" 
            SelectMethod="CreateDataTable" 
            TypeName="NorthwindProductsDataTable">
        </asp:ObjectDataSource>
        <asp:GridView ID="GridView1" runat="server" 
            AllowSorting="True" 
            DataSourceID="ObjectDataSource1">
        </asp:GridView>
        </form>
    </body>
    </html>

    以下の画像のようなソート可能な GridView を作成できます(下の画像は ProductName を降順でソートした結果)。

    2016年9月6日 11:21
  • trapemiya様、SurferOnWww様、ご回答ありがとうございます。
    すぐに試せずに時間が空いてしまって、申し訳ございませんでした。

    お二人がご回答くださった内容をあわせてみると、Sortingを使うという共通点と、
    DataViewを使うか、DataViewを使わず(にSQL文を記載する?)の違いのようでしたので、
    お二人の情報をあわせて、下記のとおりにしてみました。

    Private Sub gvw1_Sorting(sender As Object, e As GridViewSortEventArgs) Handles gvw1.Sorting
            MsgBox("sorting!")
    
            Dim strExp As String = e.SortExpression
            Dim strDrt As String = e.SortDirection
            Dim dvwView As DataView
    
            dvwView = New DataView(dtbDataTable1)
    
            If Not (gvw1.Attributes("YMDHMS") Is Nothing) Then
                If strDrt = SortDirection.Ascending Then
                    strExp = strExp & " DESC"
                    dvwView.Sort = strExp
                    gvw1.DataSource = dvwView
                    gvw1.DataBind()
                Else
                    strExp = strExp & " ASC"
                    dvwView.Sort = strExp
                    gvw1.DataSource = dvwView
                    gvw1.DataBind()
                End If
            End If
    
            '上記判定うまく通らず、とりあえず強制SORT
            strExp = strExp & " DESC"
            dvwView.Sort = strExp
            gvw1.DataSource = dvwView
            gvw1.DataBind()
    
    End Sub
    
    ですが、gvw1.Attributes("YMDHMS")がNothingとなること、e.SortDirectionは何回クリックしても0だったことから、
    とりあえず並べ替えを試したかったので、強制SORTを試し、並べ替えすることは成功しました。

    これから、SurferOnWww様の2回目にご提供くださったモジュールを試したいと思います。
    また時間を空けてしまうと思いますが、結果をご報告させてください。

    2016年9月7日 3:22
  • > gvw1.Attributes("YMDHMS")がNothingとなること、

    .aspx ファイルの GridView にその属性を設定してないからでしょう。

    私のレスにアップした .aspx ファイルのコードをよく見てください。その部分のみ以下に再掲しておきますので同じようにしてみてください。

    <asp:GridView ID="GridView1" runat="server" 
        AllowSorting="True"
        CurrentSortField=""
        CurrentSortDir=""
        onsorting="GridView1_Sorting">
    </asp:GridView>

    > e.SortDirectionは何回クリックしても0だったことから、

    どうやってそれを確認しましたか? Sorting イベントのハンドラで e.SortDirection が 0 になることはあり得ないです。

    SqlDataSource や ObjectDataSource を使わない場合、同じ LinkButton を繰り返しクリックしても e.SortDirection は Ascending で変わりません。

    (ちなみに、SqlDataSource や ObjectDataSource を使用すると、同じ LinkButton を繰り返しクリックした場合 e.SortDirection は Ascending / Descending 交互に切り替わります)

    だから、私のレスでアップした .aspx.cs のコードの、

            // 同じ 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";
                }
            }

    の部分が必要になるのです。

    Ascending / Descending の切り替えは必要なく、LinkButton をクリックしたら昇順に並べ替えるだけでよければ、Sorting イベントのハンドラで、DataView の Sort プロパティを引数の GridViewSortEventArgs オブジェクトから取得できる SortExpression に設定するだけで可能です。(即ち上のコードを削除すればよい)

    私がアップしたソースをよく読んで、意味を理解した上で、不明点があったら質問してクリアにしたうえで、ご自分のコードの反映させてください。


    • 編集済み SurferOnWww 2016年9月7日 5:25 脱字追加
    2016年9月7日 5:19
  • ですが、gvw1.Attributes("YMDHMS")がNothingとなること、e.SortDirectionは何回クリックしても0だったことから、
    とりあえず並べ替えを試したかったので、強制SORTを試し、並べ替えすることは成功しました。

    とりあえず、参考になりそうなコードを探しましたので、ご紹介しておきます。(C#でごめんなさい)

    GridView Sorting example in ASP.NET using C#
    http://www.dotnetfox.com/articles/gridview-sorting-example-in-Asp-Net-using-C-Sharp-1082.aspx


    ★良い回答には回答済みマークを付けよう! MVP - .NET  http://d.hatena.ne.jp/trapemiya/

    2016年9月7日 5:53
    モデレータ
  • > とりあえず、参考になりそうなコードを探しましたので、ご紹介しておきます。(C#でごめんなさい)
    >
    > GridView Sorting example in ASP.NET using C#
    > http://www.dotnetfox.com/articles/gridview-sorting-example-in-Asp-Net-using-C-Sharp-1082.aspx

    SqlDataSource や ObjectDataSource を使用してソートする場合と違う動きになりませんか?

    SqlDataSource や ObjectDataSource を使用すると、

    (1) 同じ LinkButton を繰り返しクリックした場合 e.SortDirection は Ascending / Descending 交互に切り替わる。GridView に表示されるソート結果もそれに応じて Ascending / Descending 交互に切り替わる。

    (2) 違う LinkButton をクリックした場合は Ascending になる。GridView に表示されるソート結果もそれに応じて Ascending になる。

    ・・・ということになるはずです。

    trapemiya さんが紹介された記事のコードでは、上記 (2) が違う動きになる(どの LinkButton をクリックしても、クリックするたび Ascending / Descending が交互に切り替わる)ように見えます。実際に上の記事のコードを試しておられたら、どのような結果になるのか教えていただけると幸いです。

    あと、「参考になりそうなコード」は既に私が紹介したつもりなのですが、それにかぶせて上の記事を紹介するということは、私が紹介したコードに何か不備があるのでしょうか? それとも trapemiya さんが紹介された記事の方法の方が簡単かつスマートとかいうことがあるのでしょうか? もしそういうことがあれば、それもあわせて教えていただけると幸いです。


    【追伸】

    私が紹介したコードで、前回クリックされた LinkButton の SortExpression およびその時 ASC or DESC どちらだったかの情報を保持するのに GridView の属性を使った理由は、同じページに GridView が複数あると、Session や ViewSate では面倒だと思ったからです。

    • 編集済み SurferOnWww 2016年9月7日 6:59 追伸追加
    2016年9月7日 6:40
  • いえいえ、SqlDataSourceを使わない方法を試されてうまく動作しなかったということでしたので、その方法における参考の一つとしてご紹介したまでです。ただし、私がご紹介したコードが参考になるかどうかは質問者さんじゃないとわかりませんが、このスレッドのタイトルを検索してたどり着いた人の中には、参考になる場合もあるんじゃないかという思いも含んでいます。

    ★良い回答には回答済みマークを付けよう! MVP - .NET  http://d.hatena.ne.jp/trapemiya/

    2016年9月7日 7:31
    モデレータ
  • > いえいえ、SqlDataSourceを使わない方法を試されてうまく動作しなかったということでしたので、
    > その方法における参考の一つとしてご紹介したまでです。

    質問者さんがうまく使えなかった GridView の属性を使うのではなく、ViewState を使う別案を紹介したと言われてますか?

    もし、そうであればそのような説明を書いて、さらに SqlDataSource や ObjectDataSource を使用してソートする場合と違う動きになる(どの LinkButton をクリックしても、クリックするたび Ascending / Descending が交互に切り替わる・・・試してないので違ったらすみません)ということも書き加えた方が、質問者さんにはもちろん、「このスレッドのタイトルを検索してたどり着いた人」にも親切かと思います。

    次回は是非そのようにお願いしたいと思います。

    2016年9月7日 8:11
  • >> gvw1.Attributes("YMDHMS")がNothingとなること、
    >.aspx ファイルの GridView にその属性を設定してないからでしょう。

    >私がアップしたソースをよく読んで、意味を理解した上で、不明点があったら質問してクリアにしたうえで、ご自分のコードの
    反映させてください。

    大変申し訳ありません、「CurrentSortField」「CurrentSortDir」を、勝手に列名と勘違いしていました。
    そしてご提示していただいたプログラムを自分のプログラムに実装しながら…としていたので、さらに変なことになってしまって
    いました。
    完成したプログラムを提供してくださっているのに、大変失礼いたしました。

    いただいたプログラムをそっくりパクらせていただき、動くことを確認しました!

    下記は、VBにしたコードです。aspは大事な部分は何も変わらないので、UPしませんでした。

    Public Class CMTest
        Inherits System.Web.UI.Page
    
        ’データソース用の DataView を作成
        Protected Function CreateDataSource() As DataView
            Dim dt As New DataTable()
            Dim dr As DataRow
    
            dt.Columns.Add(New DataColumn("ID", GetType(Int32)))
            dt.Columns.Add(New DataColumn("Name", GetType(String)))
            dt.Columns.Add(New DataColumn("Type", GetType(String)))
            dt.Columns.Add(New DataColumn("Price", GetType(Int32)))
            dt.Columns.Add(New DataColumn("Qty", GetType(Int32)))
            dt.Columns.Add(New DataColumn("Amount", GetType(Int32)))
            dt.Columns.Add(New DataColumn("CategoryID", GetType(Int32)))
            dt.Columns.Add(New DataColumn("Note", GetType(String)))
            dt.Columns.Add(New DataColumn("Discontinued", GetType(Boolean)))
            dt.Columns.Add(New DataColumn("DateTime", GetType(DateTime)))
    
            For i As Integer = 0 To 24
                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("CategoryID") = 100 - i
                dr("Note") = "Note_" + i.ToString()
                dr("Discontinued") = If((i Mod 2 = 0), True, False)
                dr("DateTime") = DateTime.Now.AddDays(i)
                dt.Rows.Add(dr)
            Next
            Return New DataView(dt)
    
        End Function
    
        Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
    
            If Not Page.IsPostBack Then
                GridView1.DataSource = CreateDataSource()
                GridView1.DataBind()
            End If
        End Sub
    
        Protected Sub GridView1_Sorting(sender As Object, e As GridViewSortEventArgs)
            Dim gv As GridView = DirectCast(sender, GridView)
            Dim view As DataView = CreateDataSource()
            Dim exp As String = e.SortExpression
    
            ' 同じ LinkButton をクリックした場合 ASC / DESC を切り替えるための細工。
            ' GridView の「属性」に CurrentSortField, CurrentSortDir というのを追加し、
            ' それに前回クリックされた LinkButton の SortExpression およびその時 
            ' ASC or DESC どちらだったかの情報を保持。
            If gv.Attributes("CurrentSortField") IsNot Nothing _
            AndAlso gv.Attributes("CurrentSortDir") IsNot Nothing Then
                If exp = gv.Attributes("CurrentSortField") Then
                    If gv.Attributes("CurrentSortDir") = "ASC" Then
                        exp = exp & " DESC"
                        gv.Attributes("CurrentSortDir") = "DESC"
                    Else
                        exp = exp & " ASC"
                        gv.Attributes("CurrentSortDir") = "ASC"
                    End If
                Else
                    gv.Attributes("CurrentSortField") = exp
                    exp = exp & " ASC"
                    gv.Attributes("CurrentSortDir") = "ASC"
                End If
            End If
    
            view.Sort = exp
            gv.DataSource = view
            gv.DataBind()
        End Sub
    
    End Class
    この段階で、質問させていただいていいでしょうか?

    1.CurrentSortFieldとCurrentSortDir は、今回のために独自で作成した属性、ということでいいのでしょうか?
     (属性は自由に作成できるということ?)
    2.「onsorting="GridView1_Sorting"」という部分についてですが、「Sortingイベントを発生」とmsdnにはあったのですが、他の、例えばRowCommandは「onRowCommand="GridView1_RowCommand"」などと記述しなくても、vb側に記述があれば動作 しますが、この違いは何なのでしょう?試しに「onsorting="GridView1_Sorting"」の記述を外してみると、「ハンドルされていないイベント Sorting を送出しました」というエラーになりましたが、この「ハンドルされていないイベント」 という意味は、どういう意味なのでしょうか?初歩的な質問で大変申し訳ありません。


    まずは、お詫びと、動いたVBコードのUPと、ご質問でした。

    これからまた次に進みます、進捗が遅くてすみません。

    (「コードブロックの挿入」の部分の色が変になってしまったのですが、何度編集しても正しくなりませんでした…)


    2016年9月7日 8:29
  • 質問者さんがうまく使えなかった GridView の属性を使うのではなく、ViewState を使う別案を紹介したと言われてますか?

    そこには着目してなくて、SqlDataSourceを使わないことにのみ着目してご紹介しただけです。もちろん、ご紹介したコードそのままで質問者さんの要求を満たすとは考えていませんし、場合によっては今回は全く役に立たないかもしれません。あくまでも参考の一つとして、もし何かのヒントになるのであれば幸いと考えただけです。

    ★良い回答には回答済みマークを付けよう! MVP - .NET  http://d.hatena.ne.jp/trapemiya/

    2016年9月7日 8:30
    モデレータ
  • > 1.CurrentSortFieldとCurrentSortDir は、今回のために独自で作成した属性、ということで
    > いいのでしょうか?(属性は自由に作成できるということ?)

    そのコントロールの既存のプロパティ名とダブらなければ自由に命名して追加できるようです。またそのコントロールの ViewState が有効になっていれば(デフォルトで有効)ポストバック前後でその属性に設定した値を保持してくれるようです。

    (「ようです」と書いたのは、それが明記してある MDSN ライブラリ等が見つからないので、実際にやってみた結果からそう言っているからです)

    > 2.「onsorting="GridView1_Sorting"」という部分についてですが、「Sortingイベントを発生」
    > とmsdnにはあったのですが、他の、例えばRowCommandは「onRowCommand="GridView1_RowCommand"」
    > などと記述しなくても、vb側に記述があれば動作 しますが、この違いは何なのでしょう?

    普段 VB は全く使わないのでよく分からないのですが、VB.NET の場合ハンドラをイベントにアタッチする場合 Handles 句が使えるので、それを使うか否かの違いだと思います。

    質問者さんがアップされた VB.NET のコードを見てください。Page_Load は Handles 句を使ってアタッチ、GridView1_Sorting は GridView の OnSorting 属性を使ってアタッチしているようです。

    RowCommand には Handles 句を使ってハンドラをアタッチしているのではないですか?

    > 試しに「onsorting="GridView1_Sorting"」の記述を外してみると、「ハンドルされていないイベント
    > Sorting を送出しました」というエラーになりましたが、この「ハンドルされていないイベント」
    > という意味は、どういう意味なのでしょうか?

    普通の .NET のプログラムでは、イベントにハンドラをアタッチしなくても、そのイベントがハンドルされないだけでエラーにはならないはずなのですが、GridView.Sorting イベントだけは何故か特別で、何らかのハンドラがアタッチされてないとエラーになるようです。

    2016年9月7日 10:43
  • >> e.SortDirectionは何回クリックしても0だったことから、
    >どうやってそれを確認しましたか? Sorting イベントのハンドラで e.SortDirection が 0 になることはあり得ないです。
    申し訳ありません。「e.SortDirection」と記載しておきながら、実際は
    「Dim strDrt As String = e.SortDirection」
    のstrDrtの上にマウスポインタを置いて、変数に入ってきた値を確認しました。
    (この情報を表示してくれる機能の呼び方が分かりません)
    true:-1、false:0 と同じ感覚で、変数に入ってくる値で確認し、それをご報告していました。
    (実際は文字列型にしてしまっていたので、Boolean型とは違うとは思いますが)

    「e.SortDirection」の上にマウスポインタを置いて確認したところ、「Ascending{0}」でした。
    何度も同じ LinkButton をクリックしてみて、「Ascending{0}」にしかならないことを確認しました。
    これは、SurferOnWww様が
    >SqlDataSource や ObjectDataSource を使わない場合、同じ LinkButton を繰り返しクリックしても 
    >e.SortDirection は Ascending で変わりません。
    >(ちなみに、SqlDataSource や ObjectDataSource を使用すると、同じ LinkButton を繰り返しクリックした場合
    >e.SortDirection は Ascending / Descending 交互に切り替わります)
    と教えてくださったとおり、「SqlDataSource」を使っていないからなのだと理解しました。
    2016年9月7日 10:51
  • >そのコントロールの既存のプロパティ名とダブらなければ自由に命名して追加できるようです。
    >VB.NET の場合ハンドラをイベントにアタッチする場合 Handles 句が使えるので、それを使うか否かの違いだと思います。

    SurferOnWww様、ご回答ありがとうございます。

    >RowCommand には Handles 句を使ってハンドラをアタッチしているのではないですか?
    おっしゃる通り、
    Private Sub gvw1_RowCommand(sender As Object, e As GridViewCommandEventArgs) Handles gvw1.RowCommand
    となっていました。
    これが、「ハンドラをイベントにアタッチ」しているところなんですね。「Private Sub gvw1_RowCommand(sender As Object, e As GridViewCommandEventArgs)」の部分しか意識していませんでした。
    そして、GridView.Sortingはイベントのところではなく、GridViewの属性のところ(vbではなくaspx側)でアタッチしていて、アタッチしていないのにイベントを書いているからエラーになる、と解釈しました。

    とてもすっきり解りました!ありがとうございました!
    2016年9月7日 11:32
  • SurferOnWww様がご回答くださった、【追伸2】ObjectDataSourceから呼び出す方法のテストの中間報告です。
    (解決までに長そうなので…)

    まずは、既にある、DataTableを作るソースを別のクラスファイルに移してグリッドビューに表示するまでを完成しました。
    その後、ObjectDataSourceから呼び出す方法に切り替えようとしているのですが、
    「ObjectDataSource 'ObjectDataSource1' の TypeName プロパティで指定された型が見つかりませんでした。 」
    というエラーとなり、画面表示できません。

        <form id="form1" runat="server">
            <div>GridViewテスト</div>
    
            <asp:ObjectDataSource ID="ObjectDataSource1" runat="server" 
                SelectMethod="CreateDataTable" 
                TypeName="TestDataTable1">
            </asp:ObjectDataSource>
            <asp:GridView ID="GridView1" runat="server" 
                AllowSorting="True"
                DataSourceID="ObjectDataSource1">
            </asp:GridView>
        </form>
    Public Class TestDataTable1
        Public Function CreateDataTable() As DataTable
            Dim dtbDatatable As New DataTable
            '・・・定義いろいろ
    
            CreateDataTable = Nothing
            '・・・処理いろいろ
            Return dtbDatatable
        End Function
    End Class
    

    「TypeName」の"TestDataTable1"がクラス名の認識でおりますが、エラーとなってしまいました。

    上記でもし何かお気づきの点がございましたら、教えていただければ大変助かります。
    クラスの概念をまだしっかり理解しておらず調査に時間がかかりそうでしたので、一旦ご報告させていただきました。

    2016年9月8日 7:23
  • > 「TypeName」の"TestDataTable1"がクラス名の認識でおりますが、エラーとなってしまいました。

    その理解で OK です。エラーになるのは名前の間違い(特に、Web アプリケーションプロジェクトでは名前空間名も必要なことに注意)か、クラスファイルがきちんとできていないからだと思います。

    Web サイトプロジェクト or Web アプリケーションプロジェクトのどちらで作っていますか?(Visual Studio の[ファイル(F)]⇒[新しい Web サイト(W)...]で作るのが前者、[ファイル(F)]⇒[新しいプロジェクト(P)...]で作るのが後者です)

    Web サイトプロジェクトで作っているなら TestDataTable1 クラスを記述したクラスファイルは App_Code フォルダに配置してください。

    Web アプリケーションプロジェクトなら TestDataTable1 クラスを記述したクラスファイルはアプリケーションルート直下に置いてください。

    そして一旦ビルドしてください。もちろんビルドは通りますよね? (通らないようですと、当たり前ですが、この先に進めません)

    ツールボックスから Page に ObjectDataSource と GridView をドラッグ&ドロップしたら、デザイン画面で ObjectDataSource の > マークをクリック ⇒[データソースの構成...]をクリックすると以下のダイアログが現われるはずです。下の画像に私のサンプルコードで作った NorthwindProductsDataTable クラスが表示されているのが分かりますか?

    TestDataTable1 クラスがきちんとできていれば、同様に、[ビジネスオブジェクトの選択(C):]のドロップダウンリストの中に TestDataTable1 というのがあるはずです。

    注1:この時[データーコンポーネントのみを表示(S)]のチェックマークは外しておいてください。

    注2:Web アプリケーションプロジェクトの場合、クラス名の前に名前空間名(プロジェクト名と同じ)が付けられますので、ドロップダウンリストに表示されるのは <プロジェクト名>.TestDataTable1 になります。

    それを選択して[次へ(N)>]ボタンをクリックすると以下のダイアログが現われます。[SELECT]タブを表示してその[メソッドの選択(C):]を見ると TestDataTable1 クラスに定義した CreateDataTable が選択できるようになっているはずです。

    それを選択して ObjectDataSource のウィザードを完了させてください。

    その後、GridView の > マークをクリックし、[だーターソースの選択:]で ObjectDataSource を選び、[並べ替えを有効にする]にチェックマークを入れれば完了です。

    .aspx ファイル、.aspx.vb ファイルの方は上記のウィザードの操作だけで、自分では一行もコードを書かなくてもページは完成します。

    • 回答の候補に設定 星 睦美 2016年9月28日 5:50
    2016年9月8日 8:28
  • SurferOnWww様、ご回答ありがとうございます。
    SurferOnWww様がすぐに回答くださっていたのに、すぐに試せず回答が遅くなってしまい大変失礼しました。

    Web アプリケーションプロジェクトで作成しており、
    >特に、Web アプリケーションプロジェクトでは名前空間名も必要なことに注意
    まさしくここでエラーになっていました。

        <asp:ObjectDataSource ID="ObjectDataSource1" runat="server" 
            SelectMethod="CreateDataTable" 
            TypeName="CMTest.TestDataTable1">
        </asp:ObjectDataSource>
    
    「TypeName="CMTest.TestDataTable1"」にすることで、動きました!

    また、今までネットで調べていてもSurferOnWww様のようにウィザードで解説してくださっているページがあまりないため、コードを直接いじるほうがなじみがあったのですが、今回SurferOnWww様がウイザードで、しかも細かく解説してくださっているのでウィザードを使ってやってみました。
    こんなに簡単に実装できるのですね!
    今回、sqlDataSourceとobjectDataSourceの違いは扱うDBが違うという認識でしかおりませんでした(かつどちらも使うメリットを知らなかった)ので、今回objectDataSourceを使わせてもらうことになりそうですので、違いをしっかり把握してみたいと思います。

    そして、今回作成したいGridViewは、タイトル部分を日本語にしたり列を指定していたりしてますので、そちらに実装させてみてまた結果をご報告させてください。
    2016年9月10日 8:08
  • > sqlDataSourceとobjectDataSourceの違いは扱うDBが違うという認識でしかおりませんでした

    一番大きな違いは、SqlDataSource を使う場合はプレゼンテーション層とビジネスロジックを分離できないが、ObjectDataSource を使うとビジネスロジックをクラスファイル等に分離でき、プレゼンテーション層、ビジネスロジック、データ層に分割した三層構造システムを容易に構築できるということです。

    質問者さんの言われる「aspx側にSQL文が記述されるのに抵抗」というのは三層構造にできないところにあると思いますが、ObjectDataSource を使えばその点を解決できます。

    DB の違いを吸収すること(以下の記事のように SQL Server の代わりに XML ファイルを使うなど)もできますが、それは一番の目的ではないと思っています。

    XML ファイルの更新操作
    http://surferonwww.info/BlogEngine/post/2010/09/27/Delete-insert-and-update-operations-of-XML-file.aspx

    2016年9月10日 10:10
  • SurferOnWww様、ご回答ありがとうございます。

    そして、検証の結果のご報告が大変遅れまして、大変失礼いたしました。
    時間がとれなかったのと、調べていくうちにどんどん横道にそれていってしまっていました。

    本当はこの検証に検索用のテキストボックスも作って試してみたかったのですが、
    その時間がまだとれそうにないため、まずは現在のところまでを自分の環境でできることが確認できたご報告を
    させていただきにきました。

    自分なりのまとめとして、
    ・データソースコントロールを配置せずに行う場合、ソートは手組で行わなければならない。
     ( GridView1_Sorting イベントに手組する)
    ・SqlDataSource を使う場合はプレゼンテーション層とビジネスロジックを分離できない。
    ・ObjectDataSource を使うとビジネスロジックをクラスファイル等に分離でき、
     プレゼンテーション層、ビジネスロジック、データ層に分割した三層構造システムを容易に構築できる
    ・データを表示してソート可能にするだけなら、型付 DataSet / DataTable + TableAdapter を作らなくとも、
     ObjectDataSourceで対応できる。

    上記の「プレゼンテーション層とビジネスロジック」などということも、今回初めて意識しました。
    こちらも調べましたが、今段階でしっかり把握できたとは到底思えず、ただ今後は意識していきたいと思います。

    現段階でのPGです。

    html

    <form id="form1" runat="server">
            <div>GridViewテスト</div>
    
            <asp:ObjectDataSource ID="ObjectDataSource1" runat="server" 
                SelectMethod="CreateDataTable" 
                TypeName="CMTest.TestDataTable1">
            </asp:ObjectDataSource>
            <asp:GridView ID="GridView1" runat="server" 
                AllowSorting="True"
                DataSourceID="ObjectDataSource1">
                <Columns>
                    <asp:ButtonField ButtonType="Button" CommandName="SelMode" Text="表示"
                            ItemStyle-Width="5%"/>
                    <asp:BoundField DataField="No" HeaderText="No.">
                        <ItemStyle Width="5%" HorizontalAlign ="Right"></ItemStyle></asp:BoundField>
            ・・・複数列分定義
            </asp:GridView>
        </form>


    TestDataTable1.vb

    Public Class TestDataTable1
        Public Function CreateDataTable() As DataTable
    
            Dim dtbData As New DataTable
            Dim dtbDatanew As New DataTable
            With dtbDatanew.Columns
                .Add(New DataColumn("No", GetType(String)))
          '・・・複数列分定義
            End With
            Dim clsSql As New System.Text.StringBuilder     'SQL文用クラス
            '・・・定義いろいろ
    
            CreateDataTable = Nothing
    
            'SQL文生成
            clsSql.Clear()
            With clsSql
                .AppendLine("SELECT  ")
                '・・・これを繰り返してSQL文を作っていく
                .AppendLine("FROM TableA  ")
                '・・・ここにwhere条件を入れる。aspのtextboxの値を持ってこれるか?→後日対応
            End With
    
            'DB問合せ
            dtbData = DB問い合わせ関数(clsSql.ToString())
    
            'データセット
            For i As Integer = 0 To dtbData.Rows.Count - 1
                Dim row As DataRow = dtbDatanew.NewRow
    
                row.Item("No") = i + 1
          '・・・複数列分定義
    
                dtbDatanew.Rows.Add(row)
            Next
    
            Return dtbDatanew
        End Function
    End Class

    上記以外、特別なことをせずに実現可能。

    この段階で、いったん回答としてマークしたほうがよいのかな、と思いますが、この後に追加質問があれば別スレッドのほうがいいですよね?

    SurferOnWww、本当にご丁寧に説明してくださって、大変勉強になりました。
    またご支援いただくことがあると思います、その際はよろしくお願いします。

    trapemiya様も、情報をたくさん記載してください、ありがとうございました。


    2016年10月3日 13:06
  • > この後に追加質問があれば別スレッドのほうがいいですよね?

    追加質問は DataTable の作り方ではないかと想像していますが・・・

    表題の「SqlDataSourceを使わずに、GridViewの並べ替えができますでしょうか?」は解決しているようですので、このスレッドはクローズして、別の質問は新たに別のスレッドを立ててそこで質問していただくのが良いと思います。

    このフォーラムは技術者同士の情報交換の場所として提供されているそうで、例えば後日検索などでこのスレッドを訪れる技術者などのことを考えると、内容は表題と関係ある Q&A に絞った方がよさそうです。

    2016年10月4日 0:56