Search code examples
c#asp.netgridviewtemplatefield

Asp.net gridview with dropdown: Rowindex is always 0 in SelectedIndexChanged event


I have a problem that's been driving me nuts. Any help would be much appreciated. I have a grid view that displays what research items are assigned to what people. It should be possible to change the assignments using a dropdown. Here's the markup:

<asp:GridView ID="assignmentsGridView" runat="server" AutoGenerateColumns="false" CellPadding="2" Font-Size="Smaller" 
OnRowDataBound="assignmentsGridView_RowDataBound" OnRowCommand="assignmentsGridView_RowCommand">
<Columns>
    <asp:BoundField HeaderText="Ticker" DataField="Item" />
    <asp:BoundField HeaderText="Name" DataField="Name" />
    <asp:TemplateField HeaderText="Assignment" ItemStyle-HorizontalAlign="Center" ControlStyle-Font-Size="Small">
        <ItemTemplate>
            <asp:DropDownList ID="assignmentDropDown" runat="server" Visible="true"
                OnSelectedIndexChanged="assignmentDropDown_SelectedIndexChanged" AutoPostBack="true" >
                <asp:ListItem Text="Joe" Value="Joe"></asp:ListItem>
                <asp:ListItem Text="Aron" Value="Aron"></asp:ListItem>
                <asp:ListItem Text="Frank" Value="Frank"></asp:ListItem>
                <asp:ListItem Text="Ross" Value="Ross"></asp:ListItem>
                <asp:ListItem Text="Alex" Value="Alex"></asp:ListItem>
            </asp:DropDownList>
        </ItemTemplate>
    </asp:TemplateField>
    <asp:BoundField HeaderText="Analyst Complete" DataField="AnalystComplete" ItemStyle-HorizontalAlign="Center" ControlStyle-Font-Size="Smaller"/>
    <asp:TemplateField HeaderText="Mark Complete" ControlStyle-Font-Size="Smaller">
        <ItemTemplate>
            <asp:Button ID="completeButton" runat="server" Text="Incomplete" CommandArgument='<%# Eval("Item") %>' 
                CommandName="Complete" Font-Size="Smaller" />

        </ItemTemplate>
    </asp:TemplateField>

</Columns>
</asp:GridView>

I want to update the database when the user changes the SelectedIndex of the dropdown. I'm handling that here:

protected void assignmentDropDown_SelectedIndexChanged(object sender, EventArgs e)
{
    if (Session["IsCompany"] == null)
    {
        Session["IsCompany"] = assignmentsDropDown.SelectedValue == "Company";
    }
    bool? isCompany = Session["IsCompany"] as bool?;
    int isCo = (isCompany == true) ? 1 : 0;

    DropDownList ddl = sender as DropDownList;
    GridViewRow gvr = ddl.NamingContainer as GridViewRow;
    //ri is always 0!
    int ri = gvr.RowIndex;
    gvr = (GridViewRow)(((Control)sender).NamingContainer);
    //ri is always 0!
    ri = gvr.RowIndex;
    gvr = ((GridViewRow)ddl.Parent.Parent);
    //ri is always 0!
    ri = gvr.RowIndex;

    string selAnalyst = ddl.SelectedValue;
    //selAnalsyt always = initial value!
    selAnalyst = ddl.SelectedItem.Value;
    //selAnalsyt always = initial value!
    string ticker = gvr.Cells[0].Text;
    //ticker always is first data row
}

As you can see in the comments, no matter what row I click, the GridViewRow associated with the sender argument is always the first row. Also, the selected value of the dropdown is always its initial value, not the value selected by the user that fired off the SelectedIndexChanged event.

I've searched for a solution to this problem online and found that it is often caused by (1) the databind event getting fired on postback and (2) duplicate rows in the source data. I have checked for duplicate data and solved that problem and put primary keys on the appropriate table. I'm also making sure not to rebind the data on postback. Here's the Page_Load method:

protected void Page_Load(object sender, EventArgs e)
{
    if (!Page.User.Identity.IsAuthenticated)
    {
        FormsAuthentication.RedirectToLoginPage();
    }
    else
    {
        string userName = Page.User.Identity.Name;
        if (Session["UserMod"] == null)
        {
            this.userMod = new userModel(userName);
        }
        else
        {
            this.userMod = Session["UserMod"] as userModel;
        }

        if (!IsPostBack)
        {
            getPeopleList(userName);
            getTickerList(userName);


            if (this.userMod.IsResearchAdmin)
            {
                getAdminList();
            }
            else 
            {

                assignmentsDropDown.Visible = false;
                assignmentsGridView.Visible = false;
                assignmentsButton.Visible = false;
                assignmentsLabel.Visible = false;
                addTextBox.Visible = false;
                Image1.Visible = true;
                analystDropdown.Visible = false;
            }
        }
    }
}

I've got a RowDataBound method as well which sets the selected value of the dropdown for each row. When debugging, I have verified that the method is only running on the initial page load, not after the postback from SelectedIndexChanged. I was using an Update Panel as well, but removed that while trying to debug this.

I'm all outta ideas now, so I'm open to any suggestions. Thanks in advance


Solution

  • It looks like I've discovered an odd idiosyncrasy of asp.net. I had the following code in the RowDataBound method:

    if (IsCompany)
        {
            e.Row.Cells.Remove(e.Row.Cells[1]);
        }
    

    I removed this code and the dropdown SelectedIndexChanged method now works correctly. All 3 methods of getting the parent GridViewRow of the dropdownlist are working after changing the RowDataBound method so that it does not remove any cells in the gridview.

    I now believe that adding or removing cells during databinding causes unexpected problems with the SelectedIndexChanged event for dropdowns within GridViews. Here is the full code of the RowDataBoundMethod:

    protected void assignmentsGridView_RowDataBound(object sender, GridViewRowEventArgs e)
    {
        if (Session["IsCompany"] == null)
        {
            Session["IsCompany"] = entityDropDown.SelectedValue == "Company";
        }
        bool? isCompany = Session["IsCompany"] as bool?;
        bool IsCompany = (bool)isCompany;
    
        TableCell cell;
        if (e.Row.RowType == DataControlRowType.DataRow)
        {
    
            cell = e.Row.Cells[0];
            HyperLink link = new HyperLink();
            if (IsCompany)
            {
                link.NavigateUrl = "~/CompanyPage.aspx?Ticker=" + cell.Text;
            }
            else
            {
                link.NavigateUrl = "~/PeoplePage.aspx?PersonId=" + cell.Text;
            }
            link.Text = cell.Text;
            cell.Controls.Clear();
            cell.Controls.Add(link);
            /* with this code included, I experience problems with the SelectedIndexChanged event of the dropdown
            if (IsCompany)
            {
                e.Row.Cells.Remove(e.Row.Cells[1]);
            }
            */
    
            cell = e.Row.Cells[2];
    
            var dd = e.Row.FindControl("assignmentDropDown") as DropDownList;
            string assignment = ((DataRowView)e.Row.DataItem)["Assignment"].ToString();
            foreach (ListItem li in dd.Items)
            {
                if (li.Value == assignment)
                {
                    li.Selected = true;
                    break;
                }
            }
            cell = e.Row.Cells[4];
            if (cell.Text == "False")
            {
                //"completeButton"
                var cb = e.Row.FindControl("completeButton") as Button;
                cb.Enabled = false;
                cb.Visible = false;
            }
            else
            {
                ((Button)cell.FindControl("completeButton")).CommandArgument = e.Row.RowIndex.ToString();
            }
        }
    }
    

    If someone could confirm that removing cells inside rowdatabound causes problems with the selected index change event in asp.net I'd appreciate it.