繁体   English   中英

ASP.NET C# Gridview 抛出索引超出范围

[英]ASP.NET C# Gridview throwing Index out of range

我正在开发一个用户控件,该控件显示来自网格视图上的 SQL 表的可用存储,供用户选择和填充文本框。 目前我能够将所有内容正确地数据绑定到 gridview 并使用每行内的选择按钮运行操作。 我目前的问题是使用“rows [index]”时我无法将单元格值返回到变量中。 当我达到这一点时,即使我的索引值在调试时是正确的,我也会得到一个 Index is out of range 错误。 出于实验目的,我在不同的迭代中为索引手动设置了 0 和 1 值,但我仍然得到错误,就好像我的 gridview 不包含任何行一样。

我选择行的方法是使用带有由 ButtonField 触发的 OnRowCommand 的 Gridview。

查看其他一些类似的问题和答案,我发现大多数问题是人们获得负值或不使用 DataKeyNames,但这不是我的情况。 我仍然无法修复此错误

我的gridview代码是这样的:

<asp:GridView ID="gvStore" runat="server" AutoGenerateColumns="False" Visible="false" DataKeyNames="Name" OnRowCommand="gvExecSelectSTR_RowCommand">
            <Columns>            
                <asp:BoundField DataField="Name" HeaderText="Name" />            
                <asp:ButtonField ButtonType="Link" Text="Select" CommandName="SelectStore"/> 
            </Columns>
        </asp:GridView>

我的按钮操作

protected void gvExecSelectSTR_RowCommand(object sender, GridViewCommandEventArgs e)
        {
            if (e.CommandName == "SelectStore")
            {
                int index = Convert.ToInt32(e.CommandArgument); //Up to this point i get correct row number for index
                GridViewRow row = gvStore.Rows[index]; //This row triggers my error
                IDStore.Text = row.Cells[0].Text;
            }
        }

此外,这是我用来将所有内容数据绑定到我的 GridView 的方法

private void GridViewStore()
        {
            try
            {
                DataTable StoreTable = new DataTable();
                StoreTable.Columns.AddRange(new DataColumn[1]
                {
                    new DataColumn("Name",typeof(string))                    
                });

                //Can't show internal SQL commands for this but here i read and load
 the SQLscript that selects the table and load it into the reader variable "readerStore"//

                if (readerStore.HasRows)
                {
                    while (readerStore.Read())
                    {  
                        StoreTable.Rows.Add(readerStore["Name"].ToString());                            
                    }
                }
                gvStore.DataSource = StoreTable;
                gvStore.DataBind();
                readerStore.Close();
            }
            catch (Exception ex)
            {
                OnErrorOccurred(new ErrorOccurredEventArgs("Grid View Bind Message" + ex.Message, ex));
            }
        }

我有什么设置错误吗?

你试过而不是

 GridViewRow row = gvStore.Rows[index];

这样做

 GridViewRow row = (GridViewRow)gvStore.Rows[e.RowIndex];  

为什么要打扰行命令? 你真的不需要那个事件模型,而且在大多数情况下,它只会导致额外的痛苦。

如果您在单击按钮时需要一个行 PK id? 然后使用数据键。 Datakeys 非常好有几个原因。

对于列表中的顶部,将数据库 PK 暴露给客户端总是一个巨大的安全漏洞。 (标记可能会被弄乱,因此您现在使用的数据库 ID 可能是标记中更改的任何内容。

所以,简单的网格视图是这样说的:

        <asp:GridView ID="GHotels" runat="server" CssClass="table" AutoGenerateColumns="false"
            width="50%" DataKeyNames="ID" >
            <Columns>
                <asp:BoundField DataField="FirstName" HeaderText="FirstName"  />
                <asp:BoundField DataField="LastName" HeaderText="LastName"    />
                <asp:BoundField DataField="HotelName" HeaderText="HotelName"  />
                <asp:BoundField DataField="Description" HeaderText="Description" ItemStyle-Width="270" />

                <asp:TemplateField HeaderText = "View" ItemStyle-HorizontalAlign ="Center" >
                    <ItemTemplate>
                    <asp:Button ID="cmdRow" runat="server" Text="View" 
                       CssClass="btn"  />
                    </ItemTemplate>
                </asp:TemplateField>
            </Columns>
        </asp:GridView>

请注意,上面“ID”中的数据键设置是我们的行 PK id,非常好的是我们没有,显示,随时在标记中显示 PK 行 id !!!

然后要填写的代码是这样的:

    protected void Page_Load(object sender, EventArgs e)
    {
        if (!IsPostBack)
            LoadData();
    }

    void LoadData()
    {
        using (SqlConnection conn = new SqlConnection(Properties.Settings.Default.TEST4))
        {
            string strSQL = 
                "SELECT * FROM tblHotelsA ORDER BY HotelName";
            using (SqlCommand cmdSQL = new SqlCommand(strSQL, conn))
            {
                DataTable rstData = new DataTable();
                conn.Open();
                rstData.Load(cmdSQL.ExecuteReader());
                GHotels.DataSource = rstData;
                GHotels.DataBind();
            }
        }
    }

我们现在有了这个:

在此处输入图像描述

现在,我们需要一个按钮的点击事件。 对于大多数按钮,您只需双击按钮即可添加/连接事件。 但是,由于按钮在 GV 内部,所以我们不能这样做。 但是我们仍然可以通过标记添加事件。

因此,在 onclick="" 的标记类型中执行此操作时,intel-sense 会弹出创建事件的选项。 像这样说:

在此处输入图像描述

(在引号下,按 ctrl-space 以弹出该 Intel-sense 对话框)。

好的,选择创建新事件。

现在,翻到后面的代码,该按钮应该有一个简单的单击事件。

因此,使用此代码:

    protected void cmdRow_Click(object sender, EventArgs e)
    {
        Button btn = sender as Button;
        GridViewRow gRow = btn.NamingContainer as GridViewRow;

        Debug.Print("Row index click = " + gRow.RowIndex);
        Debug.Print("ID (data keys) = " + GHotels.DataKeys[gRow.RowIndex]["ID"].ToString());
        Debug.Print("FirstName = " + gRow.Cells[0].Text);
        Debug.Print("Last Name = " + gRow.Cells[1].Text);
    }

输出:

在此处输入图像描述

因此,通过这个简单的设置:

如果我在 GV 上有多个按钮,那么可以获得一个简单的单独单击事件和代码存根。 没有凌乱的行命令(然后检查一些行命令参数)。

我很容易得到行索引。

我得到了从不暴露数据库 PK id 背后的代码(来自 datakeys)

我可以自由地从当前行获取/抓取任何单元格值。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM