Friday, July 29, 2011

Silverlight Drawing Tool: Silver Draw Whiteboard with Undo , Redo and Save as JPEG

My requirement was to create a whiteboard where user can draw simple shapes  and also erase drawing. User can also undo and redo drawing as they do in MS paint. After completing drawing user can also save the drawing as Image.

I did not wanted to reinvent wheel so I tried to find good Silverlight tool which support basic drawing. I found some Silverlight tools which give me such basic drawing facility. Silver Draw (http://www.codeproject.com/KB/silverlight/silverdraw.aspx) seems more promising. Which give me all basic functionality with nice color picker also.  I can draw with Pen, Brush . Also I can draw line, rectangle and ellipse. This also give WPF duplex chatting and sharing facility. I did not need sharing facility so I omit this in my example.
image
Now It reduce my work and I only need to add eraser, undo , redo and save facility.

Eraser :

Eraser is simply drawing brush which used to draw ellipse with 25 width and height . Its color is same as background color so that user will think that it act as eraser.

case CurrentTool.EraseBrush:
{
HideVirtualLine();
var spot = toolHelper.CreateBrush(PrevPoint, cupt, 25);
(spot as Shape).StrokeThickness = 0;
(spot as Shape).Fill = new SolidColorBrush(Color.FromArgb(255, 255, 255, 255));
_canvas.Children.Add(spot);
tempHolder.Add(spot as Shape);
//AddToUndoShape(spot as Shape);

PrevPoint = cupt;
break;

}

Undo, Redo :

Removing Line, Rectangle and Ellipse is easy because after mouse over this shape is added in canvas. But as pencil and brush do not have defined shape so line is created on every Mouse Move. But When user click on undo button I like to remove all pencil stroke that user draws at a time with mouse over. So I maintain another list for keeping every stroke on mouse move.


public Point DrawOnMove(Point cupt)
{
switch (tool)
{
case CurrentTool.Brush:
{
…….


break;

}

case CurrentTool.EraseBrush:
{
………..


break;

}
case CurrentTool.Pencil:
{
var pen = toolHelper.CreatePen(PrevPoint, cupt);
ApplyAttributes(pen as Shape);
(pen as Shape).StrokeThickness = 3;
_canvas.Children.Add(pen);
tempHolder.Add(pen as Shape);
PrevPoint = cupt;
break;
}
default:
………
}
return cupt;
}

here tempHolder contains every pencil stroke on mouse movement. And when user finish drawing with Mouse Over this collection is added to a dictionary  so that after clicking on undo, redo button this collection  is Added/Remove at a time.

public Point DrawOnComplete(Point cupt)
{
switch (tool)
{
case CurrentTool.Pen:
{
var pen = toolHelper.CreatePen(PrevPoint, cupt);
ApplyAttributes(pen as Shape);
_canvas.Children.Add(pen);
PrevPoint = cupt;
AddToUndoShape(pen as Shape);
break;

}

case CurrentTool.Rectangle:
{
……….;
break;
}
case CurrentTool.Ellipse:
{
……..


break;
}
case CurrentTool.Brush:
case CurrentTool.EraseBrush:
case CurrentTool.Pencil:
{
var shp = toolHelper.CreatePen(PrevPoint, cupt);
List<Shape> UnshapeList = new List<Shape>();
tempHolder.ForEach(p => UnshapeList.Add(p));
UnShapeDrawingItems.Add(shp as Shape, UnshapeList);
tempHolder.Clear();
AddToUndoShape(shp as Shape);
}
break;
}



return cupt;
}


 
Here for Pen, Rectangle and Ellipse, Shape is added to canvas and this shape is added to Undolist so that User can undo this Shape. But for Brush, EraseBrush and Pencil list of mouse


movement is added to a Dictionary by creating a virtual Pen as Key of Dictionary. This virtual Pen actually represent total mouse movement of Pencil/ Brush and this Pen is added to UndoList. 

My undo and redo list size is 400. When user click on Undo button UndoShape() function is called and when user click on redo then RedoShape() is called.For top item as Line/Ellipse and Rectangle


when user click on undo then this shape is removed from canvas and added to redo list. But for Pencil/Brush shape all strokes made by user before mouse over is removed from canvas at a time.


And the virtual Pen key item is added to redo list. RedoShape() function is completely opposite to UndoShape().


public void UndoShape()
{
if (undoTop > 0)
{
undoTop--;
Shape shape = UndoList[undoTop];
if (shape is Line && UnShapeDrawingItems.ContainsKey(shape))
{
foreach (var unShapeDrawingItem in UnShapeDrawingItems[shape])
{
_canvas.Children.Remove(unShapeDrawingItem);
}
}
else
_canvas.Children.Remove(shape);
UndoList.RemoveAt(undoTop);

AddToRedoList(shape);
}
}
public void RedoShape()
{
if (redoTop > 0)
{
redoTop--;
Shape shape = redoList[redoTop];
if (shape is Line && UnShapeDrawingItems.ContainsKey(shape))
{
foreach (var unShapeDrawingItem in UnShapeDrawingItems[shape])
{
_canvas.Children.Add(unShapeDrawingItem);
}
}
else
_canvas.Children.Add(shape);
redoList.RemoveAt(redoTop);
AddToUndoShape(shape);
}
}

private void AddToRedoList(Shape shape)
{
if (redoTop >= 400)
{
RemoveRedoBottom();
}
redoTop++;
redoList.Add(shape);
}

private void RemoveRedoBottom()
{
redoTop--;
redoList.RemoveAt(0);
}

public void AddToUndoShape(Shape shape)
{
if (undoTop >= 400)
{
RemoveUndoBottom();
}
undoTop++;
UndoList.Add(shape);

}

private void RemoveUndoBottom()
{
undoTop--;
UndoList.RemoveAt(0);
}


Saving as Image:

Now I have given Erase and Undo , Redo facility to user. And user want to save this created drawing in their server. I found an example (http://www.andybeaulieu.com/silverlight/3.0/printablesilverlight/printablesilverlight.aspx)where canvas is saved as PNG in postback. But the size of PNG is more than 2MB. I do not need so high quality image and want to reduce its size. So for image I prefer JPEG. I used FJ.Core dll for JPEG encoding as give in this stackoverflow (http://stackoverflow.com/questions/1139200/using-fjcore-to-encode-silverlight-writeablebitmap) . I also have reduced the size of image with ImageResizer.


private static string GetBase64Jpg(WriteableBitmap bitmap)
{
int width = bitmap.PixelWidth;
int height = bitmap.PixelHeight;
int bands = 3;
byte[][,] raster = new byte[bands][,];

for (int i = 0; i < bands; i++)
{
raster[i] = new byte[width, height];
}

for (int row = 0; row < height; row++)
{
for (int column = 0; column < width; column++)
{
int pixel = bitmap.Pixels[width * row + column];
raster[0][column, row] = (byte)(pixel >> 16);
raster[1][column, row] = (byte)(pixel >> 8);
raster[2][column, row] = (byte)pixel;
}
}

ColorModel model = new ColorModel { colorspace = ColorSpace.RGB };
FluxJpeg.Core.Image img = new FluxJpeg.Core.Image(model, raster);
MemoryStream stream = new MemoryStream();
ImageResizer resizer = new ImageResizer(img);
var resizedImage =resizer.Resize(300, 300,ResamplingFilters.NearestNeighbor);
JpegEncoder encoder = new JpegEncoder(resizedImage, 90, stream);
encoder.Encode();

stream.Seek(0, SeekOrigin.Begin);
byte[] binaryData = new Byte[stream.Length];
long bytesRead = stream.Read(binaryData, 0, (int)stream.Length);

string base64String =
System.Convert.ToBase64String(binaryData,
0,
binaryData.Length);

return base64String;
}

Now this whiteboard become a complete with Erase, Undo/ Redo and Save facility. You can find modified Silver Draw code with these feature in

http://dl.dropbox.com/u/20275838/SilverlightClient.rar



Tuesday, June 21, 2011

Writing Javascript in object oriented way : Namespace, Encapsulation : Public , Private , Static method

I used to develop a control which contain more than 6 tab and very tab contains control which work based on javascript functionality.My code became unmanageable and I decide to write my JavaScript code in object oriented way as I write my C# code. I found a code from codeproject for adding namespace in your code in good way.  I have share here the like of this article . http://www.codeproject.com/KB/scripting/jsnamespaces.aspx
So I created my class with name System.Classes.Placement as
registering that class in using.js
Namespace.Register("System.Classes.Placement");

and in my page
System.Classes.Placement = function () {
……
}
Now need some code public which will initialize jquery popup,  and register click handler. and also some Jquery drag drop code.
So I created functions for those function but those do not need to be public. I need only one Init() public function and CreateJqueryPopUp(), CreateDragable(), CreateDroppable() do not need to be public we can create them as private wich provide more encapsulation. So I created public public with return statement which is more readable. And other function are created before return statement.

So the function became like that
System.Classes.Placement = function () {
    var CreateJqueryPopUp =function()   {…..}
    var CreateDragable = function() {…..}
    var handleDropEvent = function(){….}
   return
  {
       init:function()
      {
        CreateJqueryPopUp ();
        CreateDragable();
      }
  };
}
and called the init() function in document ready method for initialization.
$(document).ready(function () {
            var placement = new System.Classes.Placement();            
            placement.init();
                   });
This words fine. But I also need function for deleting one item. as it would be on button click event and I do not like to create instance again for that to call RemoveItem method also I want RemoveItem method would be in that namespace. So for that I need to create a static method so that I can call this method like
onclick = "System.Classes.Placement.RemoveItem(this)"
Creating a static method is also easy. You need to define a method outside of class like
System.Classes.Placement=function(){
…..
   return{
     …
     };
}
System.Classes.Placement.RemoveItem =  function (div) {
            var parent = div.parentNode.parentNode;
            $(parent).empty();
            $(parent).removeClass('selected');
        }
So I got almost same flavor of Object oriented programing in javascript as in C# just with little convention. As much your code would be object oriented code would be more manageable.

Friday, May 27, 2011

ASP.NET server side handler for Valums Ajax file upload ; file uploader supports multiple file upload with progress bar , drag-and-drop

This ajax uploader uses XHR for uploading multiple files with progress-bar in FF3.6+, Safari4+, Chrome and falls back to hidden iframe based upload in other browsers, providing good user experience everywhere. You can also see php demo in Valums site http://valums.com/ajax-upload/
The current implementation has server side handler for java, php and Perl. But does not exist Asp.net handler. Here I have implemented a Asp.net handler for ajax file upload that supports  IE, Firefox and Chrome.
Problem is IE use context.Request.Files[] for sending file to server. But Firefox and Chrome use Context.Request.InputStream. So in handler you need to check both for reading stream.
For Firefox and Chrome you get fileName from header like
String filename = HttpContext.Current.Request.Headers["X-File-Name"];
Code that work in Firefox and Chrome
//This work for Firefox and Chrome.
Stream inputStream = HttpContext.Current.Request.InputStream;
FileStream fileStream = new FileStream(mapPath + "\\" + filename, FileMode.OpenOrCreate);
inputStream.CopyTo(fileStream);
fileStream.Close();
context.Response.Write("{success:true, name:\"" + filename + "\", path:\"" + path + "/" + filename + "\"}");
But for IE you need to use
HttpPostedFile uploadedfile = context.Request.Files[0];
Code that work for IE browser :
HttpPostedFile uploadedfile = context.Request.Files[0];
filename = uploadedfile.FileName;
uploadedfile.SaveAs(mapPath + "\\" + filename);
context.Response.Write("{success:true, name:\"" + filename + "\", path:\"" + path + "/" + filename + "\"}");
Here the response is send as JSON string and you will get JSON object as response. you need to send {success:true} to make ajax upload understand that file upload is successful otherwise you can send false.

Complete code is:
public void ProcessRequest(HttpContext context)
      {
          const string path = "Capture/Images";
          String filename = HttpContext.Current.Request.Headers["X-File-Name"];
          if (string.IsNullOrEmpty(filename) && HttpContext.Current.Request.Files.Count <= 0)
          {
              context.Response.Write("{success:false}");
          }
          else
          {
              string mapPath = HttpContext.Current.Server.MapPath(path);
              if (Directory.Exists(mapPath) == false)
              {
                  Directory.CreateDirectory(mapPath);
              }
              if (filename == null)
              {
                  //This work for IE
                  try
                  {
                      HttpPostedFile uploadedfile = context.Request.Files[0];
                      filename = uploadedfile.FileName;
                      uploadedfile.SaveAs(mapPath + "\\" + filename);
                      context.Response.Write("{success:true, name:\"" + filename + "\", path:\"" + path + "/" + filename + "\"}");
                  }
                  catch (Exception)
                  {
                      context.Response.Write("{success:false}");
                  }
              }
              else
              {
                  //This work for Firefox and Chrome.
                  FileStream fileStream = new FileStream(mapPath + "\\" + filename, FileMode.OpenOrCreate);
                  try
                  {
                      Stream inputStream = HttpContext.Current.Request.InputStream;
                      inputStream.CopyTo(fileStream);
                      context.Response.Write("{success:true, name:\"" + filename + "\", path:\"" + path + "/" + filename + "\"}");
                  }
                  catch (Exception)
                  {
                      context.Response.Write("{success:false}");
                  }
                  finally
                  {
                      fileStream.Close();
                  }
              }
          }
      }

Complete solution is available in codeproject http://www.codeproject.com/KB/aspnet/AspNetHandlerAjaxUpload.aspx

Saturday, May 14, 2011

JQuery context menu items active/ deactivate for .NET TreeView

I was needed to give some functionality to .NET tree view for user so that user can add, edit, activate or deactivate node. Jquery context menu is very nice and give me functionality to customize context menu according to my requirement. My requirement was to make some items active based on image of treeview. .NET treeview use table structure. if the node contain is <td> wich id is ‘xyz_01’ then image <td> id will be ‘xyz_01i’ . Based on this I find the image element and according to source of image I have changed the active menu items on mousedown event. MouseDown event work before displaying jquery context menu. The code is given here.

Context menu div is

<ul id="myMenu" class="contextMenu"> 
<li class="copy"><a href="#add">Add</a></li>
<li class="edit"><a href="#edit">Edit</a></li>
<li class="inactive"><a href="#Inactive">Inactivate</a></li>
<li class="active"><a href="#Active">Activate</a></li>
<li class="quit separator"><a href="#cancel">Cancel</a></li>
</ul>



The mouse down code for activate and deactive element according to treeview image source.




$("#MyTreeDiv A").mousedown(function () { 
var itemId = $(this).attr('id');
var imageItemId = itemId + 'i';

var img = $('#' + imageItemId).children('img').first();
var s = $(img).attr('src');
if (s.indexOf('inactive') > -1) {
$('#myMenu').enableContextMenuItems('#Active');
$('#myMenu').disableContextMenuItems('#Inactive');
$('#myMenu').disableContextMenuItems('#add');
$('#myMenu').disableContextMenuItems('#edit');
}
else if (s.indexOf('active') > -1) {

$('#myMenu').disableContextMenuItems('#Active');
$('#myMenu').enableContextMenuItems('#Inactive');
$('#myMenu').enableContextMenuItems('#add');
$('#myMenu').enableContextMenuItems('#edit');
}

});

Tuesday, February 15, 2011

WPF: Textblock vertical alignment with given height

I was needed to set the alignment of textblock in center and also need to set height which is greater than normal textblock text height. But problem is when the text height is assigned then vertical alignment of textblock does not work. To solve this problem easy way to set padding so that the textblock text remain at center position. For that you need to measure the height of textblock before setting the custom height of textblock. After getting the desiredsize of textblock you can calculate the top padding of text in textblock. Then set the padding and custom height of textblock. This will solve the problem of making text position at center giving custom height.
textBlock.Measure(new Size(infiniteWidth, infiniteHeight));
var textBlockHeight = textBlock.DesiredSize.Height;
var textBlockTopPaddig = (CustomHeight - textBlockHeight)/2;
textBlock.Padding = new Thickness(0,textBlockTopPaddig,0,0);
textBlock.Height = CustomHeight;
 
XAML 
But if you want to set the custom height of TextBox from XAML then create a StackPanel and set the TextBlock inside that StackPanel. Instead of setting TextBlock height set the height of StackPanel and set StackPanel vertical alignment as VerticalAlignment.Center. So now textblock will be displayed in center of StackPanel. 

Friday, February 11, 2011

WPF : Getting Size of Text of a TextBlock or Richtext from code-behind without Rendering

We can not get actual size of a textblock without rendering. So for getting the size of TextBlock or RichText we need to call measure function of TextBlock / Richtext to get the the width and height which it would desired to occupy when it will render and you will get this from DesiredSize property after calling measure function.
TextBlock textBlock = new TextBlock();
textBlock.TextWrapping = TextWrapping.Wrap;
textBlock.Text = “Test data”;
textBlock.Measure(new Size(400, 500));
there the measure takes the size which is available size of container element. We can give it infinity size for measuring the size of text.
There also have another low-level .NET class for measuring the size of Text without creating TextBlock.And it is FormattedText class. Actually TextBlock internally use FormattedText for measuring the size of Text. For measuring text size we can create object of FormattedText like this.
string testString = "Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor";
                       // Create the initial formatted text string.
                       FormattedText formattedText = new FormattedText(
                           testString,
                           CultureInfo.GetCultureInfo("en-us"),
                           FlowDirection.LeftToRight,
                           new Typeface("Verdana"),
                           32,
                           Brushes.Black);
Here we have defined an object of FormattedText class which actually takes text as string, cultureinfo, direction, font type , size of text and media brush.
This return the same size as Measure function of text block.
But we can not measure the Rich text using FormattedText function as format of every Run (Paragraph) is different. So for that we need to extract the direction, font , size of text and brush for every run element. There have a nice article for getting FormattedText from FlowDocument of RichText object. http://www.wpfmentor.com/2009/01/how-to-transfer-rich-text-from.html.