Pages

Sunday, March 24, 2013

Creating Custom HTML Helpers - ASP.NET MVC 4

What are HTML Helpers in ASP.NET MVC?

HTML Helpers are nothing but the way of rendering HTML on the view page in ASP.NET MVC. Typically what we have in traditional ASP.NET web forms is Web Controls to achieve same functionality but with the evolution of MVC pattern , you don't have any web controls to add on to the view pages.
In simple words - it is just a method which returns you a string , and string is HTML.

Example of HTML Helpers:

ASP.NET MVC framework ships with various inbuilt HTML helpers such as ActionLink , Label.
Lets take a look at how a HTML helper is added to the view page. I am considering Razor view engine for this example.


@Html.ActionLink("Display Name of Link", "MethodName", "ControllerName");

Example above shows how OOB Action Link is used to render the HTML hyperlink on the view page.

Another example is shown as below where it is used to render the Html label tag to render Store Name property of model class. 

@Html.LabelFor(model => model.StoreName)

 Why to use Html Helpers?

Question might have poped up in your mind by now that Html helpers are just going to render the string of html then why do I need to use them ? If I know the Html syntax then why not add the Html directly on to the view page? 
Answer is simple, it depends how clean and consistent you want to make your application. of course you can do the direct addition of html tags on your view pages but if you observe second example - you can see that it is simply rendering the label tag on view page for a property in model at run time. so it just for making developer's life more easy and to have the cleaner html for your view page.

Need of Custom Html Helpers?

Well, there is always need to do some custom stuff on top of what framework offers, and reason for doing this is either business requirement or to get things done in smarter way.

Writing a custom Html helper is nothing but writing an extension method. you are actually writing an extension method for HtmlHelper class.
Being a SharePoint developer I always find this task similar to creation of a web control where you override the render method of base class and do the custom Html rendering. 

Creating Custom Html Helper

All right , for demo purpose I will keep the example simple - I will simply write an Html helper which will render the header of any content on view page.
This is done simply by using the <header> tag of Html 5.

Step 1: Define your custom static class for creation of your custom Html helpers

public static class DemoCustomHtmlHelpers
{

}

Step 2: Add static method to the class and make sure that return type is MvcHtmlString. Write an input paramter of method as HtmlHelper and also a string for which the header tag needs to be generated.
You might need to add System.Web.Mvc namespace to your class.

public static MvcHtmlString Header(this HtmlHelper helper, string content)
{

          
}

Step 3 : Add the rendering logic in the method , you can take help from TagBuilder class to generate Html tags to be rendered.

public static MvcHtmlString Header(this HtmlHelper helper, string content)
{
   var tagBuilder = new TagBuilder("header");
   tagBuilder.InnerHtml = content;
   return new MvcHtmlString(tagBuilder.ToString());
}


Step 4: Build the project and open any of the view page. Now you can use your custom Html helper to render the header. Make sure that your view page has correct using namespace for using your custom Html helper extension.


@Html.Header("Create")


When you observe the view page's Html , you will be finding the generated Html tag by our Custom Html helper extension

<header>Create</header>


So this is all guys , have fun and try to create these Html Helper extensions in your scenarios.



Saturday, February 23, 2013

Passing Parameters to Events - C#

All right , again one post away from my favorite SharePoint world but this is kind of generic which can be used anywhere in C#.

Today we will see how we can pass some custom parameters to any event in C#.

Consider a scenario where you are calling some method asynchronously and execution is happening in loops.
In General, we register an event with the method which gets called after method call is completed. but as the asynchronous execution is happening in the loop , so in the on completed event you never know which for which call the execution has been completed.

Check out this sample scenario where I have a simple windows forms application with two buttons and label, both buttons are registered to a same on click event. Now say, on click of each button I want to perform some independent actions and also want to pass some parameters to the on click event. I know there can be many approaches to do this but I have just taken this example to prove the actual point of this blog post.


public Form1()
{
   InitializeComponent();

   button1.Click += new EventHandler(button_Click);
   button2.Click += new EventHandler(button_Click);
}

void button_Click(object sender, EventArgs e)
{
   label1.Text = "Hi ";
}

Now on both button clicks - the text of a label is printed as 'Hi' as the event is common between both buttons.

Now what I want is to pass some extra information to the on click event from both buttons , you can pass on any number and type of  objects as you want. for the example I will send a string and Enum to click events.

I will also need to modify the button click event , as I am going to send some parameters to event so I need to catch those in the event.

public Form1()
{
  InitializeComponent();

  button1.Click += delegate(object sender, EventArgs e) { button_Click(sender, e, "This is   From Button1", MessageType.B1); };

  button2.Click += delegate(object sender, EventArgs e) { button_Click(sender, e, "This is From Button2", MessageType.B2); };

}

void button_Click(object sender, EventArgs e, string message, MessageType type)
{
   if (type.Equals(MessageType.B1))
   {
      label1.Text = message;
   }
   else if (type.Equals(MessageType.B2))
   {
      label1.Text = message;
   }
}

enum MessageType
{
   B1,
   B2
}


Friday, February 22, 2013

Creating Custom Workflow Activity - Windows Workflow Foundation 4.0


It’s been long since I written new blog post , but today got some time to write one on which I was working recently.
This time it’s little away from SharePoint world, just to stroll Windows Workflow Foundation 4.0. The time I am writing this post, Bollywood music is running over my ears so I am really in mood of writing J

Workflow Foundation

Microsoft has provided us a very strong foundation since release of .NET Framework 3.5 known as Windows Workflow Foundation (WF), and it is getting much better with every release. The latest one is WF 4.0 and is really useful in mapping your business processes to workflows. The basic idea behind any workflow creation is to automate the business processes and do it more declarative way.

Need of Custom Activities

Though the framework provides you lots of inbuilt activities so that you can grab them and directly use in your workflow application as it is, but again in most of the cases business scenarios are complex and requirement cannot be solved directly using out of the box activities. So in such cases what needs to be done? Nothing worry, similar to SharePoint workflows – windows workflow foundation also supports creating custom activities. (Awkward to say but WF seems much much stronger when compared to SharePoint workflow engine). You can also provide input parameters to your activity and activity can return you output which can be later used in the workflow.

Example

Ok enough of theory and let’s get in to the action.
Intentionally I have taken very simple example for demo, it simply uses a custom activity and workflow simply adds a new product in the products database and finishes. Nothing much but just to understand the creation and execution of custom activity.
I will start by creating a project in Visual Studio 2012
Open up visual studio and click on create new project. From left side of new project window – click on workflow category and select Workflow Console Application project.
You will see a Workflow.xaml opened up in the editor. This is basically playground for you where you can start designing you workflow.
If you take a look at left side of the window , you will see something familiar as Toolbox (remember ASP.NET web applications? ) and here is the place where you will find all out of the box activities provided by WF 4.0
I have designed a very simple workflow which prints a line to the windows when workflow starts and do some execution of activities and then again prints line to window when workflow finishes.



Now wait a minute, did you observe something called as AddProduct there in the editor as well as in the toolbox? From where on the earth that came?
Nothing suspicious going on here so don’t worry, this is the custom activity which I created which simply adds a new product in the products inventory. I won’t go in much details of the how I have created the database and used it using the entity framework but we will concentrate more on creating that custom activity and passing parameters to it.

Implementation

Custom activities in the windows workflow foundation are nothing but the class which is derived from a class known as ‘CodeActivity’.
Once you create your own class and derive it from CodeActivity , you will need to override the execute method of the parent class. This method gives you the workflow execution context so that you can play around input and output parameters.
Following code is just for understanding purpose on how we can create custom activity, you can add any complex logic in the execute method.
So here is the AddProduct activity code



public class AddProduct : CodeActivity
{
  public InArgument<int> ProductId { get; set; }
  public InArgument<string> ProductName { get; set; }
  public InArgument<string> ProductDescription { get; set; }
  public InArgument<int> ProductPrice { get; set; }
  public InArgument<int> ProductDiscount { get; set; }
  public OutArgument<string> Result { get; set; }

  protected override void Execute(CodeActivityContext context)
  {
    try
    {
      using (var productEntities = new ProductsEntities())
      {
        productEntities.ProductInfoes.Add(new ProductInfo
        {
           ProductId = ProductId.Get(context),
           ProductName = ProductName.Get(context),
           ProductDescription = ProductDescription.Get(context),
           ProductPrice = ProductPrice.Get(context),
           ProductDiscount = ProductDiscount.Get(context)
         });
         productEntities.SaveChanges();
        }
              
    context.SetValue(Result, "Product with Id : " + context.GetValue(ProductId) + " Added Successfully!!");
     }
     catch (Exception ex)
     {
       context.SetValue(Result, "Error While Adding Product : " + ex.Message);
     }

   }
  }

As you can see that this activity is having 5 input parameters and one output parameter which is later consumed by workflow write line activity (at end of workflow) to display the message to users.
Once you are creating the class and done with implementation of execute method, then just rebuild your solution and observe the toolbox. Framework automatically adds the custom activity for you in the toolbox.
Now question is how to pass parameters to the activity?
Once the activity is available in the toolbox, simply drag and drop to the workflow. Right click on activity and go to the properties window.
Here you will need to provide the input parameters and also if you are returning something from your custom activity then you will need to catch that output to some workflow variable.



I am passing some hardcoded values as parameters to the activity but this can be made dynamic by passing in workflow arguments.
Result is nothing but the output message which our activity is producing from execute method. I have simply assigned that result to the workflow variable called as varResult , so that I will be able to use it in workflow context.
That’s all guys, I hope now you know how to create custom activities in Windows Workflow Foundation and use them to make your life easier. Hope this helps someone.

Tuesday, October 16, 2012

Creating Custom Workflow Activity for Attachments

First of all let me declare , this is not one of the latest thing you can do with SharePoint but I thought this might be helpful (at least to me)
If you have developed any list workflow using SharePoint designer , you probably have noticed that there is no way to work with list item attachments. to overcome such scenarios you have to create your own workflow activity and deploy it on server. in Office365 case , you have Sandbox solution capabilities using which you can achieve the same.

In this case , I have tried to create a custom workflow activity which reads all attachments of the list item on which workflow is running and saves them to mentioned document library. while saving , it creates a folder whose name is a combination of list item's Id and Title and then saves attachments into it. I have create this as a Sandbox solution so that anyone can easily deploy this wsp package on their Office365 Site and start using it.



and custom action can be used like



To create your custom workflow actions basically three things are needed.
  • Activity Action Class - This is the class where you will write your custom logic which will be executed when activity occurs in workflow.
  • Elements.xml - You will need to use <WorkflowActions> Element in order to deploy your custom action to SharePoint.
  • Feature - finally , a Site Collection scoped feature which will contain elements.xml you created to deploy your action to SharePoint.


So lets start creating a custom workflow activity for above mentioned case.

I have created a simple SharePoint 2010 empty solution using Visual Studio 2010 SharePoint project template.

Now lets add the Activity action class. Note that SaveItemAttachments method will be executed whenever our custom activity will be called from SPD workflow. this method takes a string method argument which is passed from SPD , as a document library name.



public class AttachmentActions
{
   public List<SPFile> filesCollection = null;
   public string tdocLib = string.Empty;
   SPUserCodeWorkflowContext tempcontext = null;

   public Hashtable SaveItemAttachments(SPUserCodeWorkflowContext context, string targetDocumentLibrary)
   {
     Hashtable results = new Hashtable();
     results["Exception"] = string.Empty;
     tdocLib = targetDocumentLibrary;
     tempcontext = context;
     try
     {
       //Get SiteCollection
       using (SPSite site = new SPSite(context.CurrentWebUrl))
       {
         //Get Web
         using (SPWeb web = site.OpenWeb())
         {
           //Get List
           SPList list = web.Lists.GetList(context.ListId, false);
           if (list != null)
           {
            //Access List Item
            SPListItem listItem = list.GetItemById(context.ItemId);
            if (listItem != null)
            {
             //Check for attachments
             if (listItem.Attachments != null && listItem.Attachments.Count > 0)
             {
              //Get All Attachments
              filesCollection = InitializeFilesCollection(web, listItem);
              if (filesCollection != null)
              {
                //Get Target Document Library
                SPList targetLibrary = web.GetList(targetDocumentLibrary);
                if (targetLibrary != null)
                {
                  //Upload attachment to document library
                  foreach (SPFile attachmentFile in filesCollection)
                  {
                    byte[] fileContents = GetFileContents(attachmentFile);
                    if (fileContents != null)
                    {
                      string folderName = string.Format("{0}.{1}", listItem.ID, listItem.Title);
                      string attachmentName = attachmentFile.Name;
                      //Create Folder in document library
                      SPFolder foundFolder = CreateFolder(folderName, targetLibrary, targetLibrary.RootFolder.SubFolders);
                      if (foundFolder != null)
                      {
                        //Add file to created folder
                        SPFile addedFile = foundFolder.Files.Add(string.Format("{0}/{1}", foundFolder.Url, attachmentName), fileContents, true);
                        foundFolder.Update();
                        }
                      }
                    }
                    targetLibrary.Update();
                    results["Status"] = "Success";
                 }
                }
               }
              }
            }
           }
         }
        }
         catch (Exception ex)
        {
          results["Exception"] = ex.ToString();
        }

        return (results);
       }

        public List<SPFile> InitializeFilesCollection(SPWeb web, SPListItem listItem)
        {
          SPAttachmentCollection allAttachments = listItem.Attachments;
          string attachmentUrlPrefix = allAttachments.UrlPrefix;

          if (allAttachments != null && allAttachments.Count > 0)
          {
            filesCollection = new List<SPFile>();
            foreach (string attachmentName in allAttachments)
            {
              string attachmentPath = string.Format("{0}{1}", attachmentUrlPrefix, attachmentName);
              SPFile attachmentFile = web.GetFile(attachmentPath);
              if (attachmentFile != null)
              {
                 filesCollection.Add(attachmentFile);
              }
            }
          }

            return filesCollection;
        }

        public SPFolder CreateFolder(string folderName, SPList docLib, SPFolderCollection subFolders)
        {
          SPFolder foundFolder = null;
          if (subFolders.Count > 0)
          {
            foreach (SPFolder folder in subFolders)
            {
              if (folder != null && !string.IsNullOrEmpty(folder.Name))
              {
                if (folder.Name.Equals(folderName))
                {
                  foundFolder = folder;
                    break;
                  }
                }
              }
           //If no matching folder found - create new folder in document library root folder.
              if (foundFolder == null)
              {
                if (!docLib.EnableFolderCreation)
                {
                  docLib.EnableFolderCreation = true;
                  docLib.Update();
                }
                foundFolder = subFolders.Add("/" + tdocLib + "/" + folderName);
                docLib.Update();
              }
            }
            return foundFolder;
        }

        public byte[] GetFileContents(SPFile file)
        {
            return file.OpenBinary();
        }

    }



Now after this , let SharePoint know that what to execute whenever action will be called. This can be done by writing a elements file. SharePoint gives you a way to deploy such actions using <WorkflowActions> element where you can specify number of parameters.

Add empty element to the solution and add following xml in it.



<Elements xmlns="http://schemas.microsoft.com/sharepoint/">
  <WorkflowActions>

    <!--Save Attachments Action-->
    <Action Name="Move Attachments to library"
        SandboxedFunction="true"
        Assembly="$SharePoint.Project.AssemblyFullName$"
        ClassName="SharePoint.Sandbox.WorkflowActions.ActivityClasses.AttachmentActions"
        FunctionName="SaveItemAttachments"
        AppliesTo="all"
        UsesCurrentItem="true"
        Category="Custom Actions">
      <RuleDesigner Sentence="Copy Attachments to library %1">
        <FieldBind Id="1" Field="targetDocumentLibrary" DesignerType="Text" Text="library name"/>
      </RuleDesigner>
      <Parameters>
        <Parameter Name="__Context" Type="Microsoft.SharePoint.WorkflowActions.WorkflowContext, Microsoft.SharePoint.WorkflowActions" Direction="In" DesignerType="Hide" />
        <Parameter Name="targetDocumentLibrary" Type="System.String, mscorlib" Direction="In" DesignerType="TextBox" />      
      </Parameters>
    </Action>
   
  </WorkflowActions>
</Elements>


If you observe the <RuleDesigner> tag , there you can specify the actual sentence which will be seen in the SharePoint designer after adding this action. %1 is the input parameter for taking the document library name from the end user. this input is provided to the custom activity executing method.

As you added empty element in the solution , visual studio by default adds a feature to hold this elements.xml. Make sure that the scope of the feature is Site. 

After this you are ready to deploy this solution to your environment.

After deployment , you can create workflow using this activity and check the result.

Above example shows , after workflow completion attachments were saved to document library.



You can download the source code and wsp here.