AJAX Control Toolkit - Modal Popup Extender - The famous one - Help to solve common problems
Actually, I've just seen a lot of differents behavior about this Ajax Control and I found how to solve them.
First, let's dress a list of redundant problems. (Note : each things below will be also in my code snippet)
- You get an error if you don't assign a value to TargetControlID ?
- Code behind event are not firing when you click on the modal popup extender Ok button ?
- Your .gif or any other animated image in your Modal Popup Extender stop animating in IE ?
- Your modal popup extender (blinks - flash - shows up) only a short moment on the page load ?
- You've set the background property to transparent but it display solid black or another non-transparent color.
- You modify server-side the content of your modal popup but when it shows up it's still unmodified.
This problem is very common when you have a dynamic control to bind with the TargetControlID of your modal popup extender. Even if it sounds the good thing to do, using the DynamicControlID won't solve your problem. The TargetControlID is mandatory.
So, the easiest way to do it is to add a dummycontrol to bind it with the ID. Then, on your dynamic buttons you can add a javascript function on the OnClientClick event to show the modal popup.
In the Modal popup extender you have two similar property "OkControlID" and "CancelControlID", when you bind a button to those it won't do any postback to the server. Don't forget that modal popup is ClientSide, if you want to do a postback to the server then don't bind your button ID with one of those.
There are two way to catch the click event; assign your code behind function name to the "OnClick" Event of the button or add Handles yourButton.Click to your code behind function.
This behavior is pretty anoying but actually it's very easy to solve it. You must include your whole page content in an UpdatePanel. If you are using a MasterPage put the Update Panel right after your ContentPlaceHolder tag, otherwise just after your Form and ScriptManager tag. You must put all page content in there including each modal popup extender and their pannel.
It's also strongly recommended to put the content of your panel (linked to the PopupControlID of your current Modal Popup Extender) in another UpdatePanel if you don't want to have some strange behavior.
Another common problem who can be solve pretty quickly. Please just add this property to your Panel (the one linked with your modal popup of course) : Sytle="display:none". The problem will be solved for most people...
That's suppose to solve it actually, but I'm still having trouble for Firefox. On my job website, the modal popup still appear for 1 second on the page load which is pretty anoying. I tought with the 3.0.6 patch of FF it could be solved but it wasn't. In another hand it's working in all other browsers, so I guess it's maybe just a special config to set for Firefox otherwise it's prolly a problem on their side.
Whatever it is, I'm still on it !
It's not a big deal here, most of developer will solve alone this problem. It seems that some addict of the wizard tool who don't understand what they do had troubles with that. Actually, I don't really understand why it could be usefull to do that... Anyway, just don't set the background:transparent because if you are using a DropShadow on the ModalPopup, in reality a black square is displayed backward, so you will just see it.
That's why it's usefull to have an UpdatePanel wraped around your current Panel content, server-side after any modifications and after the call Modal.show(), just update your pannel like that myUpdatePanel.Update(). That will solve the problem.
Note that it's not mandatory to use the Update Panel to wrap the content of the Panel linked with your Modal popup extender, but according to some Microsoft expert like Vince Xu for example, it's a good practice to use it in this case.
I made a little project as an example if you want to see it working, there is 2 practical applications in it.
The first example is very usefull to warn user or if you want him to wait during the loading process. I used it without any button, so it's really when you need to do some processing server-side.
Example creating a .zip file fetched from the database. When the file is ready, you can reload the page with a Response.Redirect and then the modal popup will hide because of the reloading.
So during that time, the user won't think your page is freezed.
The second one is only a simple
form with some Javascript feature in it. Like a default text for the required field, a bit like your Google sreach bar on your browser, when you click in it the text disapear. I also explain how to do a post-back
from your modal popup extender.
I also put the code below if you don't want to download the source code.
Hope it will help.
Good luck !
<%@ Page Language="VB" AutoEventWireup="false" CodeFile="Default.aspx.vb" Inherits="_Default" %> <%@ Register Assembly="AjaxControlToolkit" Namespace="AjaxControlToolkit" TagPrefix="cc1" %> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head runat="server"> <title>Deal with Modal Popup Extender</title> <link href="App_Themes/default/default.css" rel="stylesheet" type="text/css" /> <script type="text/javascript" language="javascript"> //This function is not used in the code, but it's a nice example //how to do an ASP.Net Post back with JavaScript function fnClickOK(sender, e) { __doPostBack(sender,e); } function openProcessModal() { var modal = $find('mpeProcessing'); modal.show(); } function loadDefaultValue() { //assign the default value document.getElementById('tbxUsername').value = "This field is mandatory"; document.getElementById('tbxEmail').value = "This field is mandatory"; document.getElementById('tbxCommentTitle').value = "This field is mandatory"; document.getElementById('tbxComment').value = "This field is mandatory"; //assing the default value color document.getElementById('tbxUsername').style.color= "gray"; document.getElementById('tbxEmail').style.color= "gray"; document.getElementById('tbxCommentTitle').style.color= "gray"; document.getElementById('tbxComment').style.color= "gray"; } function clearFieldOnFocus(btnId) { //I base my validation on the style color if (document.getElementById(btnId).style.color == "gray") { document.getElementById(btnId).value = ""; document.getElementById(btnId).style.color= "black"; } } </script> </head> <body> <form id="example" runat="server"> <asp:ScriptManager ID="ScriptManager1" runat="server" /> <asp:UpdatePanel ID="upMainDiv" runat="server"> <ContentTemplate> <div id="MainDiv"> <p> <asp:LinkButton ID="lbtnProcess" runat="server" OnClick="doProcessing" OnClientClick="openProcessModal()" Text="Download and test processing modal popup" /> <br /><br /> <asp:LinkButton ID="lbtnForm" runat="server" OnClientClick="loadDefaultValue()" Text="Open form and test the form modal popup" /> </p> </div> <asp:Label ID="dummyLabel" runat="server" Enabled="false" /> <cc1:ModalPopupExtender ID="mpeProcessing" runat="server" TargetControlID="dummyLabel" PopupControlID="panProcessing" BackgroundCssClass="AJAX_ModalPopup_Background" RepositionMode="RepositionOnWindowResizeAndScroll" /> <asp:Panel ID="panProcessing" runat="server" style="display:none" CssClass="AJAX_ModalPopup_ProcessPanelStyle"> <asp:UpdatePanel ID="upProcessing" runat="server" UpdateMode="Always"> <ContentTemplate> <asp:Image ID="imgProcessing" runat="server" ImageUrl="~/images/loading.gif" /> <br /><br /> <asp:Label ID="lblProcessing" runat="server" Text="Yeah yeah yeah... I'm really doing something in backgroung, just wait man !" /> </ContentTemplate> </asp:UpdatePanel> </asp:Panel> <cc1:ModalPopupExtender ID="mpeForm" runat="server" TargetControlID="lbtnForm" PopupControlID="panForm" CancelControlID="btnCancel" BackgroundCssClass="AJAX_ModalPopup_Background" RepositionMode="RepositionOnWindowResizeAndScroll"/> <asp:Panel ID="panForm" runat="server" style="display:none" CssClass="AJAX_ModalPopup_FormPanelStyle"> <asp:UpdatePanel ID="upForm" runat="server" UpdateMode="Conditional"> <ContentTemplate> <%-- The property here -> HorizontalAlign="Center" is to center the table in Firefox --%> <asp:Table ID="tlbForm" runat="server" CssClass="ASP_tblForm" HorizontalAlign="Center"> <asp:TableRow> <asp:TableCell runat="server" ColumnSpan="2"> <asp:Label ID="lblTitle" runat="server" Text="Adding a comment" CssClass="ASP_tblFormTitle_Style" /><br /><br /> </asp:TableCell> </asp:TableRow> <asp:TableRow> <asp:TableCell CssClass="ASP_FieldTitle" > <asp:Label ID="lblUsername" runat="server" Text="Username :"/> </asp:TableCell> <asp:TableCell CssClass="ASP_Field"> <asp:TextBox ID="tbxUsername" runat="server" Width="160px" MaxLength="20"/> </asp:TableCell> </asp:TableRow> <asp:TableRow> <asp:TableCell CssClass="ASP_FieldTitle" > <asp:Label ID="lblEmail" runat="server" Text="Email :"/> </asp:TableCell> <asp:TableCell CssClass="ASP_Field"> <asp:TextBox ID="tbxEmail" runat="server" Width="220px" MaxLength="50"/> </asp:TableCell> </asp:TableRow> <asp:TableRow> <asp:TableCell CssClass="ASP_FieldTitle" > <asp:Label ID="lblCommentTitle" runat="server" Text="Comment title :"/> </asp:TableCell> <asp:TableCell CssClass="ASP_Field"> <asp:TextBox ID="tbxCommentTitle" runat="server" Width="250px" MaxLength="50"/> </asp:TableCell> </asp:TableRow> <asp:TableRow> <asp:TableCell CssClass="ASP_FieldTitle" > <asp:Label ID="lblComment" runat="server" Text="Comment :"/> </asp:TableCell> <asp:TableCell CssClass="ASP_Field"> <asp:TextBox ID="tbxComment" runat="server" Width="342px" TextMode="MultiLine" Wrap="true" Rows="7" MaxLength="250" CssClass="ASP_TextBox_Multiline"/> </asp:TableCell> </asp:TableRow> </asp:Table> <asp:Button ID="btnSumbit" runat="server" Text="Sumbit" CssClass="ASP_Button_Style"/> <asp:Button ID="btnCancel" runat="server" Text="Cancel" CssClass="ASP_Button_Style"/> </ContentTemplate> </asp:UpdatePanel> </asp:Panel> </ContentTemplate> </asp:UpdatePanel> </form> </body> </html>
Partial Class _Default
Inherits System.Web.UI.Page
Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
Me.tbxUsername.Attributes.Add("OnFocus", "clearFieldOnFocus(this.id)")
Me.tbxEmail.Attributes.Add("OnFocus", "clearFieldOnFocus(this.id)")
Me.tbxCommentTitle.Attributes.Add("OnFocus", "clearFieldOnFocus(this.id)")
Me.tbxComment.Attributes.Add("OnFocus", "clearFieldOnFocus(this.id)")
End Sub
Public Sub sumbitForm(ByVal sender As System.Object, _
ByVal e As System.EventArgs) Handles btnSumbit.Click
'Do processing, saving the comment into the database for example.
End Sub
Public Sub doProcessing(ByVal sender As System.Object, _
ByVal e As System.EventArgs) Handles lbtnProcess.Click
'Here you could so some processing like fetching your .zip file like I said before.
Threading.Thread.Sleep(2500)
Response.Redirect(Request.Url.ToString(), False)
End Sub
End Class
How to send a mail with ASP.NET with attachment or not, using the FileUpload control
Later, I will probably put this class in the download section and replace this snippet with another one.
Of course you can add this class to your ASP.NET project and use it.
In this case I just code it for only one file attachment per mail.
Imports Microsoft.VisualBasic Imports System.Net Imports System.IO 'Next line needed to accessing Web.Config file with AppSettings Imports System.Configuration.ConfigurationManager Public Class UtilMail Private _smtpServer As New Mail.SmtpClient(AppSettings("smtpIPAddress"), _ AppSettings("smtpPortNumber")) Private _mail As Mail.MailMessage Private _SourceMail As String Private _DestinationMail As String Private _Subject As String Private _Body As String Public Property SourceMail() As String Get Return _SourceMail End Get Set(ByVal value As String) _SourceMail = value.Trim End Set End Property Public Property DestinationMail() As String Get Return _DestinationMail End Get Set(ByVal value As String) _DestinationMail = value.Trim End Set End Property Public Property Subject() As String Get Return _Subject End Get Set(ByVal value As String) _Subject = value.Trim End Set End Property Public Property Body() As String Get Return _Body End Get Set(ByVal value As String) _Body = value.Trim End Set End Property Public Sub New() DestinationMail() = AppSettings("destination_emailAdress") _SourceMail = "" _DestinationMail = "" _Subject = "" _Body = "" End Sub Public Sub SendMail(ByRef FileUpload As WebControls.FileUpload) _mail = New Mail.MailMessage(_SourceMail, _DestinationMail, _Subject, _Body) Try 'Of course the FileUpload.HasFile return a boolean value SendFile(FileUpload, FileUpload.HasFile) Catch ex As Exception Throw New Exception("Error during email sending process. (" & ex & ")") End Try End Sub Private Sub SendFile(ByRef FileUpload As WebControls.FileUpload, _ Optional ByVal AttachFile As Boolean = True) If AttachFile Then 'Here I am using a System.IO function to get the temporary folder on the server Dim strPath As String = Path.GetTempFileName() 'and I save the file there. 'strPath contains the full path of the file with file name and his extension FileUpload.PostedFile.SaveAs(strPath) Dim mailAttach As New Mail.Attachment(strPath) 'Okay, here I create another thread who will wait before deleting the TempFile 'I use that to prevent the file deletion if the mail isn't sended. 'Like you see, AddressOf is a reference to the name of the method you want to use AddHandler _smtpServer.SendCompleted, AddressOf DeleteTempfile 'Here I'm locking the mail variable for precaution SyncLock (_mail) _mail.Attachments.Add(mailAttach) _mail.Attachments.Item(0).Name = FileUpload.PostedFile.FileName 'The new thread will begin on next line _smtpServer.SendAsync(_mail, strPath) End SyncLock Else _smtpServer.Send(_mail) End If End Sub Private Sub DeleteTempfile(ByVal sender As Object, _ ByVal e As System.ComponentModel.AsyncCompletedEventArgs) If e.Error Is Nothing Then SyncLock (_mail) 'I had a problem here because even if the mail was sent, the TempFile 'was still locked by the server so we need next line for releasing the lock _mail.Attachments.Dispose() End SyncLock 'after that we can delete it without any problems. System.IO.File.Delete(e.UserState.ToString) Else Throw New Exception(e.Error.ToString) End If End Sub End Class
WARNING - you must include Async="true" in the Page tag of your aspx page to be able to use mutli-threading.
For the AppSettings() method you need to insert key like that in your Web.Config, section <configuration>
<appSettings> <add key="destination_emailAdress" value="forcemagic@forcemagic.org"/> </appSettings>
Thanks in advance for any comments and suggestions.
