Fixing FileUpload when Visibility is Changed Asynchronously
Somehow I always seem to find edge cases where the abstractions in ASP.Net simply don’t work. At some point in every project, they break down and I start digging into framework-generated scripts, the DOM, or HTTP request details to figure out what is going on.
Last month, I built an alternative to System.Web.UI.Timer because extending it with client side events would have been much more complicated than a plain JavaScript implementation. This month, I spent almost an entire day finding out why a FileUpload would sometimes fail to send data back to the server. The problem arises whenever visibility is changed during an async postback:
The Problem:
Suppose you have a FileUpload inside an UpdatePanel. It is initially set invisible because the user is supposed to do something before seeing it. In this example, a button click asynchronously posts back to the server and the server makes the FileUpload visible. The page would look something like this:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 | <%@ Page Language="C#" %> <script runat="server"> protected void Page_Load(object sender, EventArgs e) { // Initially set the FileUpload invivible. if (!Page.IsPostBack) { this.fileUploadPanel.Visible = false; } } protected void showButton_Click(object sender, EventArgs e) { // User has requested the FileUpload. Make it visible. this.fileUploadPanel.Visible = true; } protected void submitButton_Click(object sender, EventArgs e) { // Did the FileUpload send data back to the server? // Print out true or false. statusLabel.Text = string.Format("File was found = {0}", fileUpload.HasFile); } </script> <html xmlns="http://www.w3.org/1999/xhtml"> <head id="Head1" runat="server"> <title></title> </head> <body> <form id="form1" runat="server"> <asp:ScriptManager ID="sm" runat="server" /> <asp:UpdatePanel ID="updatePanel" runat="server" UpdateMode="Conditional"> <ContentTemplate> <asp:Button ID="showButton" runat="server" Text="Show FileUpload" OnClick="showButton_Click" /> <br /> <asp:Panel ID="fileUploadPanel" runat="server"> <asp:FileUpload ID="fileUpload" runat="server" /> <asp:Button ID="submitButton" runat="server" Text="Submit" OnClick="submitButton_Click" /> </asp:Panel> <br /> <asp:Label ID="statusLabel" runat="server" /> </ContentTemplate> <Triggers> <asp:AsyncPostBackTrigger ControlID="showButton" /> <asp:PostBackTrigger ControlID="submitButton" /> </Triggers> </asp:UpdatePanel> </form> </body> </html> |
The problem with this implementation is that the FileUpload fails to send a file on the first submission after it is made visible. Subsequent submissions work fine, but we need a solution that always works.
The First Solution (not great):
Several programmers have noticed that setting the visibility false on the server can cause this problem, so an easy fix is to toggle visibility client side:
1 2 | this.fileUploadPanel.Visible = true; // fails this.fileUploadPanel.Style["display"] = "none"; // works |
Using .Style["display"] in lieu of .Visible is fine, but now the server sends the FileUpload to the browser with every request. In some cases, that is not what you want. You might want to reduce unnecessary data transfer or you might have client scripts that assume the control does not exist when it is not visible, etc. This solution is also problematic in that you can apply the fix without knowing why it works.
The Second Solution (much better):
Going back to run our original markup, an examination of the DOM using Firebug or IE Developer Toolbar reveals that there is a difference in the form tag whenever the post fails or succeeds. It was not obvious at first glance, but using WinMerge helped to point it out.
DOM markup before a failed POST (first attempt):

DOM markup before a successful POST (subsequent attempts):

Apparently, the ASP.Net runtime adds enctype="multipart/form-data" to the form tag whenever it recognizes a FileUpload (or other file input) is being sent to the browser. Setting the visibility false during initial page load prevents this, and the runtime no longer adds the attribute.
Using Style["display"] = "none" — our first solution — fixes the problem in a roundabout way: It causes the FileUpload to be sent to the browser on initial page load which in turn causes the runtime to add an enctype. The presence of enctype is really what we need, but since showButton is an AsyncPostBackTrigger, the form tag (being outside the UpdatePanel) is not updated when showButton is clicked. You have to manually add enctype at some point before the FileUpload is made visible on the page.
There are several ways to ensure that this attribute is added to the form. Anyone reading this far into the post will probably know how to do that, but here is a working example just for closure.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 | <%@ Page Language="C#" %> <script runat="server"> protected void Page_Load(object sender, EventArgs e) { // Initially set the FileUpload invivible. if (!Page.IsPostBack) { this.fileUploadPanel.Visible = false; } } protected void showButton_Click(object sender, EventArgs e) { // User has requested the FileUpload. Make it visible. this.fileUploadPanel.Visible = true; } protected void submitButton_Click(object sender, EventArgs e) { // Did the FileUpload send data back to the server? // Print out true or false. statusLabel.Text = string.Format("File was found = {0}", fileUpload.HasFile); } </script> <html xmlns="http://www.w3.org/1999/xhtml"> <head id="Head1" runat="server"> <title></title> </head> <body> <form id="form1" runat="server" ENCTYPE="MULTIPART/FORM-DATA"> <asp:ScriptManager ID="sm" runat="server" /> <asp:UpdatePanel ID="updatePanel" runat="server" UpdateMode="Conditional"> <ContentTemplate> <asp:Button ID="showButton" runat="server" Text="Show FileUpload" OnClick="showButton_Click" /> <br /> <asp:Panel ID="fileUploadPanel" runat="server"> <asp:FileUpload ID="fileUpload" runat="server" /> <asp:Button ID="submitButton" runat="server" Text="Submit" OnClick="submitButton_Click" /> </asp:Panel> <br /> <asp:Label ID="statusLabel" runat="server" /> </ContentTemplate> <Triggers> <asp:AsyncPostBackTrigger ControlID="showButton" /> <asp:PostBackTrigger ControlID="submitButton" /> </Triggers> </asp:UpdatePanel> </form> </body> </html> |
Happy Coding!

Thank you, this has saved me a load of time hunting down the reason (and solution) for this annoying quirk!
BTW, I added this to the page load event to do the same:
Page.Form.Enctype = “multipart/form-data”;
Thanks a lot genius.
Thanks you very much… This is gr8 find for me.
Wow! Thanks a lot! This just saved my day!
simple simple awesomeeee
its simply simple but really awesome it solved it
You’ve saved my job! Thanks alot!
Thanks a lot! Its really what we call as genuine solution.
Thank you very much. You save mmy time =)
it was very useful solution and i appreciate the way by which you define the problem and how we can think in a way to solve it.
great effort thank you
Yes! Finally I found the answer I was looking for.
Thank you from Italy!
Thanks alot, I’ve been searching for this all morning.
Thanks man, ive been searching for this issue for months. The difference, was that until an half of hour ago, i saw that all was for the visibility of the control. Thanks man.
Thanx m8, greate post