none
ソート実行後にSqlDataSourceのSelectコマンドを変更するとエラー RRS feed

  • 質問

  • いつもお世話になります。

    開発環境:VWD Express、.NET4.0です。

    単一のSqlDataSource、GridViewを利用し、動的にSqlDataSourceのSelectコマンドを変化させ、GridViewに表示できないかと考え、下記のようなコードを作成しました。

    そうしたところ、Button1,2をそれぞれクリックすると、GridViewは期待通り変化するのですが、列名をクリックしてソートを行ったあとに一方の表を表示するボタンを押すと、「System.IndexOutOfRangeException: 列 担当者名 が見つかりません。」とエラーが表示されます。

    同様に、ページングを行ったあとに一方の表を表示するボタンを押すと、表示はされますが、1ページ目ではなく直前表示していたページが表示されてしまいます。

    ソートの項目、ページのインデックスを変更、修正することは出来ないでしょうか?よろしくお願いします。

      <asp:Label ID=Label1 runat="server" Text="Label"></asp:Label>

      <asp:Button ID="Button1" runat="server" Text="Button1" />   
      <asp:Button ID="Button2" runat="server" Text="Button2" />

      <asp:SqlDataSource ID="SqlDataSource1" runat="server" 
            ConnectionString="<%$ ConnectionStrings:ConXXX %>"
            ProviderName="<%$ ConnectionStrings:ConXXX.ProviderName %>">
      </asp:SqlDataSource>
      <asp:GridView ID="GridView1" runat="server" DataSourceID="SqlDataSource1"
       AllowPaging="True" AllowSorting="True">
      </asp:GridView>

    コード

    Protected Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
      Label1.Text = "Button1"
      SqlDataSource1.SelectCommand = "select 品目番号,品目名 from 品目情報"
    End Sub

    Protected Sub Button2_Click(sender As Object, e As EventArgs) Handles Button2.Click
      Label1.Text = "Button2"
      SqlDataSource1.SelectCommand = "select 担当者, 担当者名, 担当者名カナ from 担当者情報"
    End Sub

    Private Sub GridView1_PageIndexChanging(sender As Object, e As System.Web.UI.WebControls.GridViewPageEventArgs) Handles GridView1.PageIndexChanging
      If Label1.Text = "Button1" Then
        Button1_Click(sender, e)
      ElseIf Label1.Text = "Button2" Then
        Button2_Click(sender, e)
      End If
    End Sub

    Private Sub GridView1_Sorting(sender As Object, e As System.Web.UI.WebControls.GridViewSortEventArgs) Handles GridView1.Sorting
      If Label1.Text = "Button1" Then
        Button1_Click(sender, e)
      ElseIf Label1.Text = "Button2" Then
        Button2_Click(sender, e)
      End If
    End Sub

    2014年10月28日 7:22

回答

  • 検証に使ったコードです。C# ですが分かりますよね? データベースは Microsoft が無償で提供しているサンプルの Northwind です。

    <%@ Page Language="C#" %>
    
    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
     "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
    
    <script runat="server">
        protected string queryProducts = "SELECT [ProductID], [ProductName], [CategoryID], [UnitPrice] FROM [Products]";
        protected string queryOrders = "SELECT [OrderID], [CustomerID], [ShippedDate], [ShipCountry] FROM [Orders]";
        
        protected void Products_Click(object sender, EventArgs e)
        {
            if (Label1.Text != "Products")
            {
                Label1.Text = "Products";
                SqlDataSource1.SelectCommand = queryProducts;
                GridView1.PageIndex = 0;
                GridView1.Sort("", SortDirection.Ascending);            
            }
        }
    
        protected void Orders_Click(object sender, EventArgs e)
        {
            if (Label1.Text != "Orders")
            {
                Label1.Text = "Orders";
                SqlDataSource1.SelectCommand = queryOrders;
                GridView1.PageIndex = 0;
                GridView1.Sort("", SortDirection.Ascending);            
            }
        }
    
        protected void GridView1_PageIndexChanging(object sender, GridViewPageEventArgs e)
        {
            if (Label1.Text == "Products")
            {
                SqlDataSource1.SelectCommand = queryProducts;
            }
            else if (Label1.Text == "Orders")
            {
                SqlDataSource1.SelectCommand = queryOrders;
            }
        }
    
        protected void GridView1_Sorting(object sender, GridViewSortEventArgs e)
        {
            if (Label1.Text == "Products")
            {
                SqlDataSource1.SelectCommand = queryProducts;
            }
            else if (Label1.Text == "Orders")
            {
                SqlDataSource1.SelectCommand = queryOrders;
            }
        }
    </script>
    
    <html xmlns="http://www.w3.org/1999/xhtml">
    <head runat="server">
        <title></title>
    </head>
    <body>
        <form id="form1" runat="server">
        <div>
            <asp:Label ID="Label1" runat="server" Text="Label"></asp:Label>
            <asp:Button ID="Button1" runat="server" Text="Products" OnClick="Products_Click" />
            <asp:Button ID="Button2" runat="server" Text="Orders" OnClick="Orders_Click" />
    
            <asp:SqlDataSource ID="SqlDataSource1" runat="server" 
                ConnectionString="<%$ ConnectionStrings:Northwind %>" >
            </asp:SqlDataSource>
    
            <asp:GridView ID="GridView1" runat="server" AllowPaging="True" 
                AllowSorting="True"  DataSourceID="SqlDataSource1" 
                OnPageIndexChanging="GridView1_PageIndexChanging" 
                OnSorting="GridView1_Sorting">            
            </asp:GridView>
        </div>
        </form>
    </body>
    </html>
    

    2014年10月28日 14:37

すべての返信

  • なぜ異なる二つのテーブル「品目情報」と「担当者情報」を表示するのに一つの SqlDataSource / GridView を共通で使う必要があるのでしょうか? そこのところでかなり無理なことをしているように思います。

    「品目情報」テーブル用と「担当者情報」テーブル用で別々の SqlDataSource / GridView を使えば問題はなくなると思いますが、どうしてそうしないのですか?

    同時に二つの GridView を表示したくない(Button クリックで「品目情報」テーブルと「担当者情報」テーブルの表示を切り替えたい)ということであれば、GridView を別々の Panel に入れて、Panel の表示非表示を切り替えてはいかがですか?

    2014年10月28日 8:05
  • 返信ありがとうございます。説明不足で申し訳ありません。

    今回は2つの検索でしたが、実際作成したのはCommandのSQL部分を引数で渡して、検索結果をascx化して部品化しようと考えたからです。部品化すると、たとえば親画面から、キーワードを検索させてそれに該当するデータ一覧を小画面でModalPopupExtenderで表示して、その表の該当データを選択することにより、親画面に関連データを戻して表示させるというものです。

    実際ここまでの部分は完成して、質問しました問題以外は正しく動いています。

    この部分までを記載するとかなりの量になるので、問題が発生する部分のみを簡略化して再構築して、質問させていただきました。

    2014年10月28日 9:32
  • 直近のレスに書かれた質問者さんのやりたいことのイメージが湧きませんので、一番最初の質問に書かれたことについてのみレスします。

    質問者さんはどのぐらい ASP.NET Web Forms アプリの開発経験があるのでしょうか? Web アプリはステートレスで、ASP.NET Web Forms アプリでそれをステートフルにする仕組みが ViewSate, Postback, サーバーコントロールということは理解されているでしょうか?

    今回問題となっているソートとページングにおいては、SortExpression と PageIndex プロパティがキーです。

    ASP.NET Web Forms アプリは現在表示されている GridView の情報(SortExpression と PageIndex を含む)を ViewState に保持しています。

    ボタンクリックなどで Postback されると、ブラウザは ViewState の情報をサーバーに送信し、サーバーではその情報を元に、上で言う「現在表示されている GridView 」を復元します。SortExpression と PageIndex も上で言う「現在表示されている GridView 」のものになります。

    ここからは検証してないので想像ですが、

    (1) 「System.IndexOutOfRangeException: 列 担当者名 が見つかりません。」というのは、ViewState の SortExpression が "担当者名" なのに、ボタンクリックで切り替えたクエリで取得したデータには "担当者名" 列が含まれていないから、

    (2) 「1ページ目ではなく直前表示していたページが表示されてしまいます。」というのは、ViewState の PageIndex が使われるから、

    ・・・だと思います。

    なので、ボタンクリックのイベントハンドラで SortExpression と PageIndex をそれぞれ "" と 0 すれば、初期の状態(ソートされてない 1 ページ目)が表示されると思います。(未検証です。試してみてください)


    ところで、最初の質問のコードにあった GridView1_PageIndexChanging と GridView1_Sorting の目的は何ですか? 不要ではないですか?



    【追伸】

    実際にコードを書いて検証してみました。

    GridView1_PageIndexChanging と GridView1_Sorting の目的が分かりました。不要ではなかったですね、すみません。

    あと、GridView.SortExpression 読み取り専用なので、直接 "" は設定できませんね。GridView1.Sort("", SortDirection.Ascending) のようにするほかなさそうです。

    別途、検証に使ったコードをアップしておきます。

    • 編集済み SurferOnWww 2014年10月28日 14:33 追伸追加
    • 回答の候補に設定 星 睦美 2014年10月29日 0:48
    2014年10月28日 11:56
  • 検証に使ったコードです。C# ですが分かりますよね? データベースは Microsoft が無償で提供しているサンプルの Northwind です。

    <%@ Page Language="C#" %>
    
    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
     "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
    
    <script runat="server">
        protected string queryProducts = "SELECT [ProductID], [ProductName], [CategoryID], [UnitPrice] FROM [Products]";
        protected string queryOrders = "SELECT [OrderID], [CustomerID], [ShippedDate], [ShipCountry] FROM [Orders]";
        
        protected void Products_Click(object sender, EventArgs e)
        {
            if (Label1.Text != "Products")
            {
                Label1.Text = "Products";
                SqlDataSource1.SelectCommand = queryProducts;
                GridView1.PageIndex = 0;
                GridView1.Sort("", SortDirection.Ascending);            
            }
        }
    
        protected void Orders_Click(object sender, EventArgs e)
        {
            if (Label1.Text != "Orders")
            {
                Label1.Text = "Orders";
                SqlDataSource1.SelectCommand = queryOrders;
                GridView1.PageIndex = 0;
                GridView1.Sort("", SortDirection.Ascending);            
            }
        }
    
        protected void GridView1_PageIndexChanging(object sender, GridViewPageEventArgs e)
        {
            if (Label1.Text == "Products")
            {
                SqlDataSource1.SelectCommand = queryProducts;
            }
            else if (Label1.Text == "Orders")
            {
                SqlDataSource1.SelectCommand = queryOrders;
            }
        }
    
        protected void GridView1_Sorting(object sender, GridViewSortEventArgs e)
        {
            if (Label1.Text == "Products")
            {
                SqlDataSource1.SelectCommand = queryProducts;
            }
            else if (Label1.Text == "Orders")
            {
                SqlDataSource1.SelectCommand = queryOrders;
            }
        }
    </script>
    
    <html xmlns="http://www.w3.org/1999/xhtml">
    <head runat="server">
        <title></title>
    </head>
    <body>
        <form id="form1" runat="server">
        <div>
            <asp:Label ID="Label1" runat="server" Text="Label"></asp:Label>
            <asp:Button ID="Button1" runat="server" Text="Products" OnClick="Products_Click" />
            <asp:Button ID="Button2" runat="server" Text="Orders" OnClick="Orders_Click" />
    
            <asp:SqlDataSource ID="SqlDataSource1" runat="server" 
                ConnectionString="<%$ ConnectionStrings:Northwind %>" >
            </asp:SqlDataSource>
    
            <asp:GridView ID="GridView1" runat="server" AllowPaging="True" 
                AllowSorting="True"  DataSourceID="SqlDataSource1" 
                OnPageIndexChanging="GridView1_PageIndexChanging" 
                OnSorting="GridView1_Sorting">            
            </asp:GridView>
        </div>
        </form>
    </body>
    </html>
    

    2014年10月28日 14:37
  • 検証までしていただき、ありがとうございます。

    当初、GridView.SortExpression を指定するコードを書いたら、「読み取り専用」となったので、行き詰ってしまい質問した次第です。

    開発経験が浅くC#未経験なので、じっくりコードを見て、検証し、報告させていただきます。

    <追伸>

    初めに質問しましたコード(VB)に

    GridView1.PageIndex = 0

    を追加しましたところ、無事目的通り動きましたが、

    GridView1.Sort("", SortDirection.Ascending)

    を追加後、実行すると、Private Sub GridView1_Sorting内の

    If Label1.Text = "Button1" Then

    の場所で、「System.StackOverflowException はハンドルされませんでした。」「WebApplication1.dll で発生しました。」

    のエラーが出てしまいます。調査中ですが、まだ判らない状況です。

    2014年10月29日 0:10
  • > GridView1.Sort("", SortDirection.Ascending)
    > を追加後、実行すると、Private Sub GridView1_Sorting内の
    > If Label1.Text = "Button1" Then
    > の場所で、「System.StackOverflowException はハンドルされませんでした。」
    > 「WebApplication1.dll で発生しました。」のエラーが出てしまいます。

    それは GridView1_Sorting 内でボタンクリックのイベントハンドラ(Button1_Click または Button2_Click)を実行しているからでしょう。

    StackOverflow するのは、Buttonx_Click が呼び出されるとその中で GridView1.Sort("", ...) が実行されて Sorting イベントが発生し、GridView1_Sorting が呼び出されその中でButtonx_Click が呼び出されるというように無限ループになるからでしょう。

    GridView1_Sorting 内でボタンクリックのイベントハンドラなど呼ぶ必要はなくて、私が書いた検証用コードのように SqlDataSource1.SelectCommand を設定するだけで澄むはずです。

    せっかく問題なく動くサンプルコードまで書いてアップしたのですからよく見ていただきたいと思います。


    【追伸】

    C# が読めなければ、以下のような変換サービスを行っているサイトもありますので使ってみてください。イベントハンドラの設定(特に VB.NET の Handles ... というあたり)まで含めて完璧に変換してくれるわけではないですが、人が読んで十分理解できる程度には変換してくれるはずです。

    Convert C# to VB.NET
    http://www.developerfusion.com/tools/convert/csharp-to-vb/


    • 編集済み SurferOnWww 2014年10月29日 3:26 追伸追加&誤字訂正
    2014年10月29日 3:05
  • すみませんでした。GridView1_Sorting内のButtonx_Clickをコメント化していませんでした。

    修正後、無事に動作することが確認できました。

    また、VBへのコンバートの件も教えていただきありがとうございました。

    大変勉強になりました。

    2014年10月29日 4:19