Using (and customizing) the AutoCompleteBox for Windows Phone

This is my first article on Windows Phone 7 development and I will focus on a subject that was useful in one of my recent projects: using the an autocomplete box in a Windows Phone 7 application.

By default, the Windows Phone SDK doesn’t include an AutoCompleteBox control for use, but as probably many people already know, the Silverlight Toolkit library released on CodePlex contains such a control designed for the phone.

In order to demo the usage of the AutoCompleteBox control we’ll create a very simple application which will allow us to select our favourite cities from a predefined list. I am using Microsoft Visual Studio 2010 Express for Windows Phone.

Once you downloaded the latest Windows Phone Silverlight Toolkit – November 2011 (7.1 SDK) and installed it, you will  need to add a reference to the Microsoft.Phone.Controls.Toolkit.dll assembly in your project.

add_reference

To use the AutoCompleteBox control in your XAML, you will need to edit the XAML source and add the toolkit namespace as shown before:

namespace

After this step you are ready to use the AutoCompleteBox in your page. The AutoCompleteBox is a rather simple control to use, looking like a regular TextBox, but as the user starts typing into it, it displays a suggestions drop-down list based on the matches found. For the AutoCompleteBox to offer the suggestions based on what’s being typed we need to make sure that we set the ItemsSource property of the control which takes an IEnumerable collection.

For the simplest use of the AutoCompleteBox, all you have to do once you performed the steps above is to create an AutoCompleteBox control in your XAML as shown below:

autocomplete_xaml

and then assign the ItemsSource property to a collection (I’ve done it in code behind, but you can also use binding in your XAML):

autocomplete_codebehind

That’s it! Now you can run your application and test it… If you would be typing the character s, you should be seeing something like this:

app1

If you want to get the value from the AutoCompleteBox, you can check the Text property of the AutoCompleteBox control.

In a real world application it makes sense that you may want to set the Text property to some previously stored value and then allow the user to modify it if necessary.

So, after setting the ItemsSource of the AutoCompleteBox to the list of cities, add an extra line that will also set the Text property to Suceava (value that exists in our list):

cities.Text = “Suceava”;

If you run the  application again, you notice you have a problem:

app2

The AutoCompleteBox control has the suggestions drop-down list opened… I don’t want that by default and I guess most people don’t want this behaviour as well. So let’s fix it.

We need to handle 3 simple events exposed by the AutoCompleteBox:

events

All the events are rather simple and their names are very clear: the GetFocus and LostFocus are triggered when the control gets and loses the focus, while the DropDownOpening event is triggered just before the opening of the suggestion drop-down allowing us to stop the suggestion if needed.

The GetFocus and LostFocus will set a boolean variable to true or false to allow us to know if the control is focused or not. In the DropDownOpening event we will use this boolean value and we will not allow the drop-down list to open if the control is not focused – just the scenario we have above. To stop the drop-down from opening we need to cancel the event. Here is the code:

events2

Try running the application again once you implemented the events above. The application should behave as expected: the control has by default the value “Suceava” and the drop-down list is not opened by default.

What we’ve done so far is a very simple scenario of AutoCompleteBox usage. In lots of cases it could be enough, in others not.

Imagine you want to allow your users to input multiple cities in the same AutoCompleteBox control, each city separated by a comma and for each city you want to help your user by providing him help (suggestions) based on what he’s typing.

If you run the application now and type a comma after your initial selection and then start typing the first letter of another city, you will not get any suggestions as the list provided to the ItemsSource property doesn’t contain any string that will match your entire text value.

To finish this demo, we’ll try to modify the default behaviour of the AutoCompleteBox and customize it to allow us to cover the previous scenario.

To implement this, we’ll need to:

  • use one variable to remember the previously typed cities
  • handle the TextChanged and SelectionChanged events of the AutoCompleteBox. The TextChanged event has the same use as for a TextBox control, while the SelectionChangedevent is triggered when the selection in the suggestion drop-down control is changed.
  • we need also to implement a custom search method that will be used by the TextFilter property of the AutoCompleteBox

We will use the TextChanged event to store in the new variable the previous cities that the user types, in the SelectionChanged we will make sure to append the newly selected city to the previously typed cities.

The custom filtering method will make sure that we use only what is after a comma as search term. Also, this method will make sure to filter out cities that we already typed.

So, to get to the coding part, we need to attach some more events to the AutoCompleteBox:

events3

The custom filter method is rather simple and looks like this:

textfilter

The TextChanged and SelectionChanged events are also simple:

changed_events

If you will run the application again you will see that the same AutoCompleteBox allows us to input multiple cities, separated by comma and for each new city we get suggestions as we type.

app3

Hope that this demo will be useful to others and hope the explanations were clear enough. The AutoCompleteBox is a very useful control in lots of cases and with a bit of customization it can be quite powerful.

A very useful article that I read when I first started to use the control is AutoCompleteBox for WP7 in depth which provides plenty of valuable information about the various properties and events exposed by the control. If you need to learn more about the control, check it as it’s a very good resource.

The full project for this demo can be downloaded here – feel free to take it, run it and play with it.

Also, any comments or suggestions related to this demo are welcomed, so drop me a line in the comments if you have something to say.

Happy coding!

My Delicious – Windows Phone 7 manager for your Delicious bookmarks

My first ever Windows Phone 7 application made it to the MarketPlace!

My Delicious is a Windows Phone 7 manager for your Deliciousbookmarks. It provides the ability to:

  • view your recent bookmarks
  • browse and filter your tags
  • filter bookmarks by tag
  • add a new bookmark
  • edit an existing bookmark
  • delete a bookmark
  • share a bookmark by email or any other account linked on your phone

The MarketPlace link for the app is http://www.windowsphone.com/en-US/apps/e63bffa0-986e-4ccc-94ae-38df542083ee or you can use the QR code below to get to the application on your mobile phone.My_Delicious_MarketPlace

For now I’ve set up a homepage for the application on my blog and it can be accessed from the main menu (top of the page) – My Delicious link – the page also contains screenshots of the application.

If you own a Windows Phone 7 device and use Delicious to save your bookmarks, go ahead and install the application.

Feedback, suggestions and comments are welcomed!

Using Propel ORM with Smarty templates in a PHP project – Part 1

Recently I decided to switch a project from ASP.NET to PHP and in order to ease my development I decided to use two interesting libraries:

  • Smarty – a template engine for PHP, facilitating the separation of presentation (HTML/CSS) from application logic. This implies that PHP code is application logic, and is separated from the presentation.
  • Propel ORM – open-source Object-Relational Mapping (ORM) for PHP5. It allows you to access your database using a set of objects, providing a simple API for storing and retrieving data.

This is the first part (out of 2) of the article which will focus on explaining how to install both libraries and use them in your PHP project, how to generate the Propel classes for working with an existing MySQL database, configure and initialize Propel for you website and finally, display data from your database using the Smarty library.

This part will focus on Propel ORM framework, how to install it and use it in a project for which you have already a MySQL database. So, without further delay, let’s get started with Propel…

1. Installing Propel ORM

First step we need to do is download and install the latest Propel ORM version. To download the latest version of Propel ORM you can access the Download page of the project and choose any of the options that fits you better. I downloaded the latest full propel package – version 1.5.6.

In order to use Propel ORM, you don’t need to actually run any installer, you just need to unzip the contents of the archive in a location convenient for you. Before you can run Propel ORM generator you will need to:

  • download the Phing library which is used by Propel ORM
  • after you download Phing, make sure that phing.bat and also php.exe can be found on your PATH environment variable. I had to add to my PATH both the location for phing.bat and php.exe in order to be able to run the Propel generator.

2. Generating the schema.xml file from your existing MySQL database

For the demo purposes of this article we’ll use a very simple MySQL database with a single table name people with the following structure:

    CREATE TABLE `people` (
      `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
      `name` varchar(30) NOT NULL,
      `age` int(3) unsigned NOT NULL,
      PRIMARY KEY (`id`)
    ) ;

The first step in using Propel ORM with an existing database is to generate the schema.xml file from our existing database. For that we need to create a folder on the disk where we’ll store all the Propel generated files (om classes, configuration files). For convenience I created my PropelTest folder inside my propel\generator\bin folder. In order to generate the schema.xml file we first need to create a build.properties file which I placed inside the PropelTest folder I just created. The build.properties file looks like this:

propel.project = PropelTest

# The Propel driver to use for generating SQL, etc.
propel.database = mysql

# This must be a PDO DSN
propel.database.url = mysql:dbname=PropelTest
propel.database.user = root
# propel.database.password =

Once you have the build.properties file you can run the propel-gen command. I did it from the propel\generator\bin folder:

> propel-gen PropelTest reverse

The first argument of the command is the folder in which we have the build.properties file. If the setup of Propel and Phing was done properly, the above command should work properly. If you have any errors try to fix them (I had few errors in the beginning and the error messages were pretty clear).

Once you run successfully the above command you should see in the PropelTest folder a new file: schema.xml

If you are curious to open it in any text editor, you will see the database definition:

SCHEMA.XML
  1. <?xml version="1.0" encoding="utf-8"?>
  2. <!--Autogenerated by PropelSchemaReverseTask class.-->
  3. <database name="PropelTest" defaultIdMethod="native">
  4. <table name="people" phpName="People">
  5. <column name="id" phpName="Id" type="INTEGER" size="11" primaryKey="true" autoIncrement="true" required="true"/>
  6. <column name="name" phpName="Name" type="VARCHAR" size="30" required="true"/>
  7. <column name="age" phpName="Age" type="INTEGER" size="3" required="true"/>
  8. </table>
  9. </database>
  10.  
 

3. Generating the model classes

Once we have the schema.xml file, we can generate the model classes we will use in your project to work with the database. In order to generate the classes we need to run the following command:

> propel-gen PropelTest om

If the command finishes running successfully, in the PropelTest folder you created you should see a new folder called build, which also contains a folder called classes. In the classes folder you should find another folder which you can use in you project. The name of this folder is given by the propel.project value in the build.properties file. In our case it’s again PropelTest.

For every table in the database, Propel creates 3 PHP classes:

  • a model class (e.g. People), which represents a row in the database;
  • a peer class (e.g. PeoplePeer), offering static constants and methods mostly for compatibility with previous Propel versions;
  • a query class (e.g. PeopleQuery), used to operate on a table to retrieve and update rows

More details on the model classes can be found here.

4. Generating the Propel initialization file

Once we have generated the model classes we have one step left to perform before actually using them in our project: we need to generate an initialization file for Propel framework. In order to generate our initialization file we’ll need to create one XML file named runtime-conf.xml which should look like this:

RUNTIME-CONF.XML
  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <config>
  3. <propel>
  4. <datasources default="PropelTest">
  5. <datasource id="PropelTest">
  6. <adapter>mysql</adapter> <!-- sqlite, mysql, myssql, oracle, or pgsql -->
  7. <connection>
  8. <dsn>mysql:host=localhost;dbname=PropelTest</dsn>
  9. <user>root</user>
  10. <password></password>
  11. </connection>
  12. </datasource>
  13. </datasources>
  14. </propel>
  15. </config>
 

According to the Propel documentation, the id  attribute for the datasource element must have the same value as the name attribute of the database element from schema.xml file. You can find more details about the runtime configuration file here.

Once you created the runtime-conf.xml file you can run the following command:

> propel-gen PropelTest convert-conf

After running this command, if you didn’t get any errors, you should see in the build folder a new folder called conf. In the new folder you should find your initialization file which in my case has the name PropelTest-conf.php.

5. Using Propel in your PHP project

In order to use Propel in my PHP project I created two additional folders in my project:

  • model – folder in which I copied the content of the build folder generated running the previous steps. If you’ll decide to do the same, in the model folder you’ll have the classes and conf folder with their contents.
  • propel – folder in which I copied the contents of the runtime\lib folder from the Propel installation.

Next step was to create a setup script for Propel which I am including in all the PHP scripts which need to access the database.

The setup script is very simple and looks like this:

propel_init.php
  1. <?php
  2. // Include the main Propel script
  3. require_once 'propel/Propel.php';
  4.  
  5. // Initialize Propel with the runtime configuration
  6. Propel::init("model/conf/PropelTest-conf.php");
  7.  
  8. // Add the generated 'classes' directory to the include path
  9. set_include_path("model/classes" . PATH_SEPARATOR . get_include_path());
  10. ?>
 

6. Adding, updating and deleting entries in the database with Propel

Once you generated the model classes, using them and interacting with the database is rather simple.

In order to show how to create, update and delete an entry for the people table from our database, I just created a simple PHP script which does all the operations mentioned before. The script is just for demo purpose, I am sure that for your projects you’ll get to use more complex queries.

The demo script is:

PHP
  1. <?php
  2. include("propel_init.php");
  3.  
  4. function PrintDetails($people, $message)
  5. {
  6. print $message."<br />ID: ".$people->getId()."<br />Name: ".$people->getName()."<br />Age: ".$people->getAge()."<br /><br />";
  7. }
  8.  
  9. // create a new record
  10. $me = new People();
  11. $me->setName('Andrei');
  12. $me->setAge(28);
  13. $me->save(); // save the record to the table
  14.  
  15. PrintDetails($me, "Created:");
  16.  
  17. // update the entry
  18. $people = PeopleQuery::create()->findPK(2); // find the people record with the Primary Key 1
  19. if($people != null)
  20. {
  21. PrintDetails($me, "Found:");
  22. $people->setName('Andrei Croitoriu');
  23. $people->save();
  24. PrintDetails($people, "Updated found entry to:");
  25.  
  26. $people->delete();
  27. print "Deleted the entry!";
  28. }
  29. ?>
 

For more details on the basic CRUD operations with Propel you can read this page.

I’ll stop here with this part of the article, hoping that the steps listed above with the explanations given will be enough to get you started with Propel. I know that the example given is very simple, but should be enough to make your understand how to use Propel.

In the following article (part 2 of this one) I will explain in a similar way how to get started with Smarty (at a very low level) and also how to use MySQL views with Propel to display data in your project.

In case you have any comments, questions and any sort of feedback for this article, please feel free to drop me a message.

Happy coding!

Playing with jQuery, Uploadify and LightBox

Recently I started working on a project in ASP.NET and one of the “features” I had to implement was to allow the user to upload photos. As user experience is important, I didn’t want to have a simple page which would post back refreshing the page, or just a “one by one” upload process.

Since the FileUpload control is one of the controls that don’t work inside the Update Panel, I had to look at some other options. As I am still learning jQuery and trying to improve in this area, I decided to look for some upload plugin written in jQuery. After some research on the Internet I managed to find Uploadify which seems like one of the best options out there: simple, good looking and easy to play with.

In one of my previous blog entries I was showing how to upload, store an image to a database and then retrieve it and add a watermark to the image. I will use this article as a starting point for this new application and just modify it in order to make the upload process more user friendly.

First step to use the Uploadify plugin is to import it to our project and in order to use it we need to use a simple input element of type “file”. Once we defined the input element in our page we need to call the uploadify method on it and specify the default values we want. After this small step, the upload page should look like:

HTML
  1. <form id=“form1” runat=“server”>
  2. <div>
  3. <script type=“text/javascript”>
  4. // <![CDATA[
  5. var id = “55”;
  6. var theString = “asdf”;
  7. $(document).ready(function() {
  8. $(‘#fileInput’).uploadify({
  9. ‘uploader’: ‘uploadify/uploadify.swf’,
  10. ‘script’: ‘Upload.ashx’,
  11. ‘scriptData’: { ‘id’: id, ‘foo’: theString },
  12. ‘cancelImg’: ‘uploadify/cancel.png’,
  13. ‘auto’: false,
  14. ‘multi’: true,
  15. ‘fileDesc’: ‘Image Files’,
  16. ‘fileExt’: ‘*.jpg;*.png;*.gif;*.bmp;*.jpeg’,
  17. ‘queueSizeLimit’: 5,
  18. ‘sizeLimit’: 512000,
  19. ‘buttonText’: ‘Choose Images’,
  20. ‘folder’: ‘/uploads’,
  21. ‘onComplete’: function(event, queueID, fileObj, response, data) {
  22. },
  23. ‘onAllComplete’: function(event, queueID, fileObj, response, data) {
  24. }
  25. });
  26. });
  27. // ]]></script>
  28. <input id=“fileInput” name=“fileInput” type=“file” />
  29. <a href=“javascript:$(‘#fileInput’).uploadifyUpload();”>Upload Files</a>
  30. </div>
  31. </form>

I will not go into explaining all the parameters used for setting up Uploadify, I will just highlight 3 parameters that were of interest for me:

  • queueSizeLimit: according to the official documentation it allows you to set the maximum number of files that can exist in the queue at one time.  If the user tries to add more the the maximum limit, the onQueueFull function is triggered.  The multioption should be used in conjunction with this option.
  • sizeLimit: allows you to set the maximum file size allowed per file in bytes.  If the file exceeds the size limit, it will return an error on upload.
  • script: is required and points to the back-end script that will process your files.  The back-end script can be written in any server-side language.

As you can see I set my upload control to allow a maximum of 5 files at one time and the maximum size allowed for one file is 512000 bytes. The Upload.ashx script holds the server code which is responsible for getting the image the user tries to upload, store it in the database and reply back to the client. Since we allow the user to upload an image, I want to allow them to delete the posted image in case they changed their mind. And I want to do it by the simple click of the mouse, no page postbacks… and why not show the uploaded images in an elegant way with LightBox?

Well, let’s try to do that…

We’ll need to modify our upload page just a bit to accommodate an area where the images will be displayed after upload and also add a small piece of code which will display the images in case of successful upload – the code which will display the images will be send as response by the Upload.ashx script. I added one div element at the end of my previous code and gave it an id=”uploaded_photos” and also modified the code in the onComplete event of the uploadify method (changes are in italic font):

HTML
  1. <form id=“form1” runat=“server”>
  2. <div>
  3. <script type=“text/javascript”>
  4. // <![CDATA[
  5. var id = “55”;
  6. var theString = “asdf”;
  7. $(document).ready(function() {
  8. $(‘#fileInput’).uploadify({
  9. ‘uploader’: ‘uploadify/uploadify.swf’,
  10. ‘script’: ‘Upload.ashx’,
  11. ‘scriptData’: { ‘id’: id, ‘foo’: theString },
  12. ‘cancelImg’: ‘uploadify/cancel.png’,
  13. ‘auto’: false,
  14. ‘multi’: true,
  15. ‘fileDesc’: ‘Image Files’,
  16. ‘fileExt’: ‘*.jpg;*.png;*.gif;*.bmp;*.jpeg’,
  17. ‘queueSizeLimit’: 5,
  18. ‘sizeLimit’: 512000,
  19. ‘buttonText’: ‘Choose Images’,
  20. ‘folder’: ‘/uploads’,
  21. ‘onComplete’: function(event, queueID, fileObj, response, data) {
  22. $(‘#uploaded_photos’).append(response);
  23. },
  24. ‘onAllComplete’: function(event, queueID, fileObj, response, data) {
  25. }
  26. });
  27. });
  28. // ]]></script>
  29. <input id=“fileInput” name=“fileInput” type=“file” />
  30. <a href=“javascript:$(‘#fileInput’).uploadifyUpload();”>Upload Files</a>
  31. </div>
  32. </form>
  33. <div id=“uploaded_photos”></div>

Since our upload page is ready we need to start writing some code to get the images, store them and reply to the client in case the upload is successful. To add a new generic handler right-click on the project and then select Add –> New Item. In the dialog window that appears select Generic Handler and name it Upload.ashx. The code for the Upload.ashx looks like this:

C#
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Web;
  5. using System.Web.Services;
  6. using System.Text;
  7. namespace UploadImages
  8. {
  9. /// <summary>
  10. /// Summary description for $codebehindclassname$
  11. /// </summary>
  12. [WebService(Namespace = “http://tempuri.org/”)]
  13. [WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
  14. public class Upload : IHttpHandler
  15. {
  16. public void ProcessRequest(HttpContext context)
  17. {
  18. try
  19. {
  20. HttpPostedFile file= context.Request.Files[“Filedata”];
  21. byte[] image_data = new byte[file.ContentLength];
  22. file.InputStream.Read(image_data, 0, file.ContentLength);
  23. ImagesDbEntities2 ent = new ImagesDbEntities2();
  24. images img = new images();
  25. img.name = file.FileName;
  26. img.data = image_data;
  27. img.type = file.ContentType;
  28. ent.AddToimages(img);
  29. ent.SaveChanges();
  30. #region update the number of images the user can upload
  31. int imgs = 5; // the number of images by default – in case of error set to 0
  32. // try to limit the allowed images to 5
  33. IEnumerable<images> images = from i in ent.images select i;
  34. if (images != null && images.Count() != 0)
  35. {
  36. imgs = 5 images.Count();
  37. if (imgs < 0) imgs = 0;
  38. }
  39. else
  40. imgs = 5;
  41. #endregion
  42. // create the delete link ID which will be used in the jquery selector
  43. string deleteLinkID = “deleteImage” + img.id;
  44. StringBuilder sb = new StringBuilder();
  45. sb.Append(“<script>”);
  46. sb.Append(“$(\”a[class=gallery]\”).lightBox();”);
  47. sb.Append(“$(‘#fileInput’).uploadifySettings(‘queueSizeLimit’,” + imgs.ToString() + “);”);
  48. sb.Append(“$(\”#” + deleteLinkID + \”).click(function(){ “);
  49. sb.Append(“if(confirm(\”Are you sure you want to delete the image?\”)) { “);
  50. // start creating the script which will delete the image by using AJAX call to a web service
  51. sb.Append(“$.ajax({“);
  52. sb.Append(“type: \”POST\”,”);
  53. // define the service method that will be used to delete the image
  54. sb.Append(“url: \”ImagesService.asmx/DeleteImage\”,”);
  55. sb.Append(“data: \”{id: “ + img.id + “}\”,”);
  56. sb.Append(“contentType: \”application/json; charset=utf-8\”,”);
  57. sb.Append(“dataType: \”json\”,”);
  58. sb.Append(“success: function(msg) {“);
  59. // if the call is successful append the result so we’ll hide the image and update the# of images the user can select
  60. sb.Append(“if(msg.d != \”\”) { $(\”#uploaded_photos\”).append(msg.d); }”);
  61. sb.Append(“}});”);
  62. sb.Append(” } “); // the matching } for the if(confirm)
  63. sb.Append(” })”);
  64. sb.Append(“</script>”);
  65. string response = “<div id=\”divImage” + img.id +
  66. \” style=\”float:left; padding: 5px; text-aling: center;\”><a class=\”gallery\”
  67. href=\”GetImage.aspx?id=” + img.id + \”><img src=\”GetImage.aspx?size=t&id=” +
  68. img.id + \”></a><br /><a id=\” + deleteLinkID +
  69. \” href=\”#\” title=\”Delete\”>Delete</a></div>” + sb.ToString();
  70. context.Response.Write(response);
  71. }
  72. catch(Exception ex)
  73. {
  74. context.Response.Write(“”);
  75. }
  76. }
  77. public bool IsReusable
  78. {
  79. get
  80. {
  81. return false;
  82. }
  83. }
  84. }
  85. }

The last step is to create the ImagesService, by right-clicking on the project and then selecting Add –> New Item. In the dialog window that appears select Web Service and name it ImagesService with the .asmx extension. I had to uncomment one line in the file which was generated: I had to remove the comment for the [System.Web.Script.Services.ScriptService] attribute.

After removing the default method created generated by Visual Studio for me, I created my DeleteImage method which is called by the jQuery AJAX method in the Upload.ashx handler:

C#
  1. [WebMethod]
  2. public string DeleteImage(string id)
  3. {
  4. try
  5. {
  6. long imageID = long.Parse(id);
  7. ImagesDbEntities2 ent = new ImagesDbEntities2();
  8. images image = (from img in ent.images where img.id == imageID select img).FirstOrDefault();
  9. if (image != null)
  10. {
  11. ent.DeleteObject(image);
  12. ent.SaveChanges();
  13. }
  14. #region update the number of images the user can upload
  15. int imgs = 5; // the number of images by default – in case of error set to 0
  16. // try to limit the allowed images to 5
  17. IEnumerable<images> images = from i in ent.images select i;
  18. if (images != null && images.Count() != 0)
  19. {
  20. imgs = 5 images.Count();
  21. if (imgs < 0) imgs = 0;
  22. }
  23. else
  24. imgs = 5;
  25. #endregion
  26. StringBuilder sb = new StringBuilder();
  27. sb.Append(“<script>”);
  28. // update the number of images the user can select
  29. sb.Append(“$(‘#fileInput’).uploadifySettings(‘queueSizeLimit’,” + imgs.ToString() + “);”);
  30. sb.Append(“$(\”#divImage” + id + \”).fadeOut();”); // fadeout the image
  31. sb.Append(“</script>”);
  32. return sb.ToString();
  33. }
  34. catch (Exception ex)
  35. {
  36. return “”;
  37. }
  38. return “”; // by default
  39. }

In the screenshots below you can see the above code in action during the various steps:

default

1. the plugin before any action

images_selected

2. after some files were selected

images_uploaded

3. images inserted in the page after successful upload

lightbox_image

4. see images using the LightBox plugin

confirm_delete

5. clicking on the delete link first brings a confirmation dialog

after_delete

6. after deleting an image

The above pieces of code are working and can be used by someone looking to implement such a functionality in a project, but they can be extended and why not improved. In my real application I am associating the uploaded images to the logged in user, I send some additional info during the upload of images using the scriptData option of Uploadify plugin and of course I improved the looks of the page using some extra CSS.

Hope that the above pieces of code will be helpful for some and I am opened for suggestions or comments related to the code posted here.

UPDATE: I uploaded the code for this sample here.

Enjoy!

Adding a “watermark” to an image in ASP.NET

Currently I am working on a personal project in ASP.NET and one of the tasks is to store images in a MySQL database and later retrieve and show them to the users.

Just for demo purpose, the MySQL table structure is very simple:

db_structure

First step of my task was actually storing the images in the MySQL table and for that I used Entity Framework in order to simplify the data handling.

For adding a new image to the database I created one very simple ASP.NET page:

ASP.NET
  1. <asp:FileUpload ID="fileUploadControl" runat="server" />
  2. <asp:Button ID="UploadButton" runat="server" Text="Upload" OnClick="UploadButton_Click" /><br />
  3. <asp:Label ID="StatusLabel" runat="server" Text="" />

and the code behind for uploading the image and displaying the image details in case of success, or the error in case of any issue is:

C#
  1. protected void UploadButton_Click(object sender, EventArgs e)
  2. {
  3. if (fileUploadControl.HasFile)
  4. {
  5. try
  6. {
  7. ImagesDbEntities2 ent = new ImagesDbEntities2();
  8. images img = new images();
  9. img.name = fileUploadControl.FileName;
  10. img.data = fileUploadControl.FileBytes;
  11. img.type = fileUploadControl.PostedFile.ContentType;
  12. ent.AddToimages(img);
  13. ent.SaveChanges();
  14. StatusLabel.ForeColor = Color.Black;
  15. StringBuilder sb = new StringBuilder();
  16. sb.Append("File Name: " + fileUploadControl.FileName + "<br />");
  17. sb.Append("File Size: " + fileUploadControl.PostedFile.ContentLength + " bytes <br />");
  18. sb.Append("File Type: " + fileUploadControl.PostedFile.ContentType + "");
  19. StatusLabel.Text = sb.ToString();
  20. }
  21. catch (Exception ex)
  22. {
  23. StatusLabel.ForeColor = Color.Red;
  24. StatusLabel.Text = "EXCEPTION: " + ex;
  25. }
  26. }
  27. else
  28. {
  29. StatusLabel.ForeColor = Color.Red;
  30. StatusLabel.Text = "Please select a file to upload!";
  31. }
  32. }

The simple code above allows us to store images in the MySQL table, so next task is to retrieve an image and display it on a web page. For this I created a simple ASP.NET webpage which based on the image ID I pass in the query string it returns the image with that ID from the database. There is only code-behind written for this task and I have 2 implementations:

  1. writing some simple text on top of the image and center it
  2. using a custom image with transparent background and center it on top of the image

Most of the implementation for both ways of adding the watermark to the image is the same and consists in retrieving the table entry based on the ID we pass in the URL query string, creating a MemoryStream object based on the bytes stored on the data field and then create an Image object from the MemoryStream created previously. Also in order to write a text on top of the image or display the watermark image over the initial image we need to create a Graphics object and call the DrawString or DrawImage methods.

So, here is the code to display a text over the image retrieved from the database:

C#
  1. public partial class GetImage : System.Web.UI.Page
  2. {
  3. MemoryStream ms = null;
  4.  
  5. protected void Page_Load(object sender, EventArgs e)
  6. {
  7. if (!IsPostBack)
  8. {
  9. string id = Request.QueryString["id"];
  10. if (!string.IsNullOrEmpty(id))
  11. {
  12. ImagesDbEntities2 ent = new ImagesDbEntities2();
  13. long imgID = Convert.ToInt64(id);
  14. var image = (from img in ent.images where img.id == imgID select img).FirstOrDefault();
  15. if (image != null)
  16. {
  17. MemoryStream ms = new MemoryStream(image.data);
  18. System.Drawing.Image memImage = System.Drawing.Image.FromStream(ms);
  19. Graphics g = Graphics.FromImage(memImage);
  20. Font f = new Font("Arial", 12, FontStyle.Bold);
  21. SolidBrush brush = new SolidBrush(Color.Red);
  22. SizeF stringDimens = g.MeasureString("AndreiC - 2010", f, 300);
  23. if (stringDimens.Width < memImage.Width && stringDimens.Height < memImage.Height)
  24. {
  25. g.DrawString("AndreiC - 2010", f, brush,
  26. (memImage.Width - stringDimens.Width) / 2,
  27. (memImage.Height - stringDimens.Height) / 2);
  28. }
  29.  
  30. Response.ContentType = "image/Jpeg";
  31. memImage.Save(Response.OutputStream, ImageFormat.Jpeg);
  32.  
  33. // Clean-up
  34. ms.Dispose();
  35. memImage.Dispose();
  36. g.Dispose();
  37. }
  38. }
  39. }
  40. }
  41. }

If we want to display the image with ID=1 from the database, all we have to do is add the following HTML code in the page:

HTML
  1. <img src="GetImage.aspx?id=1" />

The result is:

text_test

In case you want to add an image as watermark over your database stored image, you can use the following code:

C#
  1. public partial class GetImage : System.Web.UI.Page
  2. {
  3. MemoryStream ms = null;
  4.  
  5. protected void Page_Load(object sender, EventArgs e)
  6. {
  7. if (!IsPostBack)
  8. {
  9. string id = Request.QueryString["id"];
  10. if (!string.IsNullOrEmpty(id))
  11. {
  12. ImagesDbEntities2 ent = new ImagesDbEntities2();
  13. long imgID = Convert.ToInt64(id);
  14. var image = (from img in ent.images where img.id == imgID select img).FirstOrDefault();
  15. if (image != null)
  16. {
  17. System.Drawing.Image watermark = System.Drawing.Image.FromFile(
  18. Server.MapPath("Images/andreic_watermark.png"));
  19. MemoryStream ms = new MemoryStream(image.data);
  20. System.Drawing.Image memImage = System.Drawing.Image.FromStream(ms);
  21. Graphics g = Graphics.FromImage(memImage);
  22.  
  23. // draw watermark only if image is larger than watermark image
  24. if (watermark.Height < memImage.Height && watermark.Width < memImage.Width)
  25. {
  26. g.DrawImage(watermark,
  27. (memImage.Width - watermark.Width) / 2,
  28. (memImage.Height - watermark.Height) / 2);
  29. }
  30. Response.ContentType = "image/Jpeg";
  31. memImage.Save(Response.OutputStream, ImageFormat.Jpeg);
  32. // Clean-up
  33. ms.Dispose();
  34. watermark.Dispose();
  35. memImage.Dispose();
  36. g.Dispose();
  37. }
  38. }
  39. }
  40. }
  41. }

For this implementation I had to create one additional PNG image with transparent background and I placed it in the project inside a folder named Images.

The HTML code to show the image stays the same and the result is:

img_watermark_test

Hope this helps!