Archive

Archive for the ‘Tutorial’ Category

[MVC3] ObjectContext.SaveChanges() — How to Use


ADO.NET Entity Framework stack

ADO.NET Entity Framework stack (Photo credit: Wikipedia)

Recently i have joined a Facebook Group The Dev circle, which is a group of likeminded developers who wish to learn, code and grok!

 

One member is starting out using Microsoft’s ORM Entity Framework (EF) and wanted to know how to update his Entities. This is easy within EF, as there is an method called SaveChanges() which will do the following:

 

Persists all updates to the data source and resets change tracking in the object context.

So how do we use it? Well with all Data Connections we need to use a “using” statement and we also need to make sure we catch the required Exceptions detailed in the MSDN documentation, one in particular is OptmisticConcurrencyException which allows us to resolve any concurrency conflicts based on parameters we pass to another method Refresh().

 

Take for e.g. a controller i have in a MVC3 Project which employs EF. Here is the code:

 

// POST: /LedgerUser/Create[Bind(Prefix = "LedgerUser")]

 [HttpPost]
 public ActionResult Create(bool Thumbnail,LedgerUser LedgerUser, 
HttpPostedFileBase imageLoad2) { var Avatar = new Accounts.Models.Image(); //--Handle the image first. if (imageLoad2 != null) { using (System.Drawing.Image img =
System.Drawing.Image.FromStream(imageLoad2.InputStream)) { //--Initialise the size of the array byte[] file = new byte[imageLoad2.InputStream.Length]; //--Create a new BinaryReader and set the InputStream for the
//--Images InputStream to the //--beginning, as we create the img using a stream.
BinaryReader reader = new BinaryReader(imageLoad2.InputStream); imageLoad2.InputStream.Seek(0, SeekOrigin.Begin); //--Load the image binary. file = reader.ReadBytes((int)imageLoad2.InputStream.Length); //--Create a new image to be added to the database Avatar.id = Guid.NewGuid(); Avatar.TableLink = LedgerUser.id; Avatar.RecordStatus = " "; Avatar.FileSize = imageLoad2.ContentLength; Avatar.FileName = imageLoad2.FileName; Avatar.FileSource = file; Avatar.FileContentType = imageLoad2.ContentType; Avatar.FileHeight = img.Height; Avatar.FileWidth = img.Width; Avatar.CreatedDate = DateTime.Now; //-- Now we create the thumbnail and save it. if (Thumbnail == true) { byte[] thumbnail =
Images.CreateThumbnailToByte(imageLoad2.InputStream, 100, 100); Avatar.ThumbnailSource = thumbnail; Avatar.ThumbnailFileSize = thumbnail.Length; Avatar.ThumbnailContentType =
Files.GetContentType(imageLoad2.FileName); Avatar.ThumbnailHeight = Images.FromByteHeight(thumbnail); Avatar.ThumbnailWidth = Images.FromByteWidth(thumbnail); } else { byte[] thumbnail = new byte[0]; Avatar.ThumbnailSource = thumbnail; Avatar.ThumbnailFileSize = 0; Avatar.ThumbnailContentType = " "; Avatar.ThumbnailHeight = 0; Avatar.ThumbnailWidth = 0; } } } if (!ModelState.IsValid) { ModelState.ModelStateErrors(); } if (ModelState.IsValid) { using (AccountsEntities context = new AccountsEntities()) { try { //--Save the LedgerUser context.LedgerUsers.AddObject(LedgerUser); context.SaveChanges(); } catch (OptimisticConcurrencyException) { context.Refresh(RefreshMode.ClientWins, LedgerUser); context.SaveChanges(); Console.WriteLine
("OptimisticConcurrencyException " + "handled and changes saved"); } catch (UpdateException ex) { Console.WriteLine(ex.ToString()); } try { //--Save the Image context.Images.AddObject(Avatar); context.SaveChanges(); return RedirectToAction("Index", "Home"); } catch (OptimisticConcurrencyException) { context.Refresh(RefreshMode.ClientWins, Avatar); context.SaveChanges(); Console.WriteLine
("OptimisticConcurrencyException Avatar" + "handled and changes saved"); } catch (UpdateException ex) { Console.WriteLine(ex.ToString() + " 2 "); } } } var userTypes = new SelectList(db.UserTypes, "id", "Description"); var ledgerUser = new LedgerUser() { id = LedgerUser.id, RecordStatus = LedgerUser.RecordStatus, CreatedDate = LedgerUser.CreatedDate, DateOfBirth = LedgerUser.DateOfBirth }; var viewModel = new LedgerUserViewModel() { UserTypes = userTypes, LedgerUser = ledgerUser }; return View(viewModel); }

 

So what is this controller doing:

  • Create new Ledger Users and their Avatar Images.
  • These are persisted using an AccountsEntities ObjectContext
  • I only save the the LedgerUser and Avatar if the ModelState.IsValid . I then try and SaveChanges.
  • I catch the required Exceptions
  • Return the viewModel if the SaveChanges() fails.

There is a good MSDN Article How to: Manage Data Concurrency in the Object Context to read here.

This approach should be used for most EF persistence.

 

 

 

Enhanced by Zemanta

[MVC 3] MvcImage Project–How did i do Thumbnail Support?

March 24, 2012 4 comments

Before reading the following, please read up on the tutorial posts listed here to get up-to speed on how i have accomplished Image handling using MVC so far, as i wont be going into detail on the code i have updated changed, only new code:

http://garfbradazweb.wordpress.com/2012/02/26/mvc-3-mvcimage-project-its-alive/

Also, before i continue, you can download and use the code on my MvcImage Codeplex Project home page.

So firstly i updated the AjaxSubmit and ImageLoad controllers. I have updated them to segregate the Image and Thumbnail byte arrays within the Session. The AjaxSubmit controller references my new Extension method Images.CreateThumbnailToByte to create the Thumbnail. I will discuss this in a moment. Remember to read up in the previous tutorials on how i use these withn a JQuery Image Preview Plugin.

So the AjaxSubmit Code is:

       [AcceptVerbs(HttpVerbs.Post)]
        public ActionResult AjaxSubmit(int id)
        {

            Session["Image.ContentLength"] = Request.Files[0].ContentLength;
            Session["Image.ContentType"] = Request.Files[0].ContentType;
            byte[] b = new byte[Request.Files[0].ContentLength];
            Request.Files[0].InputStream.Read            (b, 0, Request.Files[0].ContentLength);
            Session["Image.ContentStream"] = b;

            if (id > 0)
            {
                byte[] thumbnail = Images.CreateThumbnailToByte(Request.Files[0].InputStream, 100, 100);

                Session["Thumbnail.ContentLength"] = thumbnail.Length;
                Session["Thumbnail.ContentType"] = Request.Files[0].ContentType;

                byte[] c = new byte[thumbnail.Length];
                Request.Files[0].InputStream.Read(c, 0,                 Request.Files[0].ContentLength);
                Session["Thumbnail.ContentStream"] = thumbnail;

            }

            return Content(Request.Files[0].ContentType + ";" + Request.Files[0].ContentLength);
        }

And the ImageLoad Controller Code – the id parameter is used to determine if to Respond with the Thumbnail binary or the Image binary.

       public ActionResult ImageLoad(int id)
        {
            if(id == 0)
            {
                byte[] b = (byte[])Session["Image.ContentStream"];
                int length = (int)Session["Image.ContentLength"];
                string type = (string)Session["Image.ContentType"];
                Response.Buffer = true;
                Response.Charset = "";
                Response.Cache.SetCacheability(HttpCacheability.NoCache);
                Response.ContentType = type;
                Response.BinaryWrite(b);
                Response.Flush();
                Response.End();
                Session["Image.ContentLength"] = null;
                Session["Image.ContentType"] = null;
                Session["Image.ContentStream"] = null;
            }

            //--The following is the Thumnbail id.

            if (id == 1)
            {
                byte[] b = (byte[])Session["Thumbnail.ContentStream"];
                int length = (int)Session["Thumbnail.ContentLength"];
                string type = (string)Session["Thumbnail.ContentType"];
                Response.Buffer = true;
                Response.Charset = "";
                Response.Cache.SetCacheability(HttpCacheability.NoCache);
                Response.ContentType = type;
                Response.BinaryWrite(b);
                Response.Flush();
                Response.End();
                Session["Thumbnail.ContentLength"] = null;
                Session["Thumbnail.ContentType"] = null;
                Session["Thumbnail.ContentStream"] = null;
            }
            return Content("");
        }

It is not the nicest code but it is there to show the logic behind what I’m trying to do. I’m following the same pattern as before but this time handling two byte arrays.

So lets resume talking about the following piece of code:

byte[] thumbnail = Images.CreateThumbnailToByte(Request.Files[0].InputStream, 100, 100);

So parameter 1 is the Stream of the Image, and the 2nd and 3rd parameters are the maximum height and width of the new resized Thumbnail Image. So using inspiration from Nathanael Jones (but not using it all as i didn’t have time), i maintain the aspect ratio of the image and create a new height and width by scaling the image properly. Then again based on advice from Nathanael’s article we create a new GDI drawing surface and create the Thumbnail Image. Then lastly we convert it back to a binary stream and return the byte array.

Here is the code:

        /// <summary>
        /// This method creates a Thumbnail Image and and scales it. /// It returns a byte array
        /// to be used.
        /// </summary>
        /// <param name="stream">Image Stream</param>
        /// <param name="maxHeight">Max Height (Used to scale the image</param>
        /// <param name="maxWidth">Max Width (Used to scale the image)</param>
        /// <returns>Scaled thumbail image byte array.</returns>
        public static byte[] CreateThumbnailToByte(Stream stream,         double maxHeight, double maxWidth)
        {
            int newHeight;
            int newWidth;
            double aspectRatio = 0;
            double boxRatio = 0;
            double scale = 0;

            Stream imageStream = new MemoryStream();
            Image originalImage;

            Streams.RewindStream(ref stream);
            using (originalImage = Image.FromStream(stream))
            {
                //--We need too maintain the aspect ratio on the image.
                aspectRatio = originalImage.Width / originalImage.Height;
                boxRatio = maxWidth / maxHeight;

                if (boxRatio > aspectRatio)
                {
                    scale = maxHeight / originalImage.Height;
                }
                else
                {
                    scale = maxWidth / originalImage.Width;
                }

                //--Scale the Original Images dimensions
                newHeight = (int)(originalImage.Height * scale);
                newWidth = (int)(originalImage.Width * scale);

                using (var bitmap = new Bitmap(newWidth, newHeight))

                //--Create a new GDI+ drawing surfance based on the original Image. 
 //--This methid allows us to alter
                //--it where necessary, based on advice from here. //--http://nathanaeljones.com/163/20-image-resizing -pitfalls/
                using (var graphics = Graphics.FromImage(bitmap))
                {
                    var rectangle = new Rectangle(0, 0, newWidth, newHeight);

                    graphics.CompositingQuality = CompositingQuality.HighQuality;
                    graphics.SmoothingMode = SmoothingMode.HighQuality;
                    graphics.InterpolationMode =                     InterpolationMode.HighQualityBicubic;
                    graphics.PixelOffsetMode = PixelOffsetMode.HighQuality;
                    graphics.DrawImage(originalImage, rectangle);

                    //--The same image to a new ImageStream so we can //--convert this to a byte array.
                    bitmap.Save(imageStream, originalImage.RawFormat);

                }

                byte[] thumbnail = new byte[imageStream.Length];
                BinaryReader reader = new BinaryReader(imageStream);
                imageStream.Seek(0, SeekOrigin.Begin);

                //--Load the image binary.
                thumbnail = reader.ReadBytes((int)imageStream.Length);
                return thumbnail;

            }        

       }

.

Now in the future i will look to extend this further to encompass all of Nathanael’s advice. So that’s it! Took a lot of work getting there, not just coding the solution but understanding it which is key.

The last thing i did was add more properties to my ImageModel to save the Thumbnail data separately from the main Image, so we don’t have to convert it each and every time we want to load the Thumbnail instead of the main Image binary.

So my new ImageModel class is now:

    /// <summary> 
    /// This class represents the table for Images and it necessary columns
    /// </summary>
    public class ImageModel : IImageModel
    {
        [Key]
        public Guid UniqueId { get; set; }
        [Required]
        public Guid TableLink { get; set; }
        public String RecordStatus { get; set; }
        [Date]
        public DateTime RecordCreatedDate { get; set; }
        [Date]
        public DateTime RecordAmendedDate { get; set; }
        public Byte[] Source { get; set; }
        public Int32 FileSize { get; set; }
        public String FileName { get; set; }
        [FileExtensions("png|jpg|jpeg|gif")]
        public String FileExtension { get; set; }
        public String ContentType { get; set; }
        public Int32 Height { get; set; }
        public Int32 Width { get; set; }

        //-- New in Alpha Release 0.0.2
        public Byte[] ThumbnailSource { get; set; }
        public Int32 ThumbnailFileSize { get; set; }
        public String ThumbnailContentType { get; set; }
        public Int32 ThumbnailHeight { get; set; }
        public Int32 ThumbnailWidth { get; set; }

    }
Enhanced by Zemanta

[MVC 3] Entity Framework and Serializing JSON–Circular References

September 22, 2011 Leave a comment

ADO.NET Entity Framework stack

Image via Wikipedia

I’m at the moment trying to write a light-weight AJAX control for my home page, which displays New Properties which have been added, aptly named WhatsNew.

The issue i had was the following error which i inspected in Fiddler when i didn’t have any JSON returned to my view:

A circular reference was detected while serializing an object type of “System.Data.Metadata.Edm.Properties”

A bit of googling, and it seems the JavaScriptSerializer has gets it knickers in a twist trying to traverse your Entity Framework objects which have relationships.

There are many options, but the one i took was to make the JSON as lightweight as possible and pass a ViewModel with only and not Entity Framework at objects at all. I build the view model based on data from the Entity Framework.

Here is my viewModel – the List was my old implementation which was causing the problems.

This will be returned as a JSON ActionResult to my view.  Bingo – I’ve fixed the issue! Smile

public class WhatsNewViewModel {
    //public List<PropertyViewModel> Properties { get; set; } 
 public string UniqiueKey { get; set; }
    public string Area { get; set; }
    public string PropertyType { get; set; }
}

Enhanced by Zemanta

[MVC 3] Images – Downloading Images

August 25, 2011 2 comments

UPDATE: I have now combined all my MVC Image Handling  blog posts into a Open Source Project. Feel free to check this out once you have read the post.

http://mvcimage.codeplex.com/

Original Blog Post:

The previous posts i have shown you how to upload images, and use JQuery to preview the image. We have also used Entity Framework 4.0 and SQL to save a image as a varbinary data type.

Now I’m going to show you how to download the image, and display it on your view, using a HTML Helper.

Before we start lets think about what we need:

  • HTML Helper Extension to help display the image
  • Controller classes to grab the image
  • Object validation methods

Lets start with the code for the HTML Helper Extension. You should be familiar with how these work now. Again, mine is to accept a model using Lambda expressions:

public static MvcHtmlString DisplayImageFor<TModel, TProperty>    (this HtmlHelper<TModel> helper,
    Expression<Func<TModel, TProperty>> expression, string alt = null,     string action = null string controller= null, string actionParameterName = null,string height = null, string width = null)
{

    if (String.IsNullOrEmpty(alt)){
        string _name = ExpressionHelper.GetExpressionText(expression);
        alt = helper.ViewContext.ViewData.TemplateInfo.        GetFullHtmlFieldName(_name);
    }

    if (String.IsNullOrEmpty(height)){
        height = "126px";
    }

    if (String.IsNullOrEmpty(width)){
        width = "126px";
    }

    if(String.IsNullOrEmpty(actionParameterName)){
        actionParameterName = "id";
    }

    ///---Set the default src settings if null 
 ///--- src element is made up of action, controller and acionParameterName 
 if (String.IsNullOrEmpty(action)){
        action = "GetImage";

    }

    if (String.IsNullOrEmpty(controller)){
        controller = "ImagePreview";
    }

    ModelMetadata metadata = ModelMetadata.FromLambdaExpression(expression,     helper.ViewData);
    Object value = metadata.Model;
    Type valueType = metadata.Model.GetType();
string src = null;

    if(ObjectValidation.IsStringType(valueType))
    {
        src = String.Format(CultureInfo.InvariantCulture,
        "/{0}/{1}/{2}",controller,action,value);
    }

    var imgBuilder = new TagBuilder("img");

    imgBuilder.MergeAttribute("alt", alt);
    imgBuilder.MergeAttribute("src", src);
    imgBuilder.MergeAttribute("height", height);
    imgBuilder.MergeAttribute("width", width);

    return MvcHtmlString.Create(imgBuilder.ToString(TagRenderMode.SelfClosing));

}

So in a nutshell:

  1. If the parameters are null we set up the default values for the HTML Elements.
  2. build the metadata from the ViewData dictionary. We can then load the metadata.Model data to an Object, i.e. the value of the model.
  3. We build the src element to include the link of the controller we will build later to get the image.
  4. We use again to build the HTML Elements.
  5. The alt element will end up being the models name if there isn’t a parameter passed, for e.g.
     1: <img width="126" height="126" alt="property.UniqueKey" src="/ImagePreview/GetImage/b8b03b6d-e30c-46d6-9cba-002f3a4699ee"/>

     

You will notice we have used a static method, ObjectValidation.IsStringType. This has been built to allow me to query the object type. I need this so i can make sure the parameter I’m passing to the controller is a String. Here is how i have implemented this:

public static bool IsStringType(Type type)
{
    if (type == null)
    {
        return false;
    }

    switch (Type.GetTypeCode(type))
    {
        case TypeCode.String:
            return true;

        case TypeCode.Object:
            if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>))
            {
                return IsStringType(Nullable.GetUnderlyingType(type));
            }

            return false;

    }

    return false;
}

Quite  a simple class, which passes the type of the object (Previously we used .GetType() to achieve this), then we test to see if the equals String. If it does, we return true.

The other check is if the object is generic and Nullable, we pass the underlying type back through to the IsStringType method.

We have now built our HTML Helper and our Object Validation method, now onto our GetImage action method we set in the src element.

        #region GetImage
        public ActionResult GetImage(Guid id)
        {
            //Guid ID = new Guid(id); Medium profileimage = new Medium();

            try {
                int count = db.Media.Count(c => c.Unique_Key == id);

                if(count > 0)
                {
                    profileimage =  db.Media.SingleOrDefault(i => i.Unique_Key == id);
                    //--Convert the Image data into Memory. 
 byte[] imagedata = profileimage.Source;
                    return File(imagedata, profileimage.Content_Type, profileimage.File_Name);

                }

                count = 0;
                count = db.Media.Count(c => c.Table_Link == id);

                if (count > 0)
                {
                    profileimage = db.Media.SingleOrDefault(i => i.Table_Link == id);
                    //--Convert the Image data into Memory. 
 byte[] imagedata = profileimage.Source;
                    return File(imagedata, profileimage.Content_Type, profileimage.File_Name);
                }

                return File(new byte[0], null);
            }
            finally {
                if (db != null)
                    db.Dispose();

            }

        }

        #endregion 

This controller method contains alot of my own bespoke business logic in obtaining the image. Which is why i do 2 checks.

Enhanced by Zemanta
Categories: Tutorial Tags: , ,

[MVC 3] Upload Image –SQL Server & Entity Framework

August 16, 2011 11 comments

UPDATE: I have now combined all my MVC Image Handling  blog posts into a Open Source Project. Feel free to check this out once you have read the post.

http://mvcimage.codeplex.com/

Original Blog Post:

The last post i described how to create a HTML Helper for Image Previewing. This time around we will be uploading an image to a Database, which is handled little differently than normal CRUD operations.

The following block of code shows my Create action method within my UserProfile controller:

 1:         public ActionResult Create(UserProfile userprofile,
 2:          HttpPostedFileBase imageLoad2)
 3:         {
 4:             var profileimage = new Medium();
 5:  
 6:             if (imageLoad2 != null)
 7:             {
 8:                 using (Image img =
                        Image.FromStream(imageLoad2.InputStream))
 9:                 {
 10:  
 11:                     //--Initialise the size of the array
 12:                     byte[] file = new byte[imageLoad2.InputStream.Length];
 13:  
 14:                     //--Create a new BinaryReader and set the InputStream
 //-- for the Images InputStream to the
 16:                     //--beginning, as we create the img using a stream.
 17:                     BinaryReader reader =
                              new BinaryReader(imageLoad2.InputStream);
 18:                     imageLoad2.InputStream.Seek(0, SeekOrigin.Begin);
 19:  
 20:                     //--Load the image binary.
 21:                     file = reader.ReadBytes((int)imageLoad2.
                                   InputStream.Length);
 22:  
 23:                     //--Create a new image to be added to the database
 24:  
 25:                     profileimage.Created_Date = DateTime.Now;
 26:                     profileimage.Source = file;
 27:                     profileimage.File_Size = imageLoad2.ContentLength;
 28:                     profileimage.File_Name = imageLoad2.FileName;
 29:                     profileimage.Content_Type = imageLoad2.ContentType;
 30:                     profileimage.Height = img.Height;
 31:                     profileimage.Width = img.Width;
 32: #if DEBUG
 33:                     profileimage.Record_Status = "T";   //--Testing.
 34: #else
 35:                         profileimage.Record_Status = " ";/--Live.
 36: #endif
 37:  
 38:  
 39:  
 40:                 }
 41:             }
 42:  
 43:             if (ModelState.IsValid)
 44:             {
 45:  
 46:                 db.Media.AddObject(profileimage);
 47:                 db.SaveChanges();
 48:  
 49:                 return RedirectToAction("Index", "Home");
 50:  
 51:             }
 52:  
 53:             var viewModel = new UserProfileViewModel
 54:             {
 55:                 UserProfile = userprofile,
 56:                 UserTypes = userTypes
 57:             };
 58:  
 59:             return View(userprofile);
 60:         }

This method creates has two parameters, the model UserProfile, and the imageLoad2 is the file posted. Have a look at the previous post about building the HTML.

NB: the parameter name imageLoad2 needs to be the name of the input file from the view so that is is bound (using the MVC Model Binding). If you used a different name the parameter would be null.

The method creates a Entity Framework model Medium, which is my class to describe the SQL Table which holds the Images. The images are saved within SQL as (MAX). Quite easy really, create a   to convert the image stream to binary,  then using the EF to the entity set, then save it to the table.

There we have it. Join me next time for downloading the image, using another custom HTML Helper.

 

Enhanced by Zemanta

[MVC 3] Image Preview –HTML Helpers

August 9, 2011 3 comments

UPDATE: I have now combined all my MVC Image Handling  blog posts into a Open Source Project. Feel free to check this out once you have read the post.

http://mvcimage.codeplex.com/

Original Blog Post:

In my i showed you how to preview an image using a combination of JQuery/AJAX and controller code behind.

This blog post i will show you how to take that a step further and take the HTML code and generate a nice HTML Helper. HTML Helpers are a nice way of re-using your code for future projects.

Lets take a look at the HTML code we wish to generate on the fly:

<input class="upload" type="file" name="imageLoad2" id="File1"/> <img alt="Preview" id="imgThumbnail2" height="126px" width="126px" /> 

This is the code that our JQuery, plays around with to preview an image. Not a lot of code, but wouldn’t be nice to encapsulate this within an Helper?

Firstly what are helpers? Well all they are is code that returns HTML either as a string or Mvchtmlstring. They are useful where you have a lot of conditional logic, plus they also allow you to pass through your model as LINQ Lambda Expression.  Most of the common ones are scaffolded when you create a CRUD controller, that creates your Controller and View using the default T4 Templates.

image

Ones like:

  1. Html.EditorFor()
  2. Html.LabelFor()
  3. Html.DropDownListFor()

There are lots of them, check them out using your Razor view.

image

So, to create my custom  helpers, I’ve created a folder in my Project called Helpers and a static class ImageHelpers . Because we are creating a HTML Helper that works like the ones included in the framework, we need to create them using . So you will notice the first parameter is thisHtmlHelper<TModel> helper which will define this extension method as an extension of HtmlHelper. The rest of the parameters allow us to customise the method if needed.

We also have created the method to allow us to pass the generic model and property types. This is useful so we can pass any of our models through, and later on we will add a new T4 template which includes varbinary in the templating. That is a different post though.

 public static class ImageHelpers {
     public static MvcHtmlString UploadImageFor<TModel, TProperty>
       (this HtmlHelper<TModel> helper,
       Expression<Func<TModel, TProperty>> expression, string id = null,
       string name = null, string cssclass = null, string alt = null,
       string imgId = null, string height = null, string width = null)
     {
         const string type = "file";

         if (cssclass == null)
         {
             cssclass = "upload";
         }

         if (id == null)
         {
             id = "File1";
         }

         if (name == null)
         {
             name = "imageLoad2";
         }

         if (alt == null)
         {
             alt = "Preview";
         }

         if (imgId == null)
         {
             imgId = "imgThumbnail2";
         }

         if (height == null)
         {
             height = "126px";
         }

         if (width == null)
         {
             width = "126px";
         }

         var fileBuilder = new TagBuilder("input");
         var imgBuilder = new TagBuilder("img");
         var mergedBuilder = new TagBuilder("p");

         ///Firstly we build the input tag. //--Add the CSS class. fileBuilder.AddCssClass(cssclass);        
        //--Add the name. fileBuilder.MergeAttribute("name", name);        
        //--Add the id. fileBuilder.MergeAttribute("id",id);        
        //--Add the type. fileBuilder.MergeAttribute("type",type);

        ///Secondly we build the img tag. //--Add the alt. imgBuilder.MergeAttribute("alt", alt);        
        //--Add the id. imgBuilder.MergeAttribute("id", imgId);        
        //--Add the height and width. imgBuilder.MergeAttribute("height", height);        
        imgBuilder.MergeAttribute("width", width);

         ///Merge the two together into a p tag. 
        mergedBuilder.AddCssClass("file-upload-");        
        mergedBuilder.InnerHtml += fileBuilder;        
        mergedBuilder.InnerHtml += imgBuilder;        
        return MvcHtmlString.Create(mergedBuilder.ToString());

     }

}

So, we use three variables to to build an input, image and paragraph tags. This is a nifty little class that allows us to build HTML Elements, and some methods and properties to allow us to quickly to get our desired result, and we return a once we have built it.

We set the default elements to be the names we use in our JQuery, so that the default logic is applied if we have our jquery-previewimage.js used.

We then add our Html helper to our view (After deleting the original code):

          <div class="editor-label"> @Html.LabelFor(model => model.ProfileImage)         
         </div> <div class="editor-field"> @Html.UploadImageFor(model => model.ProfileImage)         
        </div> 

This will generate the original code as seen here when run:

image

So there we have it, our own HTML Helper which we can re-use for future projects.

Categories: Tutorial Tags: , , ,

[MVC 3] Image Preview–JQuery

August 3, 2011 11 comments

UPDATE: I have now combined all my MVC Image Handling  blog posts into a Open Source Project. Feel free to check this out once you have read the post.

http://mvcimage.codeplex.com/

Original Blog Post:

At the moment I’m learning ASP.NET and MVC 3. Lets say I’m loving it at the moment, and at this present moment, I’m trying to employ my new skills in creating a new site for my friends friend, but i cannot disclose anymore on that….for now.

What i can disclose is snippets and code samples on my journey. Once such snippet is Image Previewing. Sounds easy, but as I’m new to this, it isn’t!

What I’m blogging about today and code I’m showing you is based on lots of articles i have read on the web. I’m one of those coders that cannot just Google + Copy & Paste, then move on to the next problem (to be resolved by the latter). I need to try and understand what the code is doing. So in the last week i have learnt:

  • JQuery and some JavaScript
  • A little more on how HTTP works behind the scenes using an awesome tool called Fiddler.
  • Specific browser requirements

Now before i move on, i have based my code on the following. Before this i was learning JQuery, so i wanted to amend the JavaScript portion to JQuery, but keeping the same concept:

http://weblogs.asp.net/imranbaloch/archive/2010/04/03/image-preview-in-asp-net-mvc.aspx

In addition my good friend and fellow Web Developer, Shaw Young, helped me with some tweaks! So thank you Imran for teaching me and Shaw for helping me!

Before i start discussing the JQuery, the following is the HTML that the code references.

        <input class="upload" type="file" name="imageLoad2" id="File1"/> <img alt="Preview" id="imgThumbnail2" height="26px" width="26px" /> 

So lets start from the beginning. I have created a JQuery script file, which is bound to the change JavaScript event, using the JQuery handler .change().

$(document).ready(function () {
  var inputTag = ".upload";
  var imgTag = "#imgThumbnail2";
  $(inputTag).change(function () {

I’m presuming you understand JQuery, but the .change() is hooking ANY input tags with the CSS class name of .upload and referencing any image tags with the id of  #imgThumbnail2 (which if you are using id, should be 1!).

Next we create a cloned image tag. We do this, because modern browsers will not allow you to copy the filename/directory values from one tag to another due to security reasons.  We then insert this cloned tag after the original .upload one.

NB: If you query the value in IE9 of the directory, you will be greeted with C:\Fakepath… now.

//-- Create a input attribute with the same type as the original //-- Insert it after the original's location. $('<input>').attr(
{
           class: "upload-clone",
           type: $(inputTag).attr('type'),
           name: "name-clone" }).insertAfter(inputTag);

Next we need to create a hidden form which we will post to one of our action methods Ajaxsubmit.

//--Create a hidden form with an action method pointer to //--our custom controller. $("<form>").attr(
{
           method: "post",
           id: "prototype",
           action: "/ImagePreview/AjaxSubmit" }).appendTo("body").hide();

We then need to change our new form’s encoding. Because different browsers behave differently, we change the attribute using the following.

//--Change the encoding based on the browser, as IE doesn't allow you to//--change the encoding. //--Append our original input to the hidden form. $('#prototype').attr((this.encoding ? 'encoding' : 'enctype'), 'multipart/form-data');

So we can submit the form and its contents, in this case the .upload input tag, we append it to the form, and hide it.

$(inputTag).appendTo("#prototype").hide();

We then use AJAX to submit the input .upload and its contents. Because we set the action method ImagePreview/Ajaxsubmit, this will be called, then the image tag will use the ImageLoad method. The first set is the JQuery and the 2nd is to re-iterate Imran’s method.

JQuery:

//--Use AJAX to post the form, and if successful, load the binary info the //--image tag. $('#prototype').ajaxSubmit(
        {
            success: function (responseText) {
                var d = new Date();

                $(imgTag)[0].src = "/ImagePreview/ImageLoad?a=" +                  d.getMilliseconds();

                $(inputTag).insertAfter('.upload-clone').show();

                $('.upload-clone').remove();
                $('#prototype').remove();

            }

        });

C# Action Methods:

public class ImagePreviewController : Controller {
    #region Ajax Submit
    [AcceptVerbs(HttpVerbs.Post)]
    public ActionResult AjaxSubmit(int? id)
    {
        Session["ContentLength"] = Request.Files[0].ContentLength;
        Session["ContentType"] = Request.Files[0].ContentType;
        byte[] b = new byte[Request.Files[0].ContentLength];
        Request.Files[0].InputStream.Read(b, 0, Request.Files[0].ContentLength);
        Session["ContentStream"] = b;
        return Content(Request.Files[0].ContentType + ";" +         Request.Files[0].ContentLength);
    }
    #endregion #region ImageLoad
    public ActionResult ImageLoad(int? id)
    {
        byte[] b = (byte[])Session["ContentStream"];
        int length = (int)Session["ContentLength"];
        string type = (string)Session["ContentType"];
        Response.Buffer = true;
        Response.Charset = "";
        Response.Cache.SetCacheability(HttpCacheability.NoCache);
        Response.ContentType = type;
        Response.BinaryWrite(b);
        Response.Flush();
        Response.End();
        Session["ContentLength"] = null;
        Session["ContentType"] = null;
        Session["ContentStream"] = null;
        return Content("");
    }
    #endregion }

So that’s it, with a little of JQuery magic, sprinkled with MVC goodness, we have an Image Previewing client side code, mixed with some AJAX.

I will try and get a solution up for people to use ASAP. In the meantime, if anyone needs it, let me know and i will email them the code.
Next up i will be:

  1. Adding some client side validation to make sure we are selecting image content types.
  2. Wiring up the JQuery to a HTML Helper using Razor for input file types.
  3. Create a T4 scaffolding template for input types to be re-used.
Categories: Tutorial Tags: , , , ,
Follow

Get every new post delivered to your Inbox.

Join 281 other followers

%d bloggers like this: