Tuesday 26 June 2007

"Trying to use SPWeb object that has been closed.." Error

We were getting “Trying to use an SPWeb object that has been closed or disposed and is no longer valid.” in our MOSS -Workflow project.

After investigating all the source codes, I found the error causing statement in WebPart class.

The best practice is to dispose objects either by calling Dispose() in finally block or with using clause.

The snippet causing problem in our project was

SPGroup group;
using (SPWeb web = SPContext.Current.Web)
{
group = web.Groups[“Administrators”];
this.DisplayText = "Submit for Approval";
:
:
}

Here, the object is not really created in the memory, only the reference to the exisiting object is got and used. We should not dispose the current context web object manually in our code.

More information about best practice can be found at
http://msdn2.microsoft.com/en-us/library/ms778813.aspx

Happy Coding :)

Tuesday 19 June 2007

Extending stsadm.exe with custom actions

Stsadm.exe command can be extended by inheriting
Microsoft.SharePoint.StsAdmin.ISPStsadmCommand interface.

The actual process involves two steps.
  1. Creating the configuration xml file.
  2. Creating a class that implements ISPStsadmCommand interface.
Configuration file
The configuration xml file indicates to stsadm.exe which operations are available. This configuration file must follow well known naming convention stsadmcommands.<customcommands>.xml where <customcommands> can be any meaningful name but must be unique. Through this naming convention for configuration files, stsadm.exe implements a pluggable architecture for declaring custom operations.


<?xml version="1.0" encoding="utf-8"?>

<commands>

<command name="<name for command>" class="<namespace>,<class>,Version=<version>,

Culture=neutral,PublicKeyToken=<token>">

</command>

</commands>


The configuration file itself is a simple XML document with a root element containing one or more sub-elements. These sub-elements are where we declare custom operations and include two attributes name and class. The name attribute indicates the name of the operation to be executed by stsadm.exe. Note that the exact value of the name attribute will be specified by an administrator at runtime using the -o named parameter in order to execute the declared custom operation. The second attribute class specifies the class and .NET Assembly name where our classes that implement the ISPStsadmCommand interface.

.Net assembly
The .Net assembly containing the classes has to be placed in GAC. Each operation requires separate class inheriting ISPStsadmCommand interface. The ISPStsadmCommand interface has two methods to implement.
1. GetHelpMessage – this method returns help and usage information.
2. Run – this method actually does the custom operation.



class CustomActivateFeature : ISPStsadmCommand
{
//method to return help message
public string GetHelpMessage(string command)
{
//named parameters expected
return "-url ";
}

//method called to perform the action
public int Run(string command,
System.Collections.Specialized.StringDictionary keyValues,
out string output)
{

//assign value to variables
if (keyValues.ContainsKey("url"))
{
url = keyValues["url"];
if ((url == null) (url.Length == 0)) output += "url cannot be empty";
:
:


Deployment
The deployment process involves two steps.
1. Copy the configuration file (stsadmcommands.feature.xml) to [12 Hive]\CONFIG folder.
2. Copy the DLL file to GAC.

Now we can perform our custom actions using stsadm.exe by typing

stsadm.exe –o <name we have specified in xml file> <named
parameters> at command prompt.

It can be very useful to include in script/batch file for actions like backup, restore or some other automated actions.

Tuesday 12 June 2007

Custom property of Webpart - dynamic DropDownList

Custom properties can be included directly by including the properties (GET-SET accessor) in the web part class. Based on the return data type of the property, corresponding UI control will be rendered at run time.
Ex. If there is a property in webpart class as below

private string columnName = “”;

[WebBrowsable(true),
Personalizable(true),
Category("Web Part Settings"),
DisplayName("Site Column name."),
WebDisplayName("Site Column name."),
WebDescription("Site Column name.")]
public string ColumnName
{
get { return columnName; }
set { columnName = value; }
}


Note: WebBrowsable and Personalizable attributes should be true.
Above property will be rendered as text box on the property pane.

To display dropdown list if values are static, create a enum containing all static values and use that enum in the get-set method.
Ex:

public enum searchscope
{
All_Sites,
Current_Site
}


private searchscope searchScope = searchscope.All_Sites;
[WebBrowsable(true),
Personalizable(true),
Category("Web Part Settings"),
DisplayName("Search scope."),
WebDisplayName("Search scope."),
WebDescription("Detirmines the scope to search.")]


public searchscope SearchScope
{
get { return searchScope; }
set
{
searchScope = value;
}
}


The above code will diplay a dropdown list with two static values.

Ok, what we have to do if the values are to be dynamically read from sharepoint list or some other source.
1. Create a class that inherit from Microsoft.SharePoint.WebPartPages.ToolPart
2. Create and initialise DropDownList
3. Add the drop down list to the Controls in the overridden method CreateChildControls.
4. Override ApplyChanges() method to update the value in the main webpart class.

class CustomToolPart : ToolPart
{
DropDownList ddlTypes = new DropDownList();
:
:

public CustomToolPart()//Constructor
{
this.Title = "Select Type";//Set the title for our custom tool part.
}
protected override void CreateChildControls()
{
PopulatePageTypes(); //Method to populate the drop down list.
Controls.Add(ddlTypes);
}
public override void ApplyChanges()
{
//Logic to update the variable value in main webpart class
}
}

Now in the main web part class,
1. Override GetToolParts() method
2. Create and initialise our custom class.
3. Add to ToolParts array.

public override ToolPart[] GetToolParts()
{
ToolPart[] toolparts = new ToolPart[3];
WebPartToolPart wptp = new WebPartToolPart(); //WebPart's default properties
CustomPropertyToolPart cptp = new CustomPropertyToolPart(); //Standard Custom properties added to webpart like ColumnName and SearchScope.
CustomToolPart ctp = new CustomToolPart(); // Our custom property ToolPart

toolparts[0] = wptp;
toolparts[1] = cptp;
toolparts[2] = ctp;

return toolparts;
}

That’s all. Deploy the webpart and you can see the dropdown list with dynamic values when you modify the properties of the webpart.