Friday, July 30, 2010

Disabling an ASP.NET button when clicked

Hi all,

In one of my projects, I had an requirement where when a user browse a file via "FileUpload" control and hit the "Submit" button then button should be disabled. Moreover, I thought besides disabling the button I could display an image as well so that the user interface is more interactive. I have developed a custom user control that is hosted in an application page.

After "Googling" for this requirement, I found a couple sample but most of them used "HTML" input controls. But, I wanted an <asp:Button> control. Finally, I got the trick. When we double click on the "Submit" button, then an event handler has been generated with a "OnClick" server side code. We can leverage on "OnClientClick" property that executes a client side script. Besides this "OnClientClick" property we need to implement our own custom javascript that disables the button. UseSubmitBehavior="false" - Indicates that the button control should use the ASP.NET postback mechanism instead of the client browser's submit mechanism (UseSubmitBehavior property).

 <script type="text/javascript" language="javascript">
        function disableBtn(btnID, newText) {

            var btn = document.getElementById(btnID);
            btn.disabled = true;
            btn.value = newText;

            //Setting Image properties 
            btn.style.backgroundImage = 'url(/_layouts/images/projectName/ajax-loader.gif)';
            btn.style.backgroundRepeat = 'no-repeat';
            btn.style.backgroundPosition = 'center';

        }
    </script>
 
<asp:button id="btnOne" tabIndex="0" 
                    Runat="server" Text="Submit"
                    onclick="btnOne_Click" 
                    OnClientClick="disableBtn(this.id, 'Submitting...')" 
                    UseSubmitBehavior="false" /> 
 
This code works perfectly on Firefox but image loading part did not work. 
There is a very useful tip from  4guysfromrolla where page "Freeze" on the form submission. 

Cheers, 
--aaroh 

References: 
1) ASPAlliance  
2) 4GuysfromRolla


Sunday, July 18, 2010

Taking a crash course in LINQ - Part 1 (LINQ to SQL)


Hi All,

Last week one of my colleagues, showed me LINQ to SQL in our project. I was bit interested that how he had managed the data access layer so easily. Oh boy! after experimenting with LINQ to SQL classes and objects has just blew my mind. LINQ is probably the most revolutionary change in the .NET 3.5 framework besides CLR enhancements, ASP.NET three new controls, WPF (with XAML designer in VS 2008, VS 2010), WCF etc. Check out more on the .NET 3.5 framework. Any developer who wants to learn LINQ for ASP.NET should start off with Scott Guthrie.  

What is LINQ??

Language INtegrated Query (LINQ) a technology inside of ADO.NET built to query relational databases that could be collections, SQL Server databases, DataSets, XML files etc. C# and VB.NET has integration with LINQ and following benefits: 
  1. Full intellisense
  2. Strongly typed
  3. Compile-time checking
  4. Debugging support
  5. Dramatically ease data access and portability!  
For e.g. LINQ to SQL (this blog post), LINQ to DataSet, LINQ to XML, LINQ to SharePoint.
 
LINQ is highly extensible and therefore, we can query data sources beyond what Microsoft ships such as LINQ to Flicker, LINQ to Amazon (via Web Services), LINQ to Google etc.  

Visual Studio 2008/2010 provides "LINQ to SQL" template that provides an object relational model (O/RM) and we can use .NET classes to develop the data access layer. Besides querying the database, we can also CRUD (Create - Insert, Read - Select, Update , Delete) operations as well.

Getting Started with LINQ syntax

1) Standard Query Operator:

To start off our LINQ query, the first thing to be an acquaintance with C# 3.0 and add "System.LINQ", "System.Collections.Generic" in our C# program. Furthermore, IEnumerable interface has to be used which supports a simple iteration over a non-generic collection. Lets take an sample from MSDN that displays names where length of names array is exactly 5. 

---------------------------------------------------------------- 
using System;
using System.Linq;
using System.Collections.Generic;

class app {
  static void Main() {
    string[] names = { "Burke", "Connor", "Frank", 
                       "Everett", "Albert", "George", 
                       "Harris", "David" };

    IEnumerable<string> query = from s in names 
                               where s.Length == 5
                               orderby s
                               select s.ToUpper();

    foreach (string item in query)
      Console.WriteLine(item);
  }
}
-----------------------------------------------------------------------------

Output
BURKE
DAVID
FRANK
 
The "query" variable is in fact a called "query expression" that can have one or more data sources. It has similar to SQL. This expression uses three of the standard query operators: 
  • Where  
  • OrderBy
  • Select 

The arguments to the Where, OrderBy and Select operators are called "Lambda Expressions". 

Lets dissect lambda expressions further. When we develop a data layer via LINQ syntax, we will get a data context (will be discussed shortly in this blog).

In this data context, we can query a table such as "Products" where select ProductName equal to "Chai"

If we notice that parameter "predicate" is of type Func<Product,bool> and involves delegate which is old and tedious way of specification this function. Instead "Lambda Expression" leverage succinct way of achieving the same result follows:
"=>" operator is divided in two sections:
a) The left hand side("c" in above example) defines the parameters and types are usually implied.
b) The right hand side uses the parameters and evaluates to the  defined return type.

Lambda expression is strongly typed and VS 2008/2010 intellisense in great deal.

2) More Standard Query Operator:

There an ensemble of operators that provide useful ways of manipulating sequences and composing queries. These are the most used operators:

a) Sorting and Grouping (OrderBy, OrderByDescending, ThenBy, ThenByDescending)
b) Aggregation operators (Aggregate):
They are defined for aggregating a sequence of values into a single value
c) Count operator (Count)
d) Four numeric aggregation operators (Max, Min, Sum and Average

Let's work on an LINQ to SQL project in ASP.NET. There are pre-requisites:
Sample Project:

Step1:  VS 2008/2010 project

Create a new Visual Studio 2008/2010 project, File >> New >> Project >> Select "ASP.NET Web Application"

 



Step2: Add a GridView 
Once the project has been added on to VS 2008/2010, we open the "Toolbox" and drag/drop "GridView" control on the "Default.aspx"

Step3: Create a data access layer via LINQ

a) To connect to database, we go to "Tools" >> Connect to Database >>  Connection to Database and fill in the server name and the database nameas illustrated below:


We can view the database connections via "Server Explorer" and it will display tables, stored procedure, View etc. 

b) Right-click on "LINQtoSQLWebApp" project >> Add New Item >> Select "LINQ to SQL Classes" and in the name, type in "Northwind"

 
Obviously, there will be not be any class in the DBML designer surface.




We drag Product, Category, Order and Order_Detail tables to the DBML designer surface and we are done with data access layer/Data model. Each of the table has been generated as .NET object with associated properties such as ProductID, ProductName etc. When we drag the Tables from Northwind database, object mapping is done automatically.
 

Step4: Query the data via LINQ

Just for the demonstration purpose, we will query all the rows the product class via LINQ:

-------------------
NorthwindDataContext db = new NorthwindDataContext();

        var query = from p in db.Products
                         select p;

-------------------

Output:
 
What happened is that when we use LINQ query syntax against data model, LINQ to SQL automatically take this query syntax and transform into regular relational SQL expressions, query run against the database, database returns the content and LINQ to SQL constructs classes such as Product class that returns the data. The best part of LINQ syntax is we get expressive and readable constructs. LINQ is strongly typed as well. 

We can use "Where" operator to query just the "Beverages".


-------------------

NorthwindDataContext db = new NorthwindDataContext();

        var query = from p in db.Products
                         where p.Category.CategoryName == "Beverages"
                         select p;


-------------------

Output: 



We can do some further querying using powerful LINQ constructs. For e.g. in the where clause "CategoryName", we can filter category name by using LINQ operators such as count, Max, Min, Average, Spilt, ToLower, ToUpper, Sum, Remove, Replace, StartsWith etc.


-------------------
NorthwindDataContext db = new NorthwindDataContext();

var query = from p in db.Products
                    where p.Category.CategoryName.StartsWith("c")
                    select p;
--------------------

Output:


Many times, we do not need all the columns to be returned in the GridView. It can be done in LINQ as well:

-------------------
NorthwindDataContext db = new NorthwindDataContext();

var query = from p in db.Products
                    where p.Category.Products.Count > 5
                    select new
                        {
                            p.ProductID,
                            p.ProductName

                        };
-------------------

Output: 



We can use sum, count, average of LINQ operators to generate more powerful query. 


-------------------
NorthwindDataContext db = new NorthwindDataContext();

 var query = from p in db.Products
                    where p.Category.Products.Count > 5
                    select new
                        {
                            p.ProductID,
                            p.ProductName,
                            TotalUnits = p.Order_Details.Sum(o => o.Quantity),
                            Revenue = p.Order_Details.Sum(o => o.Quantity * o.UnitPrice)

                        };
---------------------


Output:



Creating Server side paging of our Query Results
One of the common needs in web scenarios is to be able to efficiently build data paging UI.  LINQ provides built-in support for two extension methods that make this both easy and efficient - the Skip() and Take() methods.

We can use the Skip() and Take() methods below to indicate that we only want to return 10 product objects - starting at an initial product row that we specify as a parameter argument. 


-------------------
NorthwindDataContext db = new NorthwindDataContext();

 var query = from p in db.Products
                    where p.Category.Products.Count > 5
                    select new
                        {
                            p.ProductID,
                            p.ProductName,
                            TotalUnits = p.Order_Details.Sum(o => o.Quantity),
                            Revenue = p.Order_Details.Sum(o => o.Quantity * o.UnitPrice)

                        };
GridView1.DataSource = query.Skip(20).Take(10);
 GridView1.DataBind();
---------------------

Output:




We have just developed a mapping objects to relational data with the Visual Studio designer tool that automated code generators which help us to create the O/RM map objects.


Besides Visual Studio designer tool that are two more options that generate O/RM map objects


  • SQLMetal.exe - A command line tool that generate both .DBML files and O/RM code for a given database. Its excellent for larger databases. 
  • Code Editor - Connecting classes to database objects using code.
   
Download the source here. 
References: 

1) Video by Scott Guthrie - (Part 1, Part 2 , Part 3)
2) Complete tutorials series of LINQ to SQL by Scott Guthrie

Saturday, July 10, 2010

How to: Display SharePoint groups and users in a custom application page.

Hi all,

In one of my projects, I had to develop an application page which display all SharePoint groups & users for a specific site collection and all the users even with visitor role should be able to view this page. Out-of-the-box, SharePoint 2007 does not provide this page. Therefore, I developed a solution to facilitate users.

Prerequisite:
Publishing features (site and web) level must be enabled.

What has to be developed:
  1. An custom application page 
  2. A user control and application page will host this user control.
Step1: Open Visual Studio 2008 and create a WSPBuilder and name it: DisplayGroupsUsers 
Step2: Create an application page named "DisplayGroupsUsers.aspx". (Refer to my earlier blog post)

Custom Application Page: 
-----------------------------------------------------------------------------------------
<%@ Page Language="C#" MasterPageFile="~masterurl/default.master" %>

<%--Control Place Holder--%>
<asp:Content ID="PageTitle" runat="server" contentplaceholderid="PlaceHolderPageTitle" >
   View Group Membership
</asp:Content>

<asp:Content ID="PageTitleInTitleArea" runat="server"
             contentplaceholderid="PlaceHolderPageTitleInTitleArea" >
   View Group Membership
</asp:Content>
<asp:Content ID="Main" runat="server" ContentPlaceHolderID="PlaceHolderMain">
    <div>

</div>
 
</asp:Content>
---------------------------------------------------------------------------------------------

Step3: Create a user control named: "COM_DisplayGroupsUsers.ascx": (Refer to my earlier blog post)
Step4: In this user control, we will display SharePoint groups and users in a ASP.NET TreeView control.
For a user interface for the user control, we can add some text and drag drop the TreeView control from the "Toolbox" as follows:

COM_DisplayGroupsUsers.ascx: 


---------------------------------------------------------------------------------------------------------------------
<%@ Control Language="C#" AutoEventWireup="true" CodeBehind="COM_DisplayGroupsUsers.ascx.cs" Inherits="DisplayGroupsUsers.COM_DisplayGroupsUsers,  DisplayGroupsUsers, Version=1.0.0.0, Culture=neutral, PublicKeyToken=f006780d75630210" %>
<p>
    This page displays all the SharePoint groups and users for the site.
</p>

<p>
    <asp:TreeView ID="treeView" runat="server" ImageSet="Contacts" NodeIndent="10"
        Width="250px">
        <ParentNodeStyle Font-Bold="True" ForeColor="#5555DD" />
        <HoverNodeStyle Font-Underline="False" />
        <SelectedNodeStyle Font-Underline="True" HorizontalPadding="0px"
            VerticalPadding="0px" />
        <NodeStyle Font-Names="Verdana" Font-Size="8pt" ForeColor="Black"
            HorizontalPadding="5px" NodeSpacing="0px" VerticalPadding="0px" />
    </asp:TreeView>
</p>
 ----------------------------------------------------------------------------------------------------------

 For the user control code, we have to supply the delegate (SPSecurity.RunWithElevatedPrivileges) so that all users can view all SharePoint groups and users. Once its done,we have to manipulate Groups and users in a treeview.

---------------------------------------------------------------------------------------------------------
COM_DisplayGroupsUsers.ascx.cs: 
using System;
using System.Collections;
using System.Configuration;
using System.Data;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.HtmlControls;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using Microsoft.SharePoint;


namespace DisplayGroupsUsers
{
    public partial class COM_DisplayGroupsUsers : System.Web.UI.UserControl
    {
        protected void Page_Load(object sender, EventArgs e)
        {
            if (!IsPostBack)
            {
                  SPSecurity.RunWithElevatedPrivileges(delegate
                                                           {

                                                               //Get the current site
                                                               using (SPSite site = new SPSite(SPContext.Current.Site.ID))
                                                               {
                                                                   using (SPWeb currentWeb = site.OpenWeb())
                                                                   {
                                                                       // set the tree view properties
                                                                       treeView.ShowLines = true; //show lines
                                                                       treeView.ExpandDepth = 1; //expand level one in the treeview
                                                                       
                                                                       //get all the SharePoint groups within current site
                                                                       SPGroupCollection groupCollection =
                                                                           currentWeb.Groups;

                                                                       //loop all SharePoint groups
                                                                       foreach (SPGroup group in groupCollection)
                                                                       {
                                                                           //get all the SharePoint users within a specified group
                                                                           SPUserCollection userCollection = group.Users;

                                                                           //build the tree
                                                                           TreeNode rootNode = new TreeNode(group.Name,
                                                                                                            "",
                                                                                                            "~/_layouts/images/GroupsUsers/Groups.gif",
                                                                                                            "", "");
                                                                           treeView.Nodes.Add(rootNode);

                                                                           //loop the users in a specific SharePoint group
                                                                           foreach (SPUser user in userCollection)
                                                                           {
                                                                               //add a new node for the current user
                                                                               TreeNode newNode = new TreeNode(
                                                                                   user.Name, "",
                                                                                   "~/_layouts/images/GroupsUsers/User.gif",
                                                                                   "", "");
                                                                               rootNode.ChildNodes.Add(newNode);

                                                                           }

                                                                       }

                                                                   }
                                                               }

                                                           });
               
            }

        }
    }

------------------------------------------------------------------------------------------------

Step5: In the DisplayGroupsUser.aspx page, we need to host the user control as follows:

 DisplayGroupsUsers.aspx with user control: 

-------------------------------------------------------------------------------------------------- 
<%@ Page Language="C#" MasterPageFile="~masterurl/default.master" %>

<%@ Register src="~/_controltemplates/COM_DisplayGroupsUsers.ascx" tagname="COM_DisplayGroupsUsers" tagprefix="wssuc" %>

<%--Control Place Holder--%>
<asp:Content ID="PageTitle" runat="server" contentplaceholderid="PlaceHolderPageTitle" >
   View Group Membership
</asp:Content>

<asp:Content ID="PageTitleInTitleArea" runat="server"
             contentplaceholderid="PlaceHolderPageTitleInTitleArea" >
   View Group Membership
</asp:Content>
<asp:Content ID="Main" runat="server" ContentPlaceHolderID="PlaceHolderMain">
    <div>
<wssuc:COM_DisplayGroupsUsers ID="COM_DisplayGroupsUsers" runat="server" />

</div>
 
</asp:Content>

--------------------------------------------------------------------------------------------

The final project structure will look like this:

Step6: Build and deploy the WSP. Go to Site Actions >> Check if both the publishing features have been activated >> Once verified, Activate our groups and users feature >> Navigate to the "Pages" document library >> Upload the "DisplayGroupsUsers.aspx" page which is located at 12 hive >> Templates >> Features >> DisplayGroupsUsers folder. The final custom application page will look this:


 

Cheers,
--aaroh

Download the source here.

Reference: Creating a Tree View Control WebPart

How to: Use cascading drop-down lists in PowerApps

Hi all, Using cascading dropdown, users can easily fill the forms by selecting drop-down values dependent on values from another dro...