Thursday, January 28, 2016

I actually engineered something today!

In a web forms application there is a form for configuring an email template. The template may include a client logo which you may upload. It would be nice if you could preview it before posting the form and naturally there is no way to slurp the bytes out of a file type input field with AJAX really so how could I get at the file to preview itself before submitting the greater form? The file upload field went into separate web form which got looped in via an iFrame. The code behind looked like this:

using System;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Drawing.Imaging;
using System.IO;
using DevExpress.Web.ASPxUploadControl;
using MyExternalDependency;
namespace Whatever
{
   public partial class IFrameForLogo : System.Web.UI.Page
   {
      public string ButtonName;
      public int Height;
      public int Width;
      
      protected void Page_Load(object sender, EventArgs e)
      {
         User user = (User)Session["User"];
         Group group = user.Group;
         if (group.GroupID.ToString() != GroupId.Value)
         {
            GroupId.Value = group.GroupID.ToString();
            GetSizeAndMaybeResize(group.Logo);
         }
         var isExistingImage = group.Logo != null && group.Logo.Length > 0;
         StagePage(isExistingImage);
      }
      
      protected void EmailUploadControl_FileUploadComplete(object send,
            FileUploadCompleteEventArgs e)
      {
         var bytes = GetSizeAndMaybeResize(e.UploadedFile.FileBytes);
         var isExistingImage = bytes != null && bytes.Length > 0;
         StagePage(isExistingImage);
      }
      
      private void StagePage(bool isExistingImage)
      {
         if (isExistingImage)
         {
            ButtonName = "Change";
            ControlBox.Attributes.Remove("style");
            ControlBox.Attributes.Add("style", "width: 70px; padding-left: " + (288-Width) +
            "px;");
            ImageBox.Visible = true;
            ImageBox.Attributes.Add("style", "width: " + Width + "px; height: " + Height +
                  "px; margin-top: " + (80-Height) + "px; background-color: #EE3224;
                  background-image:url('Logo.ashx?id=" + GroupId.Value + "');");
         }
         else
         {
            ButtonName = "Upload Logo";
            ControlBox.Attributes.Remove("style");
            ControlBox.Attributes.Add("style", "width: 358px;");
            ImageBox.Visible = false;
         }
      }
      
      private byte[] CastImageToByteArray(Image image)
      {
         using(MemoryStream memoryStream = new MemoryStream())
         {
            image.Save(memoryStream, ImageFormat.Png);
            return memoryStream.ToArray();
         }
      }
      
      private Byte[] GetSizeAndMaybeResize(byte[] bytes)
      {
         var shrunken = new byte[0];
         if (bytes != null && bytes.Length > 0)
         {
            Session["bytes" + GroupId.Value] = bytes;
            using (var memoryStream = new MemoryStream(bytes))
            {
               var image = Image.FromStream(memoryStream);
               Height = image.Height;
               Width = image.Width;
               if (Height > 80)
               {
                  var ratio = (float)Width/(float)Height;
                  int difference = Height - 80;
                  Height = 80;
                  Width = Width - (int)(difference*ratio);
                  if (image.Height != Height && image.Width != Width)
                  {
                     Bitmap bitmap = new Bitmap(Width, Height);
                     using (Graphics shrinker = Graphics.FromImage((Image) bitmap))
                     {
                        shrinker.InterpolationMode = InterpolationMode.HighQualityBicubic;
                        shrinker.DrawImage(image, 0, 0, Width, Height);
                     }
                     shrunken = CastImageToByteArray(bitmap);
                     Session["bytes" + GroupId.Value] = shrunken;
                  }
               }
            }
            if (shrunken.Length > 0)
            {
               if (Width > 200)
               {
                  using (var memoryStream = new MemoryStream(bytes))
                  {
                     var image = Image.FromStream(memoryStream);
                     Height = image.Height;
                     Width = image.Width;
                     var ratio = (float) Height/(float) Width;
                     int difference = Width - 200;
                     Width = 200;
                     Height = Height - (int) (difference*ratio);
                     if (image.Height != Height && image.Width != Width)
                     {
                        Bitmap bitmap = new Bitmap(Width, Height);
                        using (Graphics shrinker = Graphics.FromImage((Image) bitmap))
                        {
                           shrinker.InterpolationMode = InterpolationMode.HighQualityBicubic;
                           shrinker.DrawImage(image, 0, 0, Width, Height);
                        }
                        shrunken = CastImageToByteArray(bitmap);
                        Session["bytes" + GroupId.Value] = shrunken;
                     }
                  }
               }
               bytes = shrunken;
            }
            else
            {
               using (var memoryStream = new MemoryStream(bytes))
               {
                  var image = Image.FromStream(memoryStream);
                  Bitmap bitmap = new Bitmap(Width, Height);
                  using (Graphics shrinker = Graphics.FromImage((Image) bitmap))
                  {
                     shrinker.InterpolationMode = InterpolationMode.HighQualityBicubic;
                     shrinker.DrawImage(image, 0, 0, Width, Height);
                  }
                  var recast = CastImageToByteArray(bitmap);
                  Session["bytes" + GroupId.Value] = recast;
               }
            }
         }
         else
         {
            Height = 0;
            Width = 0;
         }
         return bytes;
      }
   }
}

 
 

The actual web form looks like this. I end up using a DevExpress ASPxUploadControl instead of the file type input so that I may restrict what types of files are acceptable (though that doesn't work in old IE and I still need a sanity check in jQuery nonetheless).

<%@ Page Language="C#" AutoEventWireup="true"
      CodeBehind="IFrameForLogo.aspx.cs" Inherits="Whatever.IFrameForLogo" %>
<%@ Register TagPrefix="dx" Namespace="DevExpress.Web.ASPxUploadControl"
      Assembly="DevExpress.Web.v13.1, Version=13.1.9.0, Culture=neutral,
      PublicKeyToken=b88d1754d700e49a" %>
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
   <head runat="server">
      <title>Whatever</title>
      <script src="js/jquery-1.3.2.min.js"></script>
      <style type="text/css" media="all">
         body {
            margin: 0;
            padding: 0;
         }
         #bottom {
            position: absolute;
            margin-left: -9999px;
            width: 200px;
            height: 200px;
         }
         form {
            margin: 0;
            padding: 0;
         }
         .left {
            float: left;
            height: 20px;
            margin-top: 55px;
         }
         .left button {
            margin: 0;
         }
         .right {
            float: right;
         }
         .top {
            position: absolute;
            margin-top: 35px;
            margin-left: 0;
            left: 0;
            color: #333333;
            font-family: Arial;
            font-size: 10pt;
            font-weight: bold;
            height: 20px;
            width: 358px;
         }
         #wrapper {
            margin: 0;
            padding: 0;
            height: 80px;
            width: 358px;
            background-color: #FFFFFF;
         }
      </style>
   </head>
   <body>
      <form id="wrapperform" ClientIDMode="Static" runat="server">
         <div id="wrapper">
            <div class="top">Email Template</div>
            <div class="left" id="ControlBox" runat="server">
               <button id="button" type="button"><%=ButtonName %></button>
            </div>
            <div class="right" id="ImageBox" runat="server" Visible="false"></div>
            <div id="bottom">
               <dx:ASPxUploadControl id="EmailUploadControl" runat="server"
                     ClientIDMode="Static"
                     OnFileUploadComplete="EmailUploadControl_FileUploadComplete">
                  <ValidationSettings AllowedFileExtensions=".gif,.jpg,.png"
                        NotAllowedFileExtensionErrorText="Use a .gif, a .jpg, or a .png!"/>
                  <ClientSideEvents TextChanged="function(s,e){UploadLogo(s,e);}">
                  </ClientSideEvents>
               </dx:ASPxUploadControl>
            </div>
         </div>
         <asp:HiddenField runat="server" id="GroupId" value="0"/>
      </form>
      <script type="text/javascript">
         var clicking = false;
         $(function () {
            $("#button").bind('click', function () {
               var counter = 0;
               $('#bottom input').each(function () {
                  console.log(this);
                  if (counter === 3) {
                     $(this).click();
                  }
                  var readonly = $(this).attr("readonly");
                  if (counter != 3 || !readonly) {
                     counter++;
                  }
               });
            });
         });
         function UploadLogo(s,e) {
            $('#EmailUploadControl_TextBox0').each(function () {
               var title = $(this).attr("title");
               var extension = title.toLowerCase().substring(title.length - 4, title.length);
               if (extension === ".gif" || extension === ".jpg" || extension === ".png") {
                  $('#wrapperform').submit();
               }
            });
         };
      </script>
   </body>
</html>

 
 

Interesting things:

  • I sanity check the size of images and resize them if need be in the code behind and also each image gets cast to a .png for convenience.
  • Note that I hide the ASPxUploadControl and have a button to click on to click on it "under the hood" when I do I have to find a specific item in a set of items that DevExpress renders out. It's usually the third item, but in the case of IE it's the fourth!

 
 

Addendum 2/1/2016: A lot of this needs love. I am trying to put stuff into Session not yet saved, but when I send test emails the emails cannot pull stuff out of Session. I'm going to need a temp data table. Also my hacky way of finding the ASPxUploadControl blows. There is a way in DevExpress to just make that control look like a button. This might have it. I'm not sure yet. Also I forgot to mention earlier that I'd like you to note that I am forcing the form in this web form to submit with JavaScript whenever the ASPxUploadControl is "touched."

 
 

Addendum 2/2/2016: This suggests the following better way to emulate a click on an ASPxUploadControl.

document.getElementById("EmailUploadControl_TextBoxT_Input").click();

 
 

Addendum 2/3/2016: The thing I suggest immediately above really won't work because the file never gets uploaded that way. I'll try to find another way. Wait! I found it! It turns out that TextBoxT just needs to become TextBox0 for this to work.

No comments:

Post a Comment