Search code examples
asp.netdevexpressaspxgridview

ASPxGridView: Cannot hide custom command button selectively. Is it a bug on my side?


Summary: When trying to hide a custom command button in ASPxGridView command column, it hides the buttons strangely, one of the buton texts appears in the filter row entry, and the button handlers stop working. The error message like "More controls with the DXCBtn_0_9_-1 were found. The FindControl requires unique identifiers of the controls" (loosely translated) appeares.

I am using DevExpress 14.2.3.0, the grid view is nested in another grid views and in ASPxRoundPanels.

Details: The command column contains the following custom buttons...

    <dx:GridViewCommandColumn VisibleIndex="9">
        <CustomButtons>
            <dx:GridViewCommandColumnCustomButton ID="btnClose" Text="Close as done">
            </dx:GridViewCommandColumnCustomButton>
            <dx:GridViewCommandColumnCustomButton ID="btnReopen" Text="Reopen">
            </dx:GridViewCommandColumnCustomButton>
        </CustomButtons>
    </dx:GridViewCommandColumn>

The buttons are displayed fine (as links) and the following code handles them nicely:

protected void gvMilestoneTasks_CustomButtonCallback(object sender, ASPxGridViewCustomButtonCallbackEventArgs e)
{
    ASPxGridView grid = sender as ASPxGridView;
    if (e.ButtonID == "btnClose")
    {
        int milestoneID = Convert.ToInt32(grid.GetRowValues(e.VisibleIndex, "ID"));
        DbUtil.ExecuteNonQuery(String.Format("EXEC sp_milestone_tasks_close_open {0}, 0, N'{1}'", 
                                        milestoneID, Page.User.Identity.Name));
        grid.DataBind();
    } else if (e.ButtonID == "btnReopen")
    {
        int milestoneID = Convert.ToInt32(grid.GetRowValues(e.VisibleIndex, "ID"));
        DbUtil.ExecuteNonQuery(String.Format("EXEC sp_milestone_tasks_close_open {0}, 1, N'{1}'",
                                        milestoneID, Page.User.Identity.Name));
        grid.DataBind();
    }
}

(That is, dedicated SQL stored procedures with different arguments are called [notice the second argument if curious], and the grid.DataBind(); is used to refresh the content of the status column.)

I want to show only one of the buttons. When the row shows the open one, only the Close button should be displayed and active. When the row shows it was closed earlier, only the Reopen button should be visible and active.

I tried to handle visibility in the CustomButtonInitialize event handler (based on the status info -- when the closed is NULL in the database, then it is open; otherwise, the closed contains the datetime of when it was closed):

protected void gvMilestoneTasks_CustomButtonInitialize(object sender, ASPxGridViewCustomButtonEventArgs e)
{
    if (e.VisibleIndex == -1)
        return;

    ASPxGridView grid = sender as ASPxGridView;
    if (e.ButtonID == "btnClose")
    {
        object o = grid.GetRowValues(e.VisibleIndex, "closed");
        bool flagVisible = Convert.IsDBNull(o);
        e.Visible = flagVisible ? DefaultBoolean.True : DefaultBoolean.False;
    }
    else if (e.ButtonID == "btnReopen")
    {
        object o = grid.GetRowValues(e.VisibleIndex, "closed");
        bool flagVisible = !Convert.IsDBNull(o);
        e.Visible = flagVisible ? DefaultBoolean.True : DefaultBoolean.False;
    }
}

I can observe also an error message in the browser in the sense "More controls with the DXCBtn_0_9_-1 were found. The FindControl requires unique identifiers of the controls" (loosely translated) -- this is hidden somewhere deep in the controls; I am not using the FindControl.

Where is the bug hidden?

Thanks for your help.


Solution

  • The reason is that FilterRow behaves as another row of the displayed grid. It is considered to be another visible row. This way the handler should return early also in the case when the cell type is detected as filter.

    if (e.VisibleIndex == -1 || e.CellType == GridViewTableCommandCellType.Filter)
        return;
    

    Alternatively, that part can be changed to...

    if (e.CommandCellType != GridViewTableCommandCellType.Data)
        return;