Wednesday, June 10, 2009

How to write Server-side ( C# ) code in SharePoint custom pages

There are basically two approaches to write server side code:
A) Inline coding ( C# and HTML code in same ASPX page)
B) Code-Behind (C# and HTML code in seperate files)

A) Inline coding

1) In this approach, first you have to modify application's web.cofig file.
Locate PageParserPaths node in config file and then change it as per your requirement.

If you doesn't know page(s) name which is(are) using server side code, then write /* in VirtualParth attribute,
else write page name as /pagename.aspx in VirtualParth attribute.
[For each page add a seperate PageParserPath node,
for example you want to allow server side code in 5 pages then you have to add PageParserPath nodes.]



<PageParserPaths>
<PageParserPath VirtualPath="/*" CompilationMode="Always"
AllowServerSideScript="true" />
</PageParserPaths>




2) Write server side code as


<script runat="server">

protected void Page_Load(Object sender, EventArgs e)
{
Response.Write("Server code");
}

</script>



B) Code-Behind approach

1) Create a C# file (or VB file) for your server code


using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace MyNameSPace
{
public class MyClass : Microsoft.SharePoint.WebControls.LayoutsPageBase
{
//Server control declarations
protected Label lblMessage;

protected void Page_Load(Object sender, EventArgs e)
{
lblMessage.Text = "Server Code";
}
}
}



Points to remember:
- Inherits the class with LayoutsPageBase (there are other options also available for choosing base class)
- Declare ASP.Net server control with protected modifier.



2) Register the DLL (that contains above class) in GAC.

3) safecontrol entry in application's web.config file.

4) Create ASPX file for HTML code


<%@ Assembly Name="MyNameSPace, Version=1.0.0.0, Culture=neutral,
PublicKeyToken=bf118dafb4e11f4b" %>
<%@ Page Language="C#" MasterPageFile="~masterurl/default.master"
Inherits="MyNameSPace.MyClass" %>

<asp:Content ID="Content1" ContentPlaceHolderID="PlaceHolderMain" runat="server">
<asp:Label runat="server" ID="lblMessage" ForeColor="Red"
Visible="false"></asp:Label>
</asp:Content>



Points to remember:
- Specify assembly reference of class created in step 1 (with proper Name, Version, Culture and PublicKeyToken)
- In case of Master-Content page scenario specify MasterPageFile attribute in Page directive
- Specify Inherits attribute in Page directive with proper NameSpace and Class name (e.g. Inherits="MyNameSPace.MyClass" )
- Write ASP.Net server control(s) that we have declared in Step 1 (with protected modifier)


Pros n Cons

Inline
- Easy to deploy, no need to do DLL registration in GAC, IISRESET and safecontrol entry in web.config
- It is unsafe, to allow server side code by modifying PageParserPaths node in web.cofig
- Can't debug the page [Major disadvantage while development].
- In case of any change(s) in code (HTML + C#), replace the existing ASPX file with modified one in SharePoint Designer.


Code-Behind
- Can debug server side code ( Debug -> Attach to Process -> w3wp.exe process )
- Need to do DLL registration in GAC, IISRESET and safecontrol entry in web.config
- It's safer to use compare to inline code
- In case of any change(s) in HTML code, replace the existing ASPX file with modified one in SharePoint Designer.
- In case of any change(s) in server-side ( C# ) code, re-register the DLL in GAC (drag-n-drop dll in assembly folder) and do IISRESET [ Must ] to see the change(s).

Tuesday, May 12, 2009

ASP.Net Tips n Tricks

1) Get the full file path (with name) in ASP.Net's FileUpload control.

FileUpload1.PostedFile.FileName returns full file path (including file name),
FileUpload1.FileName returns only file name.

2) Password Validation

Following code snippet display password validation at Client side (JavaScript).
Here password must contains Non-AlphaNumeric character(s) and length should be greater than 6.


<asp:TextBox ID="txtPassword" runat="server" Width="270px"
Enabled="true"></asp:TextBox>
<asp:CustomValidator ID="valPasswd" runat="server"
ControlToValidate="txtPassword" ClientValidationFunction="validatePassword"
Display="Dynamic"
ErrorMessage="* Inadequate password strength."></asp:CustomValidator>

<script type="text/javascript">

function validatePassword(oSrc,args)
{
var isValid = false;

var regExp = /\W/; // /[^a-zA-Z0-9]/;
if( (regExp.test(args.Value)) && (args.Value.length > 6) )
isValid = true;

args.IsValid = isValid;
}

</script>



3) How to get GridView's RowIndex (current row index) in GridView's RowCommand event:

There are two ways to find rowindex, as per me second is comparatively good one
because we need not to do control.Parent.Parent




int rowIndex
= ((GridViewRow)((Control)e.CommandSource).Parent.Parent).RowIndex;

int rowIndex
= ((GridViewRow)((Control)e.CommandSource).NamingContainer).RowIndex;




4) How to search a particular object in a Generic List:

Consider we have a generic list that holds an organization's employees objects,
and we find to a particular employee object based on some input like employee ID.
Here is the code:



List employees = ""; //Logic to fill employees list.
//Search a particular employee based on employee code
//[Here EmployeeCode is one of the variable or properties in Employee class]
Employee employee = employees.Find(delegate(Employee emp)
{ return emp.EmployeeCode == inputValue; });





5) How to find control(s) in ASP.Net GridView's EmptyDataTemplate?

If we are using ASP.Net GridView for displaying records, then to display GridView with no record we have to use EmptyDataTemplate. Inside that we will write all our required controls.

Consider a case... in which EmptyDataTemplate contains a textbox and a button.
If we click button, then GridView's OnRowCommand event will fire.

In GridView_OnRowCommand method to get textbox control we can follow one of the following approaches:


//Approach 1
TextBox txt
= (TextBox)gridviewSP.Controls[0].Controls[0].Controls[0].Controls[1];

//Approach 2
GridViewRow row
= (GridViewRow)((ImageButton)e.CommandSource).NamingContainer;
TextBox txt = row.FindControl("txtName");


As per me second approach is good one, because we need to do blind control search.

In second approach first we are getting GridViewRow, that contains textbox and button. Then in that GridViewRow, with FindControl function find our required textbox.


6) ASP.Net's GridView sorting logic




//C# code

//Call to get GridView's SortOrder and SortExpression...
string sortExpression = e.SortExpression;
bool sorting = GetSortDirection(sortExpression) == "ASC" ? true : false;
.
.


.
.
/// <summary>
/// Gets sort direction for gridview sorting
/// </summary>
/// <param name="column"></param>
/// <returns></returns>
string GetSortDirection(string column)
{
// By default, set the sort direction to ascending.
string sortDirection = "ASC";

// Retrieve the last column that was sorted.
string sortExpression = ViewState["SortExpression"] as string;

if (sortExpression != null)
{
// Check if the same column is being sorted.
// Otherwise, the default value can be returned.
if (sortExpression == column)
{
string lastDirection = ViewState["SortDirection"] as string;
if ((lastDirection != null) && (lastDirection == "ASC"))
{
sortDirection = "DESC";
}
}
}

// Save new values in ViewState.
ViewState["SortDirection"] = sortDirection;
ViewState["SortExpression"] = column;

return sortDirection;
}




7) Operation related to ASP.Net's TextBox with TextMode set to Password :
If we set the TextMode property of ASP:TextBox to Password, then we can't directly access the Text property of TextBox after page postback. Its a known issue and one of the solution is to use Attributes of textbox. We can set the TextBox's Attributes in TextBox_PreRender event and consume its value anywhere we want by using the stored value in Attributes.



<!-- HTML code -->
<asp:TextBox ID="TextBoxPassword" OnPreRender="TextBoxPassword_PreRender"
TextMode="Password" runat="server" Width="150px"></asp:TextBox>


//C# code

protected void TextBoxPassword_PreRender(object sender, EventArgs e)
{
TextBoxPassword.Attributes.Add("value", TextBoxPassword.Text);
}

protected void ButtonAddUser_Click(object sender, EventArgs e)
{
String password = TextBoxPassword.Attributes["value"];
}





8) Operation related to ASP.Net's GridView and DataTable :
This section covers DataTable and its usage in ASP:GridView, especially the Primary Key column and Find functionality of DataTable. [Code are self-explainatory with comments]



//C# code

protected void Page_Load(object sender, EventArgs e)
{
if (!Page.IsPostBack)
{
//Get the DataTable instance and store it
//in ViewState to be used later on.
if (ViewState["EmpDataTable"] == null)
{
ViewState["EmpDataTable"] = CreateDataTable();
}

dataTableEmps = (DataTable)ViewState["EmpDataTable"];

//Set the DataSource and Bind the Grid
GridViewEmps.DataSource = dataTableEmps;
GridViewEmps.DataBind();
}
}


//Create and return a DataTable and its schema.
DataTable CreateDataTable()
{
//Create a DataTable, then add two Columns in it.
DataTable dataTable = new DataTable("Employeess");

dataTable.Columns.Add("EmployeeName", Type.GetType("System.String"));

DataColumn primaryColumn
= new DataColumn("EmployeeID", Type.GetType("System.String"));
dataTable.Columns.Add(primaryColumn);

//Set the PrimaryKey in DataTable
//(to be used in Find functionality of DataTable)
primaryColumn.Unique = true;
dataTable.PrimaryKey = new DataColumn[] { primaryColumn };

return dataTable;
}

//GridView's OnRowDeleting event
protected void GridViewEmps_RowDeleting(object sender
, GridViewDeleteEventArgs e)
{
//Get the control value
string empID
= ((Label)GridViewEmps.Rows[e.RowIndex]
.FindControl("LabelGridViewEmpID")).Text;

//Get the DataTable instance
if (ViewState["EmpDataTable "] == null)
ViewState["EmpDataTable "] = CreateDataTable();
else
dataTableEmps = (DataTable)ViewState["EmpDataTable "];

//Find the row, which we want to delete.
//Only pass the value of Primary key column.
DataRow row = dataTableEmps.Rows.Find(empID);

dataTableEmps.Rows.Remove(row);
GridViewEmps.DataSource = dataTableEmps;
GridViewEmps.DataBind();
}


<!-- HTML code -->
<asp:GridView ID="GridViewEmps" runat="server"
AutoGenerateColumns="false"
OnRowDeleting="GridViewEmps_RowDeleting">
<Columns>
<asp:TemplateField HeaderText="Name">
<ItemTemplate>
<asp:Label ID="LabelGridViewEmpName" runat="server"
Text='<%# Bind("EmployeeName") %>'></asp:Label>
</ItemTemplate>
</asp:TemplateField>
<asp:TemplateField HeaderText="LoginName" Visible="false">
<ItemTemplate>
<asp:Label ID="LabelGridViewEmpID" runat="server"
Text='<%# Bind("EmployeeID") %>'></asp:Label>
</ItemTemplate>
</asp:TemplateField>
</Columns>
</asp:GridView>






9) Function to convert a image/text file into byte array

private byte [] StreamFile(string filename)
{
FileStream fs = new FileStream(filename, FileMode.Open,FileAccess.Read);

// Create a byte array of file stream length
byte[] dataArray = new byte[fs.Length];

//Read block of bytes from stream into the byte array
fs.Read(dataArray,0,System.Convert.ToInt32(fs.Length));

//Close the File Stream
fs.Close();
return dataArray; //return the byte data
}




10) Code to add a new XmlNode / XmlElement in XmlDocument:

string strFilename = "Employees.xml";
XmlDocument xmlDoc = new XmlDocument();

if (File.Exists(strFilename))
{
xmlDoc.Load(strFilename);

XmlElement xElement = xmlDoc.CreateElement("Employee");
string strNewEmployee = "<ID>9</ID>" +
"<Name>Avi</Name>" +
"<Age>25</Age>";

xElement.InnerXml = strNewEmployee;
xmlDoc.DocumentElement.AppendChild(xElement);

xmlDoc.Save("Employees.xml");
}

Operations related to SharePoint's Column type Person or Group

In this post I discussed how we can fetch records from SPListItem's column type Person or Group and stored information back into that column.

Here in following method first I am creating the SPUser by using EnsureUser method of SPSite and then adding that SPUser in SPListItem of SPList. Then I am fetching that SPUser from the SPListItem.

(In Folloing method codes with comments are self-explainatory)



/// <summary>
/// Working with with column of type [Person OR Group]
/// </summary>
void user()
{
//SPListItem spListItem;

string loginName = "DOMAIN\\USERNAME";
//Assign SPUser in your custom column and
//also its Login name (string) in seperate column
//(for ease in development operation)
spListItem["User"] = oWebsite.EnsureUser(loginName);
spListItem["UserLoginName"] = loginName;


//Fetch SPUser values from SPColumn
SPFieldUserValueCollection values
= (SPFieldUserValueCollection)spListItem["User"];
loginName = values[0].User.LoginName;
}



Note : From UI control point of view, we can use SharePoint's PeoplePicker control to enter and validate SPUser.

SharePoint's ListViewWebPart --- Part 2

In continuation with previous post SharePoint's ListViewWebPart.

In this post I discussed how we can apply complex filter in SharePoint's ListView WebPart.

In one of appliation I was using SharePoint's ListView webpart in an aspx page to display the records from SPList. In that page I am also providing a search funtionality. Based on the applied search I am changing the records displayed in ListView webpart.

So the challenge was to apply the complex search filter on ListView webpart programmatically and dynamically. ListView webpart exposes FilterString property but in that we can apply simple filter and that only to one column (as per my knowledge).

So to apply complex filter, first we have to get the SPView from which we are populating data in ListViewWebPart. Then change the Query property of SPView as per our requirement.

In following method I implemented the above mentioned logic (codes with comments are self-explainatory).




/// <summary>
/// Search functionality OR Apply complex filter in SharePoint's ListView WebPart
/// </summary>
/// <param name="eventArgument"></param>
void searchRecord(string flag)
{
try
{
SPSite oWebsite = SPContext.Current.Web;
SPList oList = oWebsite.Lists["LISTNAME"];
//oListViewWP is ListView Webpart's ID

oWebsite.AllowUnsafeUpdates = true;

//Clear Search filter OR revert back SPView modification.
if (flag == "false")
{
//Listview webpart related settings
oListViewWP.ListName = oList.ID.ToString("B").ToUpper();

//Set SPView for Listview WP
SPView view = oList.Views["SPView_NAME"];
view.Query = string.Empty;
view.Update();

oListViewWP.ViewGuid = view.ID.ToString("B").ToUpper();
oListViewWP.GetDesignTimeHtml();
}
else
{
//Build the SPQuery
StringBuilder strbPreQuery
= new StringBuilder("<Where><Contains>");

StringBuilder strbPostQuery
= new StringBuilder("</Value></Contains></Where>");

string strQueryKeyword = string.Empty;

switch (drpSearchKeywordType.SelectedValue)
{
case "Contact Number":
strQueryKeyword
= "<FieldRef Name='Contact_x0020_Number'/>
<Value Type='Text'>";
break;

case "Email ID":
strQueryKeyword
= "<FieldRef Name='Email_x0020_ID'/>
<Value Type='Text'>";
break;
}

//Build SPQuery
SPQuery oQuery = new SPQuery();
oQuery.Query
= strbPreQuery.ToString() + strQueryKeyword +
txtSearchKeyword.Text + strbPostQuery.ToString();
SPListItemCollection itemCol = oWebsite.Lists[spListName].GetItems(oQuery);

if (itemCol.Count > 0)
{
//Listview webpart related settings
oListViewWP.ListName = oList.ID.ToString("B").ToUpper();

//Modifying SPView as per required SPQuery.
SPView view = oList.Views["SPView_NAME"];
view.Query = oQuery.Query;
view.Update();

//Modifying ListView WebPart as changed SPView
oListViewWP.ViewGuid = view.ID.ToString("B").ToUpper();
oListViewWP.GetDesignTimeHtml();
}
}

oWebsite.AllowUnsafeUpdates = false;
}
catch (Exception ex)
{
}
}


How to programmatically attach a document or file in a SPListItem

How to programmatically attach a document or file in a SPListItem

In this post I discussed, how we can programmatically attach a document or file in a SPListItem of SPList (SharePoint's List).

Following method contains the logic to attach the document / file (codes with comments are self-explainatory).



/// <summary>
/// Programmatically Attach document in SPDocument Library
/// </summary>
void documentAttachmentInSPList()
{
//SPListItem spListItem = null; //write code to get the SPListItem.
//fileUpload is control ID of ASP.Net FileUpload control
if (!string.IsNullOrEmpty(fileUpload.FileName))
{
//Get file extension ( *.doc OR *.docx )
string fileExtension
= fileUpload.FileName.Substring(fileUpload.FileName.IndexOf("."));

//FILENAME is file name visible in SPListItem
//Check file is already added or not. If added then delete it.
for (int i = 0; i < spListItem.Attachments.Count; i++)
{
if ((spListItem.Attachments[i] != null)
&& (spListItem.Attachments[i].Equals("FILENAME" + fileExtension)))
{
spListItem.Attachments.Delete("FILENAME" + fileExtension);
break;
}
}

//Attach the file.
spListItem.Attachments.Add("FILENAME" + fileExtension, fileUpload.FileBytes);

//LISTNAME is SPList's name
//See the attached file as link in user-created custom column.
string attachmentURL = Request.Url.Scheme + "://" + Request.Url.Authority
+ "/Lists/" + "LISTNAME" + "/Attachments/" + spListItem.ID + "/";

spListItem["Attached File"]
= attachmentURL + "FILENAME" + fileExtension + ", View FILE";
}
}

How to programmatically upload a document in a SPDocumentLibrary

In this post I discussed, how we can programmatically upload a document in a SPDocumentLibrary (SharePoint's document library).

Following method contains the logic to upload the document (codes with comments are self-explainatory).



/// <summary>
/// Programmatically UPLoad document in SPDocument Library
/// </summary>
void documentUploadInSPDocumentLibrary()
{
//fileUpload is ASP.Net FileUpload control.
if (!string.IsNullOrEmpty(fileUpload.FileName))
{
SPSite oWebsite = SPContext.Current.Web;
oWebsite.AllowUnsafeUpdates = true;
SPDocumentLibrary docLib
= oWebsite.Lists["DOCLIB_NAME"] as SPDocumentLibrary;

//Get file extension ( *.doc OR *.docx )
string fileExtension
= fileUpload.FileName.Substring(fileUpload.FileName.IndexOf("."));
byte[] fileBytes = fileUpload.FileBytes;
string destUrl
= docLib.RootFolder.Url + "/" + "MyFileName" + fileExtension;
SPFile destFile = docLib.RootFolder.Files.Add(destUrl, fileBytes, true);
destFile.Update();

oWebsite.AllowUnsafeUpdates = true;
}
}

Check Record or FileName in a SPDocument Library

In this post I discussed how we can check whether a particular record / filename exist in a SPDocumentLibrary (SharePoint's document library) or not.

In the following method, first I discussed the 4 approaches suggested in following blog/forum, then the 5th apporach (in which I used SPQuery).

According to me the 5th and last approach is good-one because it would't raise an Exception.




/// <summary>
/// Check FileName/Records in SPDocumentLibrary
/// </summary>
void docLibFileExits()
{
SPWeb currentSite = SPContext.Current.Web;
SPDocumentLibrary docLib
= (SPDocumentLibrary)currentSite.GetList(currentSite.Url + "/Documents");

//APPROACH 1
if (docLib.RootFolder.Files[fileName] != null)
{
// do something
//This throws an Argument Exception
}

//APPROACH 2
if (docLib.RootFolder.Files[fileName].Exists == true)
{
// do something
//Again this throws a Argument Exception
}

//APPROACH 3
if (currentSite.GetFile(fileName).Exists == true)
{
// do something
//work
}

//APPROACH 4
bool fileExist = true;

try { docLib.RootFolder.Files[fileName]; }
catch (ArgumentException) { fileExist = false; }

if (fileExists)
{
//...
}



//APPROACH 5
SPSite oWebsite = SPContext.Current.Web;
SPList docLib = oWebsite.Lists["docLib"] as SPDocumentLibrary;
SPListItemCollection itemcolCVs = null;
SPQuery oQueryFileCheck = new SPQuery();

string FILENAME = ""; //FileName that you want to search.

//You can use EQUALS or CONTAINS operation as per your requirement.
//
/*
StringBuilder strBFileCheckQuery = new StringBuilder(
"<Where>" +
"<Eq>" +
"<FieldRef Name='FileLeafRef' />" +
"<Value Type='Text'>FILENAME</Value>" +
"</Eq>" +
"</Where>");
*/

StringBuilder strBFileCheckQuery = new StringBuilder(
"<Where>" +
"<Contains>" +
"<FieldRef Name='FileLeafRef' />" +
"<Value Type='Text'>FILENAME</Value>" +
"</Contains>" +
"</Where>");

oQueryFileCheck.Query = strBFileCheckQuery.ToString();
if (docLib.GetItems(oQueryFileCheck).Count > 0)
{
//To do
}

}





P.S. Here CAML Query Builder helped me a lot to identify and build above SPQuery.

Google