質問者
gridViewのページングについて

質問
-
なのですが、スマートタグからデータベースを接続してバインドさせるとタグの中にページの設定が出来るようになっていますが、aspx.csにてデータベースに接続、クエリを行った時、gridView1.DataSource = command.execudeReader();
gridVIew1.DataBind();でデータは表示する事は出来るのですが、ページの設定をしようとプロパティでAllowPageingをtrueにするとエラーになってしまいます。データソースはpagingをサポートしていません。というメッセージになります。このようにコードでデータベースからgridViewで表示させた場合ページングを簡単にする方法はありますか??
すべての返信
-
KentaroM さんからの引用
例えば、ユーザーがデータを検索したい場合、クエリの中に入力情報をlike文付け加えなくてはいけないと思うので、どうしてもスマートタグからデータを取得できないと思うんですが、となるとdataReader()を使わざるを得ないと考えます。
そんなことはないですよ。SqlDataSourceのSelectCommandプロパティにSQL文をセットして、GridViewのDataBindメソッドを実行してあげればよいです。KentaroM さんからの引用
そうなると、検索したデータが限定されたとはいえ、まだ膨大なデータがあるとするとページングをしないといけない場合、言われたように手作業でページングを作らなくてはいけないのでしょうか。また、どのように作成していくのでしょうか。
SqlDateReaderを使用した場合のページングは、GridViewのAllowPagingをtrueにしておいて、GridViewのPageIndexChangingイベントで処理をしてあげればよいです。 -
アドバイスいただいて、いろいろリファレンスとかみているんですが、もう一度おさらいしますと、ページングを設定したいのですが、サポートされていません例外になってしまいます。gridViewのスマートタグからデータをバインドさせると問題なくページングのallowPagingをtrueにして出来ます。SqlConnection conn = new SqlConnection();
conn.ConnectionString = connection;
conn.Open();
SqlCommand command = new SqlCommand();
command.Connection = conn;
command.CommandText = "Select * from Login";
gridView1.DataSource = command.ExecuteReader();
gridView1.DataBind();
conn.close();
の様にして、データを取得します。そしてデザインビューからgridView1のプロパティでallowpagingをtrueにしてデバックするとエラーになります。目標は、ユーザーがさらにtextboxにキーワードを入れ絞込み検索をしたいので、クエリ文に%を足そうと考えています。アドバイスをいただいてSqlDataSourceのSelectCommandにクエリをセットしてバインドさせるという事ですが、これはC#のソースコードでやるのだと思われますが、具体的にどの様にすればいいでしょうか。(htmlコード内ではないですよね。)また、SqlDateReaderを使用した場合のページングは、GridViewのAllowPagingをtrueにしておいて、GridViewのPageIndexChangingイベントで処理をしてあげればよいです。という事ですが、これも具体的にイベントを生成して、e. とやってみると確かpageIndex(int型)等がインテリセンスでありましたがこれもどのようにすればいいのでしょうか?
-
KentaroM さんからの引用
また、SqlDateReaderを使用した場合のページングは、GridViewのAllowPagingをtrueにしておいて、GridViewのPageIndexChangingイベントで処理をしてあげればよいです。という事ですが、
大変申し訳ないです。SqlDataReaderはページングをサポートしていませんでした。私の手持ちのテストコードの中に、GridViewのページングをSqlDataReaderで実現している例があったものですから、うっかり動くものと勘違いしてしまいました。どうやら動かないことを試したコードだったようです。(^^;こちらでも話題にしていました。
DataGridのページ移動
http://forums.microsoft.com/msdn-ja/ShowPost.aspx?PostID=463085&SiteID=7ごめんなさい。m(_ _)m
KentaroM さんからの引用
アドバイスをいただいてSqlDataSourceのSelectCommandにクエリをセットしてバインドさせるという事ですが、これはC#のソースコードでやるのだと思われますが、具体的にどの様にすればいいでしょうか。
こちらを参考にしてみて下さい。検索機能
http://forums.microsoft.com/msdn-ja/ShowPost.aspx?PostID=579236&SiteID=7なお、追加で、下にパラメタライズドクエリとストアドプロシージャを利用する場合のサンプルを載せます。
protected void Button3_Click(object sender, EventArgs e)
{SqlDataSource1.SelectCommandType = SqlDataSourceCommandType.Text;
SqlDataSource1.SelectCommand = "select testid, test2id from test where testid = @id";
SqlDataSource1.SelectParameters.Clear();
SqlDataSource1.SelectParameters.Add("id", TypeCode.Int32, TextBox1.Text);
GridView1.DataBind();
}
protected void Button4_Click(object sender, EventArgs e){
SqlDataSource1.SelectCommandType = SqlDataSourceCommandType.StoredProcedure;
SqlDataSource1.SelectCommand = "test_selectid";
SqlDataSource1.SelectParameters.Clear();
SqlDataSource1.SelectParameters.Add("id", TypeCode.Int32, TextBox1.Text);
GridView1.DataBind();
}
なお、like句を使う場合には、パラメータ化しても特殊文字がエスケープされませんから、次のように事前にエスケープしておいてから、%などをくっつける必要があります。
s = s.Replace("[", "[[]");
s = s.Replace("%", "[%]");
s = s.Replace("_", "[_]");(参考)
SQL インジェクション
http://www.microsoft.com/japan/msdn/security/guidance/secmod87.mspx#EBEAC -
おかげさまで、SqlDataSourceのselectQueryプロパティからtextboxを選択できそれとパラメータに連結させる事が出来ました。あとはボタンクリックイベントに GridView1.DataSource = sqlDataSource1;
GridView1.DataBind(); とするとコードは2行で検索するデザインが出来上がりました。ナイスです。後は、当初の目的である、ページングです。gridView1のallowPagingをtrueにして今度はコンパイルできました。しかし、次のページを開こうとするとpageIndexChangingにコードを加えなさいとメッセージがでます。試しにe. とやると確かにクリックしたページのindexが出ますがどう設定していけばいいのでしょうか??
-
その後、表に取ってきたデータの一部を計算して表示する事になり、結局dataReaderを使ってgridviewにバインドする事になっています。(そのうち集計するデータだけ、while(reader.Read()){ dataItem.Revenue = reader["rev"];で取り、計算します。)そうなるとまた問題になってくるのがソートです。htmlタグのgridviewのdataSourceIDにsqlDataSourceなどがバインドされていればプロパティのallowSortingをtrueにして問題ないのですが、コードからgridView1.DataSource = reader;
gridView1.DataBind(); の様にバインドした場合ソートするとエラーになります。gridVIew1の属性に何か追加するのか一工夫必要な様です。
-
アドバイスありがとうございます。結局いろいろ試行錯誤しまして、ソートをIComparerでやり(その方がクエリを呼ばないので早い)ました。ページングはPageIndexChangingイベントで GridView1.PageIndex = e.NewPageIndex; で解決しました。ただ、ただ、その後機能を追加するにあたり、動的にデータベースからGridViewにバインドしながら、静的なインデックスをつけようと思い、いろいろ試しGridVIewのDataRowBoundイベントで、
if(e.Row.RowType == DataControlRowType.DataRow)
{
int i;
i++;
e.Row.Cells[0].Text = i.ToString();
}
で、ソートしてもリスト番号は変わらない様には出来ました。ただ、これだと、ページングをした時にまた、1,2,3,4...となってしまい、理想は例えばpegesizeが10なら、次のページは11から表示したいのです。だとすると、PageIndexを判別していないとどんどん足されていってしまうので、PageIndexChangingのPageIndexをViewStateで記憶させておけばいいと思ったのですが、なんと、ディバックしてみるとページをクリックしても先にDataRowBoundイベントが始まり、indexを認識できないではありませんか!さらにindexを認識出来たとしても、
if (ViewState["pageIndex"] != null)
{
int pIndex = (int)ViewState["pageIndex"];
}
.......
int pSize = GridView1.PageSize;
int pageNum = pSize * pIndex;
if (e.Row.RowType == DataControlRowType.DataRow)
{
pageNum++;
e.Row.Cells[0].Text = pageNum.ToString();
}
みたいに考えたのですが、rowが生成される度にpSize * pIndexも通過してしまうので、どのみちうまくいきません。なので、今のところsizeを多めにして1++;のようなページングされても1からになっちゃうものでやっています。
この状況でいい解決法があるでしょうか??
また、dataTableを使うと言われていましたが、その方法も軽く教えていただけたらうれしいです。
-
KentaroM さんからの引用 また、dataTableを使うと言われていましたが、その方法も軽く教えていただけたらうれしいです。
ここを参考にしてみて下さい。C# GridView Sorting/Paging w/o a DataSourceControl DataSource
http://community.strongcoders.com/content/CSGridViewSortingPaging.aspx -
その後、dataViewを使う方法を試しています。以下の感じでやっています。OracleDataAdapter dAdapter = new OracleDataAdapter(command); DataSet dSet = new DataSet(); dAdapter.Fill(dSet); DataTable dTable = (DataTable)dSet.Tables[0]; DataView dView = new DataView(dTable); GridView1.DataSource = dView; GridView.DataBind(); Session["dVIew"] = dView;でGridViewに表示できました。ページング、ソートしてもインデックスが変わらない方法は、ItemTemplateのtext属性に<%# Container.DataItemIndex + 1 %>として解決出来ました。それで、DataViewでバインドした場合、例えば、Priceカラムがあるとして、その合計値をFooterに表示したいのですが、下記のようにやったらうまくいきませんでした。
double listPrice = 0;
protected void DataBound(object sender, DataBoundEventArgs e)
{
string strList = "";
if (e.Row.RowType == DataControlRowType.DataRow)
{
double listPrice = double.Parse(e.Row.Cells[10].Text);
listPriceTotal += listPrice;
strList = String.Format("{0:c}", listPriceTotal);
Label3.Text = strList;}
if (e.Row.RowType == DataControlRowType.Footer)
{
e.Row.Cells[10].Text = Label3.Text;
}
}Label3はHidden的に使用しています。
具体的に何がうまくいかないかと言うと、ListPrice列の合計をFooterに表示させたいのですが、1ページ目に表示される合計値は全体の合計ではなく、1ページ目だけの合計値になり2ページ目をクリックすると全体の合計になり、1ページ目に戻っても全体の合計値のままになっています。breakpointでどのように合計が加算されているか調べているのですが、dataBoundイベント発生時とpostbackの関係のせいなのか、思うような結果が得られません。
-
KentaroM さんからの引用 ちなみにrowdataboundイベントでやっております。 イベントプロシージャの型が、GridViewRowEventArgsではないため、RowDataBoundではないと思ってしまいました。
KentaroM さんからの引用 dataTable(dataView)を使ってデータを取得した場合、列の計算はこの様にRowDataBound内でやるんでいいのでしょうか??。 ページングしない時はうまくいくようですが、ページングするとダメなようです。変わりにPreRenderイベントで行うと、うまくいきます。C#ですが、以下のような感じでテストしました。
int sumwk = 0;
protected void GridView1_PreRender(object sender, EventArgs e)
{
foreach (GridViewRow gvr in GridView1.Rows)
{
sumwk += int.Parse(gvr.Cells[0].Text);
}
this.Label1.Text = sumwk.ToString();
}
-
preRenderイベントで早速試しましたが、rowDataBoundイベントと同じ結果になってしまいます(ページのサイズ分の合計しか表示されない)。試しにこの二つにブレイクポイントを置いてみましたが、たしかにpreRenderイベントが先に発生します。ただ、データベースからデータを取得する前(ウェブページを最初に起動する時)なので、Label1は0と表示され、データ取得後はrowDataBoundイベントが先に起こるので、結果はどちらも一緒になってしまいます。ただ、foreach(GridViewRow gvr in GridView1.Rows)でrowをループする仕方を教わったので、どこか他のイベントで(つまり、gridViewのpageSizeを問わない)使えればいいのですが・・・。