ModalPopupExtender in a GridView sample

by Arnold Matusz 11 11 2008

Although the ModalPopupExtender isn’t new at all there are still countless poeple who search the web for a comprehensive sample on how a ModalPopupExtender is used from a GridView.

First of all you’ld need to have a Button in each row which should trigger the ModalPopupExtender, then you would need to have an instance of a ModalPopupExtender with the TargetControlID set to the ID of that Button.

On the page you should create a Panel which the one which should popup whenever you press the button inside the GridView. This is the preeliminary setup. Because you don’t just pop up a modal “Window” on your form for fun … you need controls to interact with the user! For example one which should Cancel the operation and the other one which should proceed the operation. These can be using the OkControlID and the CancelControlID of the ModalPopopExtender.

This again is all fine … buuuuut without any postback there is no fun … what would be the real reason to use such a scenario where you don’t “save” the user’s choice. There has to be some functionality which sends the form back for processing.

Things that you need to consider in a sitation like this:

  1. The Cancel control simply hides the form (maybe you would need to clear the form before you let it pop up again!). Mind you, there is another way to do this using Javascript by setting the OnCancelScript property of the ModalPopupExtender.
  2. The OK control simply hides the form, which is not very good if you want to validate a field for example (and you still need to have the Modal Panel open if it didn’t pass the validation). Once again this can be done using the OnOkScript property of the ModalPopupExtender, but as more people are confortable with server-side validation than with client-side validation I’'ll show a way to leave the modal popup open even after a postback.
  3. The Control that triggers the opening of the ModalPopupExtender often needs a postback aswell to set up the Controls in the Modal Panel. (Ex: To load the firstname, lastname for which you wish to update the birthday details).

This example exposes a solution to all of the above points! To exemplify this I came up with a little scenario, you have a list of products which you can add to your cart! The GridView contains a button for each product, and by pressing it a Modal Popup comes up where you can fill in the Quantity you wish to order! Of course the modal popup contains the add button which acts as the “OKControlID” and a cancel button which is the Cancel Control ID. The OKControlID isn’t explicitely set because it would automatically hide the ModalPopup, so the actuall showing and hiding is done on the codebehind.

<asp:GridView ID="GridView1" runat="server" AutoGenerateColumns="False" DataKeyNames="ProductID"
    DataSourceID="EntityDataSource1">
    <Columns>
        <asp:TemplateField>
            <ItemTemplate>
                <asp:ImageButton ID="btnSelect" runat="server"
                                 ImageUrl="~/Images/addtocart.gif"
                                 ImageAlign="AbsMiddle" OnClick="btnSelect_Click" />
                <ajax:ModalPopupExtender ID="gv_ModalPopupExtender" runat="server"
                                         TargetControlID="btnSelect"
                                         PopupControlID="pnlModalPanel"
                                         CancelControlID="btnCancel"
                                         BackgroundCssClass="modalBackground">
                </ajax:ModalPopupExtender>
            </ItemTemplate>
        </asp:TemplateField>
        <asp:BoundField DataField="ProductID" HeaderText="ID"
                        ReadOnly="True" SortExpression="ProductID" />
        <asp:BoundField DataField="ProductName" HeaderText="ProductName"
                        SortExpression="ProductName" />
        <asp:BoundField DataField="ProductPrice" HeaderText="ProductPrice"
                        SortExpression="ProductPrice" DataFormatString="{0:C}" />
    </Columns>
</asp:GridView> 

* Notice how each row in the GridView (except of course the header and the footer) contains an ImageButton and a ModalPopupExtender.

For a visually pleasing result the BackgroundCssClass of the ModalPopupExtender is set. You could use the following style for a start:

<style type="text/css">
    .modalBackground
    {
        background-color: Black;
        filter: alpha(opacity=80);
        opacity: 0.8;
        z-index: 10000;
    }
</style>

The panel that will pop up can be inserted into an UpdatePanel so that when there is an async postback so people can’t notice the whole page bouncing back and forth. I’ve put the pnlModalPanel inside an UpdatePanel because the btnAddtoCart button needs to do some logic on the codebehind.

<asp:Panel ID="pnlModalPanel" runat="server" Style="display: none;">
    <table>
        <tr align="center">
            <td colspan="2">
                <asp:Label ID="lblQuantity" runat="server" 
                           ForeColor="White">Quantity:</asp:Label>
                <asp:TextBox ID="txtQuantity" runat="server"></asp:TextBox>
            </td>
        </tr>
        <tr>
            <td align="left">
                <asp:Button ID="btnAddToCart" runat="server" 
                            Text="Add to Cart" OnClick="btnAddToCart_Click" />
            </td>
            <td align="right">
                <asp:Button ID="btnCancel" runat="server" Text="Cancel" />
            </td>
        </tr>
    </table>
</asp:Panel>

The first noticeable thing that happens on codebehind is that I set the OnClientClick attribute of the btnAddToCart button, so that it will post back.

    protected void Page_Load(object sender, EventArgs e)
    {
        btnAddToCart.OnClientClick = String.Format("functionPostback('{0}','{1}')",
                                                         btnAddToCart.UniqueID, "");
    }

Because I manually want to show the modal popup (this might be due to the fact that the controls inside the popup have to initialized according to the pressed button or row). This is not available in my example but maybe it would be good to load the product name and price inside the modal popup so we can insert the correct quantity accoring to our budget. This is why I’m showing the modal popup from server side. In my example I’m only clearing the quantity textbox.

    protected void btnSelect_Click(object sender, ImageClickEventArgs e)
    {
        GridViewRow row = ((GridViewRow)((ImageButton)sender).NamingContainer);
        //NamingContainer return the container that the control sits in
        AjaxControlToolkit.ModalPopupExtender mpe =
                           (AjaxControlToolkit.ModalPopupExtender)row.FindControl("mpe");
        txtQuantity.Text = ""; //reset the quantity textbox
        mpe.Show(); //show the modal popup extender
    }

The next thing to do is to handle the logic of the btnAddtoCart button where for the sake of the example I’m only setting a label to the quantity that has been selected!

    protected void btnAddToCart_Click(object sender, EventArgs e)
    {
        lblMessage.Text = txtQuantity.Text + " items have been added!";
        txtQuantity.Text = ""; //reset the quantity textbox
    }

A tricky situation for the btnAddToCart_Click function is when the user would like more products than what the you can deliver (not enough products in stock, etc). In that case you would need to set a label inside the ModalPanel to a message telling the customer that there is a certain problem. The important thing is that after the postback ends the ModalPopup needs to be shown again. You need to find the instance of the ModalPopupExtender that has been shown in the first place and then call the Show() method on it.

UPDATE! - Appearantly I forgot to post what the javascript functionPostback function is doing. As it's name suggests it's a Javascript function which submits the form. (It calls the native __doPostBack(EventTarget, EventArgument) function). Thank you for your comment for pointing this one out!

<script type="text/javascript">
    function fnClickUpdate(sender, e)
    {
        __doPostBack(sender,e);
    }
</script>            

Working with the ModalPopupExtender in more complex situations can be quite tricky but the yielded result can be ever more satisfactory if you get it right.
If you need any assistance don’t hesitate to write me and together we could find a solution, and if you liked this post don’t forget to kick it!

DZone it Digg it Submit to StumbleUpon Submit to Technorati Submit to reddit.com Submit to del.icio.us Submit to NewsVine Submit to Furl Submit to BlinkList

Tags: , , ,

AJAX | ASP.NET | C#

Comments

11/12/2008 1:32:38 AM #

trackback

Trackback from DotNetKicks.com

ModalPopupExtender in a GridView sample

DotNetKicks.com |

12/4/2008 3:55:04 AM #

noname

what is functionPostback? can we have a complete source code?

noname United States |

1/5/2009 9:00:42 PM #

Jonx

To trigger a postback with the button you don't have to use the script magic. you just have to tell the updatepanel to update conditionnal and add the button as a trigger and the it happens on the click event...

Jonx |

1/11/2009 8:34:40 AM #

trackback

Trackback from Web Development Community

ModalPopupExtender in a GridView sample

Web Development Community |

2/4/2009 1:32:42 PM #

jhobs

this is good!!!

jhobs United States |

6/5/2009 3:16:08 AM #

satya

It's Good but i want to do operation depending on Row index  when  vtnaddtocart clicked ...could u please how can i get row index in coed

satya India |

6/5/2009 6:18:08 PM #

Arnold Matusz

Hi Satya

I don't think it's a good idea to get the row index based on the button you've clicked due to the following reason: you may sort the GridView and then you may not find what you are looking for in the DataSet based on the RowIndex.

The better way is to find the Row that caused the ModalPopup to show, where you can search for some clues to see what ID you are looking at. A common way to do this is to have a HiddenField in the ItemTemplate of a Row and you need to set the value to the ID of the DataSet you bind to.

As shown in the btnSelect_Click method, you get the row by using:
GridViewRow row = ((GridViewRow)((ImageButton)sender).NamingContainer);

Now to find the ID you are looking for (Instead of the index of course) you need:
HiddenField fld = (HiddenField)row.FindControl("fldContextID");
//where fldContextID is the HiddenFields ID in your ItemTemplate

But if you simply need the RowIndex you can use the same approach as above (except the HiddenField) in the following way:
int rowIndex = row.RowIndex;

But beware (normally using row.DataItem will be null on a postback, hence I recommend using the approach aboce - with the HiddenField so you know what data you are operating on).

Hope this helps, and if you need assistance you can contact me at any time.

Arnold Matusz Romania |

7/1/2009 8:00:25 AM #

Gaurav

protected void btnSelect_Click(object sender, ImageClickEventArgs e)
{

}

is not getting fired for me. can you please let me know why?

Gaurav United States |

7/1/2009 9:19:58 AM #

Arnold Matusz

I think it may be the fact that you do not set the OnClientClick property of your Button like in the example above.

But its pretty much difficult to assess why it's not working, the ModalPopupExtender behaves in different ways based on what you set your OkControlID / CancelControlID / TargetControlID to.

The way above assures the fact that the button will post back so that on the server-side you'll know that the client has clicked on or the other button (may that be the OK or Cancel button).

I don't see very many scenarios where you would not be interested in seeing the result of the OkButton click. (ex Data entry, etc).

Arnold Matusz Romania |

7/15/2009 7:27:31 PM #

Ramesh

Hi Arnold,

If i validate the controls in the panel using asp validators then validation summary will be displayed before modal pop up is opened. And the modal pop will not open.

It is working fine without asp validator functions.

Thanks in Advance

Ramesh India |

7/15/2009 7:58:13 PM #

Arnold Matusz

Hi Ramesh

You have to set the ValidationGroup of the controls in your ModalPopup.

ex: you have 3 TextBoxes, 2 validators and one Save button
=>  you have to set the ValidationGroup of all these controls
!     you have to set the CausesValidation property of an eventual CancelButton which should be set to ="false"

Arnold Matusz Romania |

8/27/2009 8:47:22 AM #

Carlos Chavez

hello,
just wanted to say this was a great example.
I tried it and it worked, but when I try it with a Master page file, with my popup panel inside a contentplaceholder, after I click the button the page extends infinitely in height and width. Is it a bug with master page files? have you made it work with a master page file?
thankx

Carlos Chavez Colombia |

8/27/2009 5:49:05 PM #

Arnold Matusz

I give it a 99% chance that there is a problem with your CSS.

Please review that, if it still doens't work, send me your code and we'll work it out.

Kind regards

Arnold Matusz Romania |