7

I've struggled a lot with how to show a modal panel on click on a button inside a grid view.

To context: I have a data row with a string field that can contain a simple text or a base 64 encoded image, so I'm using a custom template to define when to show the raw content or a button "View Image". This image will be opened on a modal panel that should rise up on button click.

This is the Panel I've created as a control (ascx):

<asp:Panel ID="pnlModalOverlay" runat="server" Visible="true" CssClass="Overlay">
    <asp:Panel ID="pnlModalMainContent" runat="server" Visible="true" CssClass="ModalWindow">
        <div class="WindowTitle">
            <asp:Label ID="lbTitle" runat="server" />
        </div>
        <div class="WindowBody">
            <asp:Panel ID="pnlContent" runat="server" Visible="true">
                <asp:Image ID="imgContent" runat="server" CssClass="ImageView" />
            </asp:Panel>
            <div class="Button">
                <asp:Button ID="btnOk" runat="server" class="btn btn-default " Text="Close" OnClientClick="loadingPanel.Show();" />
            </div>
        </div>
    </asp:Panel>
</asp:Panel>

And this is the page and ASPxGridView where I wanna use it:

<asp:UpdatePanel ID="UpdatePanel1" runat="server" UpdateMode="Conditional" ChildrenAsTriggers="true">
    <ContentTemplate>
        <div style="margin-top: 12px;">
            <asp:Button type="button" ID="btnShowImage" AutoPostBack="true" class="btn btn-default navbar-right" Text="Show Image"
                runat="server" Style="margin-left: 5px;" OnClientClick="loadingGridPanel.Show();" />
        </div> 

        <!-- Some data filter controls  -->

        <MyWorkspace:AlertModal ID="alertModal" runat="server" Visible="false" />
        <MyWorkspace:ImageModal ID="imageModal" runat="server" Visible="false" />

    </ContentTemplate>
    <Triggers>
        <asp:AsyncPostBackTrigger ControlID="mainGrid" />
    </Triggers>
</asp:UpdatePanel>

<MyWorkspace:GridViewWrapper ID="mainGrid" runat="server" Visible="true" />

Codebihind:

public partial class MyPage : System.Web.UI.Page
{
    protected override void OnInit(EventArgs e)
    {
        base.OnInit(e);

        btnShowImage.Click += new EventHandler(ShowImage); // This call works fine
    }

    protected void Page_Load(object sender, EventArgs e)
    {
        try
        {
            if (!IsPostBack)
            {
                mainGrid.CanEditItems = true;

                mainGrid.CustomTemplates.Add(new CustomColumnTemplate { columnName = "Id", template = new LinkColumn(CreateParentLink, "Go to parent") });
                mainGrid.CustomTemplates.Add(new CustomColumnTemplate { columnName = "Value", template = new ButtonColumn(ShowImage, "View Image") }); // This one doesn't works
            }
        }
        catch (Exception ex)
        {
            modalAlerta.Show("Page_Load", ex.Message, false, false, "");
        }
    }

    void ShowImage()
    {
        modalImagem.Show(); // Set Modal's Visible property to True
        // UpdatePanel1.Update(); <-- Tryin' force it to work with no success
    }

}

The ButtonColumn template creation:

public class ButtonColumn : System.Web.UI.ITemplate
{
    private Action action;
    private string controlId;
    private string tooltip;

    public ButtonColumn(Action onClick, string toolTip)
    {
        this.action = onClick;
        this.controlId= "btnShowImage";
        this.tooltip = toolTip;
    }

    public void InstantiateIn(System.Web.UI.Control container)
    {
        GridViewDataItemTemplateContainer gridContainer = (GridViewDataItemTemplateContainer)container;

        if (System.Text.RegularExpressions.Regex.IsMatch(gridContainer.Text, "^([A-Za-z0-9+/]{4})*([A-Za-z0-9+/]{4}|[A-Za-z0-9+/]{3}=|[A-Za-z0-9+/]{2}==)$"))
        {
            ImageButton button = new ImageButton();
            button.ID = idControle;
            button.ImageUrl = "/Images/html5_badge_64.png";
            button.Width = 20;
            button.Height = 20;
            button.ToolTip = tooltip;

            button.Click += (s, a) =>
            {
                if (onClick != null)
                    onClick();
            };

            container.Controls.Add(button);
        }
        else
        {
            Label label = new Label()
            {
                Text = gridContainer.Text,
                ToolTip = tooltip
            };
            container.Controls.Add(label);
        }
    }
}

The method's call at the click of btnShowImage button works fine. But when I do the same call by one ImageButton (or button) inside the gridview it doesn't work. Both calls reach the ShowImage method.

Any help would be appreciated. Thank you all.

EDIT 1: The GridView is encapsulated in GridViewWrapper (there I build the columns dynamically using a combination of class's properties gotten by reflection and stored metadata), this class have too much code to share here and I do not think it's the reason. Also, I've executed in debug mode and passed thru it step by step every relevant method inside this one.

The column add method:

CustomColumnTemplate customTemplate = CustomTemplates.FirstOrDefault(f => f.columnName == metadata.ColumnIdName);

gridView.Columns.Add(new GridViewDataColumn()
{
    FieldName = metadata.ColumnIdName,
    VisibleIndex = GetVisibleIndexByColumnIdName(metadata.ColumnIdName),
    Caption = metadata.Caption,
    Width = new Unit(DefaultColumnWidth, UnitType.Pixel),
    DataItemTemplate = customTemplate == null ? null : customTemplate.template
});

I've made sure the ShowImage method is being hitten, but it behaves like the UpdatePanel1 isn't have been updated

anonymoose
  • 1,169
  • 2
  • 21
  • 62
Diego Rafael Souza
  • 5,241
  • 3
  • 23
  • 62
  • 1
    I do not see ASPxGridView in your code. Possibly, it is encapsulated in GridViewWrapper. Can you show how you place the button inside the grid? Or even better, [submit a support ticket](https://www.devexpress.com/Support/Center/Question/Create) with all the details to DevExpress support. They reply quickly – Vladimir Jan 19 '18 at 07:14
  • Thank you for check @Vladimir. I've added some details about the GridViewWrapper. The button is inserted thru the custom template, on rendering each row. I don't think it's a bug, I'm think there's something conceptual I'm missing here, but submit a support ticket is a good idea. I'll do it soon. – Diego Rafael Souza Jan 19 '18 at 15:50

2 Answers2

6

The ASPxGridView stores information about columns in ViewState, but does not save information about column templates. This is made on purpose since templates can be very complex and their serialization makes ViewState very huge. So, if you create columns with templates at runtime, disable ViewState:

ASPxGridView.EnableViewState="false"

and create columns on every callback:

//if (!IsPostBack)
//{
    mainGrid.CanEditItems = true;
    mainGrid.CustomTemplates.Add(new CustomColumnTemplate { columnName = "Id", template = new LinkColumn(CreateParentLink, "Go to parent") });
    mainGrid.CustomTemplates.Add(new CustomColumnTemplate { columnName = "Value", template = new ButtonColumn(ShowImage, "View Image") }); // This one doesn't works
//}
Vladimir
  • 828
  • 6
  • 8
  • Perfect! It works greatly. I hadn't need to set `EnableViewState` to `False` (when I did it, it broke a lot of things on my GridViewWrapper and it was not worked as it had to), I'd just move the template additions to outside the `!IsPostBack block`. Thank you very much. – Diego Rafael Souza Jan 24 '18 at 13:03
2

You used code below:

<Triggers>
    <asp:AsyncPostBackTrigger ControlID="mainGrid" />
</Triggers>

According to this article, in asp:AsyncPostBackTrigger If the EventName property is not specified, the DefaultEventAttribute attribute of the control is used to determine the default event. For example, the default event for the Button control is the Click event.

mainGrid control created by GridViewWrapper that it doesn't connected to controls that are in mainGrid. Updatepanel tries to register async trigger for the mainGrid control which is outside the panel but it can't do it.

solution: I think solution of this problem is update Updatepanel in ShowImage() method.

Ali Soltani
  • 9,589
  • 5
  • 30
  • 55
  • Thanks for check, Ali Soltani. I've tried it too. As you can see at the comment on 3rd code block, `// UpdatePanel1.Update(); <-- Tryin' force it to work with no success`. The `Update` call is executed with no error, but nothing new happens. – Diego Rafael Souza Jan 22 '18 at 13:37
  • @DiegoRafaelSouza OK I had not seen it. Did you try to change order `modalImagem.Show();` and `UpdatePanel1.Update();`? – Ali Soltani Jan 22 '18 at 14:15
  • @DiegoRafaelSouza Do you get any javascript errors immediately on loading? Maybe something that kills javascript on the page? – Ali Soltani Jan 22 '18 at 14:28
  • @DiegoRafaelSouza Another things is `RegisterAsyncPostBackControl` do you try `RegisterAsyncPostBackControl(mainGrid)` like [this](https://forums.asp.net/t/1043071.aspx?UpdatePanel+Update+refreshing+page+not+working+like+Ajax+supposed+to+)? – Ali Soltani Jan 22 '18 at 14:32
  • No javascript errors. Neither change the order nor register grid as async postback control worked. The same behavior than before. I'm almost giving up and thinking in make my way thru another approach: Open it as a popup or simple page. – Diego Rafael Souza Jan 22 '18 at 15:33