From WSS visual How Tos, adding an Xml file for namespace http://schemas.microsoft.com/sharepoint in %ProgramFiels%Microsoft Visual Studio 8XmlSchema directory will bring the intellisense for feature, element manifest, and all other CAML oriented files developed in Visual Studio.
Author Archive
Configuring IntelliSense with CAML Files When Developing for WSS 3.0
Monday, December 17th, 2007Configuring IntelliSense with CAML Files When Developing for WSS 3.0
Monday, December 17th, 2007From WSS visual How Tos, adding an Xml file for namespace http://schemas.microsoft.com/sharepoint in %ProgramFiels%Microsoft Visual Studio 8XmlSchema directory will bring the intellisense for feature, element manifest, and all other CAML oriented files developed in Visual Studio.
CAML Query <And> <Or> can only compare a pair at a time
Tuesday, November 27th, 2007It's strange that CAML query can only compare a pair of expressions either by <And> or <Or>. So if you have a query like the one follows, you will see an error message "Cannot complete this action. Please try again", which is not very helpful.
<Where>
<And>
<Gt>
<FieldRef Name='Created' /><Value Type='DateTime'><Today OffsetDays='-30' /></Value>
</Gt>
<Neq>
<FieldRef Name='ContentType' /><Value Type='Text'>Folder</Value>
</Neq>
<Neq>
<FieldRef Name='ContentType' /><Value Type='Text'>InfoPath Form Template</Value>
</Neq>
</And>
</Where>
The workaround is to use nested <And> or <Or>, in which each operator only takes two expressions. The following snippet corrects the above one. This will make things more complicate especially for a number of conditions mixed with <And> and <Or>.
<Where>
<And>
<And>
<Gt>
<FieldRef Name='Created' /><Value Type='DateTime'><Today OffsetDays='-30' /></Value>
</Gt>
<Neq>
<FieldRef Name='ContentType' /><Value Type='Text'>Folder</Value>
</Neq>
</And>
<Neq>
<FieldRef Name='ContentType' /><Value Type='Text'>InfoPath Form Template</Value>
</Neq>
</And>
</Where>
CAML Query <And> <Or> can only compare a pair at a time
Tuesday, November 27th, 2007It's strange that CAML query can only compare a pair of expressions either by <And> or <Or>. So if you have a query like the one follows, you will see an error message "Cannot complete this action. Please try again", which is not very helpful.
<Where>
<And>
<Gt>
<FieldRef Name='Created' /><Value Type='DateTime'><Today OffsetDays='-30' /></Value>
</Gt>
<Neq>
<FieldRef Name='ContentType' /><Value Type='Text'>Folder</Value>
</Neq>
<Neq>
<FieldRef Name='ContentType' /><Value Type='Text'>InfoPath Form Template</Value>
</Neq>
</And>
</Where>
The workaround is to use nested <And> or <Or>, in which each operator only takes two expressions. The following snippet corrects the above one. This will make things more complicate especially for a number of conditions mixed with <And> and <Or>.
<Where>
<And>
<And>
<Gt>
<FieldRef Name='Created' /><Value Type='DateTime'><Today OffsetDays='-30' /></Value>
</Gt>
<Neq>
<FieldRef Name='ContentType' /><Value Type='Text'>Folder</Value>
</Neq>
</And>
<Neq>
<FieldRef Name='ContentType' /><Value Type='Text'>InfoPath Form Template</Value>
</Neq>
</And>
</Where>
Sort a SPView using code
Tuesday, November 27th, 2007Long time no blog….
My current project with MOSS requires doing almost everything programmatically, provisioning sites, constructing site navigation, creating lists, applying content types, etc. I spent a little while to figure how to sort a SPView for a document library. Once you know it, it's actually super easy. Just modify the Query property.
Example:
…..
SPView view = list.DefaultView;
view.Query = "<OrderBy><FieldRef Name='Modified' Ascending='FALSE' /></OrderBy>"'
…..
This set the view ordered by the modified date, the newest document at the top. The default order is by document name. Also we can filter the records for this view since it is CAML query string.
Sort a SPView using code
Tuesday, November 27th, 2007Long time no blog….
My current project with MOSS requires doing almost everything programmatically, provisioning sites, constructing site navigation, creating lists, applying content types, etc. I spent a little while to figure how to sort a SPView for a document library. Once you know it, it's actually super easy. Just modify the Query property.
Example:
…..
SPView view = list.DefaultView;
view.Query = "<OrderBy><FieldRef Name='Modified' Ascending='FALSE' /></OrderBy>"'
…..
This set the view ordered by the modified date, the newest document at the top. The default order is by document name. Also we can filter the records for this view since it is CAML query string.
How to connect Web Parts programmatically in WSS 3
Thursday, June 21st, 2007Web parts are usually connected on the UI as part of customization. There are times better to connect Web parts using code on the fly. For example, we have a site template with a page containing two Web parts working together. Making the connection during site provisioning would be more user friendly. Actually it becomes a must for the template I am working on currently because huge number of sites will be instantiated based on it. Thanks for edhild's post giving me great hints and a good start. The following is what I have come up with for this topic.
Besides WSS 2 Web part, there are two WebPart base classes, ASP.NET WebPart and WSS 3 WebPart, and two sets of connection models, IWebPartXXX in ASP.NET 2/3 and IXXXProvider / IXXXConsumer in WSS 3. Choosing different Web part bases and interfaces will affect the way how to connect them programmatically and how to include them in the site definition. The general rule is using Web part manager to connect Web parts in ASP.NET connection model, using Web parts' Connections property to connect those in WSS connection model. Furthermore, ASP.NET based Web parts cannot be put into a SPWebPartZone in the site template (maybe just I don't know how).
Connecting Web parts in ASP.NET connection medel
As edhild's post shows, we need SPLimitedWebPartManager to connect the Web parts with ASP.NET style interfaces. The steps for this are:
- Get the SPLimitedWebPartManager instance on the page;
- Get the provider and consumer Web parts need to be connected;
- Get the connection points for the provider and consumer Web parts;
- Connect them using SPConnectWebParts method. If necessary, certain transforming needs to be in place for compatible interfaces.
Code snippet:
..
SPWeb web = (SPWeb)properties.Feature.Parent;
SPLimitedWebPartManager mgr = web.GetLimitedWebPartManager("default.aspx", PersonalizationScope.Shared);
System.Web.UI.WebControls.WebParts.WebPart addSearch = mgr.WebParts["OTAddressSearch"];
System.Web.UI.WebControls.WebParts.WebPart addDisplay = mgr.WebParts["OTAddressDisplay"];
ConsumerConnectionPoint addDisplayConnPoint = mgr.GetConsumerConnectionPoints(addDisplay)["AddressConsumer_ot"];
ProviderConnectionPoint addSearchConnPoint = mgr.GetProviderConnectionPoints(addSearch)["AddressProvider_ot"];
mgr.SPConnectWebParts(addSearch, addSearchConnPoint, addDisplay, addDisplayConnPoint);
Some thoughts:
- It is preferable to assign the Web part ID property in the template, so that it can be referenced by ID instead of index. Well, I don't how to assign a list view Web part an ID, but for a custom Web part, the following is a sample:
<AllUsersWebPart WebPartZoneID="Left" WebPartOrder="1">
<![CDATA[<?xml version="1.0" encoding="utf-8"?><WebPart xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns="http://schemas.microsoft.com/WebPart/v2">
<ID>OTAddressSearch</ID>
<Title>Address Search</Title>
..
- The same for the connection point, it is better to reference it by ID. The ID is defined in Web part source code, such as:
[ConnectionProvider("Address search criteria", "AddressProvider_ot")]
public IWebPartField GetConnectionInterface()
{
return this;
}.
- I haven't figured out how to put a Web part derived from ASP.NET WebPart class into the site template through onet.xml. It is possible to put this type of Web parts onto default.aspx (or other Web part pages), but the user won't be able to edit them by SharePoint page editing. My approach is to base my Web parts on SharePoint WebPart class, and implement ASP.NET connection interfaces.
Connect Web parts in WSS connection model
If the Web parts use WSS connection model, we should be using a different way to link them. The key idea is to set the Connections property of the consumer Web part, and generally it includes following steps:
- Get the SPLimitedWebPartManager instance of the page;
- Get the provider and consumer Web parts need to be connected, and cast them to WSS WebPart;
- Assign the Web parts ConnectionID if they don't have one yet. For a Web part never been connected, this property is an empty GUID.
- Set the Connections property of the consumer Web part;
- Save the changes.
Code snippet:
……
SPWeb web = (SPWeb)properties.Feature.Parent;
SPLimitedWebPartManager mgr = web.GetLimitedWebPartManager("default.aspx", PersonalizationScope.Shared);
Microsoft.SharePoint.WebPartPages.WebPart imgSearch = (Microsoft.SharePoint.WebPartPages.WebPart)mgr.WebParts["OTImgSearch"];
Microsoft.SharePoint.WebPartPages.WebPart imgDisplay = (Microsoft.SharePoint.WebPartPages.WebPart)mgr.WebParts["OTImgDisplay"];
if (imgSearch.ConnectionID == Guid.Empty)
imgSearch.ConnectionID = Guid.NewGuid();
if (imgDisplay.ConnectionID == Guid.Empty)
imgDisplay.ConnectionID = Guid.NewGuid();
imgDisplay.Connections = imgDisplay.ConnectionID + "," +
imgSearch.ConnectionID + "," +
"MyCellConsumerInterface_WPQ_" + "," +
"MyCellProviderInterface_WPQ_" + "," +
"MyCellConsumerInterface_WPQ_" + "," +
"MyCellProviderInterface_WPQ_";
mgr.SaveChanges(imgSearch);
mgr.SaveChanges(imgDisplay);
Some thoughts:
- Refer the msdn for details about the Connections property. The above example only shows the simplest case, connecting Web parts that implement ICellProvider and ICellConsumer interfaces.
- We need the connection point ID for the two Web parts. They are defined in the RegisterInterface() method, e.g.:
RegisterInterface("MyCellConsumerInterface_WPQ_", //InterfaceName
InterfaceTypes.ICellConsumer, //InterfaceType
WebPart.UnlimitedConnections, //MaxConnections
ConnectionRunAt.ServerAndClient, //RunAtOptions
this, //InterfaceObject
"CellConsumerInterface_WPQ_", //InterfaceClientReference
"Get String Value", //MenuLabel
"Just a simple ICellConsumer");
Next step is to put the code into proper site provisioning event. If the Visual Studio site definition project template happens to be enough for your needs, then simply put them into the place MS reserved for developers – the OnActivated method. For those who make their own SharePoint solutions, just like me, we need to define a feature, include this feature in the site definition, and put the code in the feature receiver's FeatureActivated event. One thing I noticed is that, the default site home page (default.aspx) should be defined as an element file as this feature, not defined in the site definition (in site template folder together with the onet.xml file). Otherwise, I got the file not found error when opening the page to get the Web part manager instance. I am guessing this is because the page defined in site definition is instantiated after the feature activation. Anyway, making it as an element file does not bother me since I will have to define many other Web part pages along with this feature.
How to connect Web Parts programmatically in WSS 3
Thursday, June 21st, 2007Web parts are usually connected on the UI as part of customization. There are times better to connect Web parts using code on the fly. For example, we have a site template with a page containing two Web parts working together. Making the connection during site provisioning would be more user friendly. Actually it becomes a must for the template I am working on currently because huge number of sites will be instantiated based on it. Thanks for edhild's post giving me great hints and a good start. The following is what I have come up with for this topic.
Besides WSS 2 Web part, there are two WebPart base classes, ASP.NET WebPart and WSS 3 WebPart, and two sets of connection models, IWebPartXXX in ASP.NET 2/3 and IXXXProvider / IXXXConsumer in WSS 3. Choosing different Web part bases and interfaces will affect the way how to connect them programmatically and how to include them in the site definition. The general rule is using Web part manager to connect Web parts in ASP.NET connection model, using Web parts' Connections property to connect those in WSS connection model. Furthermore, ASP.NET based Web parts cannot be put into a SPWebPartZone in the site template (maybe just I don't know how).
Connecting Web parts in ASP.NET connection medel
As edhild's post shows, we need SPLimitedWebPartManager to connect the Web parts with ASP.NET style interfaces. The steps for this are:
- Get the SPLimitedWebPartManager instance on the page;
- Get the provider and consumer Web parts need to be connected;
- Get the connection points for the provider and consumer Web parts;
- Connect them using SPConnectWebParts method. If necessary, certain transforming needs to be in place for compatible interfaces.
Code snippet:
..
SPWeb web = (SPWeb)properties.Feature.Parent;
SPLimitedWebPartManager mgr = web.GetLimitedWebPartManager("default.aspx", PersonalizationScope.Shared);
System.Web.UI.WebControls.WebParts.WebPart addSearch = mgr.WebParts["OTAddressSearch"];
System.Web.UI.WebControls.WebParts.WebPart addDisplay = mgr.WebParts["OTAddressDisplay"];
ConsumerConnectionPoint addDisplayConnPoint = mgr.GetConsumerConnectionPoints(addDisplay)["AddressConsumer_ot"];
ProviderConnectionPoint addSearchConnPoint = mgr.GetProviderConnectionPoints(addSearch)["AddressProvider_ot"];
mgr.SPConnectWebParts(addSearch, addSearchConnPoint, addDisplay, addDisplayConnPoint);
Some thoughts:
- It is preferable to assign the Web part ID property in the template, so that it can be referenced by ID instead of index. Well, I don't how to assign a list view Web part an ID, but for a custom Web part, the following is a sample:
<AllUsersWebPart WebPartZoneID="Left" WebPartOrder="1">
<![CDATA[<?xml version="1.0" encoding="utf-8"?><WebPart xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns="http://schemas.microsoft.com/WebPart/v2">
<ID>OTAddressSearch</ID>
<Title>Address Search</Title>
..
- The same for the connection point, it is better to reference it by ID. The ID is defined in Web part source code, such as:
[ConnectionProvider("Address search criteria", "AddressProvider_ot")]
public IWebPartField GetConnectionInterface()
{
return this;
}.
- I haven't figured out how to put a Web part derived from ASP.NET WebPart class into the site template through onet.xml. It is possible to put this type of Web parts onto default.aspx (or other Web part pages), but the user won't be able to edit them by SharePoint page editing. My approach is to base my Web parts on SharePoint WebPart class, and implement ASP.NET connection interfaces.
Connect Web parts in WSS connection model
If the Web parts use WSS connection model, we should be using a different way to link them. The key idea is to set the Connections property of the consumer Web part, and generally it includes following steps:
- Get the SPLimitedWebPartManager instance of the page;
- Get the provider and consumer Web parts need to be connected, and cast them to WSS WebPart;
- Assign the Web parts ConnectionID if they don't have one yet. For a Web part never been connected, this property is an empty GUID.
- Set the Connections property of the consumer Web part;
- Save the changes.
Code snippet:
……
SPWeb web = (SPWeb)properties.Feature.Parent;
SPLimitedWebPartManager mgr = web.GetLimitedWebPartManager("default.aspx", PersonalizationScope.Shared);
Microsoft.SharePoint.WebPartPages.WebPart imgSearch = (Microsoft.SharePoint.WebPartPages.WebPart)mgr.WebParts["OTImgSearch"];
Microsoft.SharePoint.WebPartPages.WebPart imgDisplay = (Microsoft.SharePoint.WebPartPages.WebPart)mgr.WebParts["OTImgDisplay"];
if (imgSearch.ConnectionID == Guid.Empty)
imgSearch.ConnectionID = Guid.NewGuid();
if (imgDisplay.ConnectionID == Guid.Empty)
imgDisplay.ConnectionID = Guid.NewGuid();
imgDisplay.Connections = imgDisplay.ConnectionID + "," +
imgSearch.ConnectionID + "," +
"MyCellConsumerInterface_WPQ_" + "," +
"MyCellProviderInterface_WPQ_" + "," +
"MyCellConsumerInterface_WPQ_" + "," +
"MyCellProviderInterface_WPQ_";
mgr.SaveChanges(imgSearch);
mgr.SaveChanges(imgDisplay);
Some thoughts:
- Refer the msdn for details about the Connections property. The above example only shows the simplest case, connecting Web parts that implement ICellProvider and ICellConsumer interfaces.
- We need the connection point ID for the two Web parts. They are defined in the RegisterInterface() method, e.g.:
RegisterInterface("MyCellConsumerInterface_WPQ_", //InterfaceName
InterfaceTypes.ICellConsumer, //InterfaceType
WebPart.UnlimitedConnections, //MaxConnections
ConnectionRunAt.ServerAndClient, //RunAtOptions
this, //InterfaceObject
"CellConsumerInterface_WPQ_", //InterfaceClientReference
"Get String Value", //MenuLabel
"Just a simple ICellConsumer");
Next step is to put the code into proper site provisioning event. If the Visual Studio site definition project template happens to be enough for your needs, then simply put them into the place MS reserved for developers – the OnActivated method. For those who make their own SharePoint solutions, just like me, we need to define a feature, include this feature in the site definition, and put the code in the feature receiver's FeatureActivated event. One thing I noticed is that, the default site home page (default.aspx) should be defined as an element file as this feature, not defined in the site definition (in site template folder together with the onet.xml file). Otherwise, I got the file not found error when opening the page to get the Web part manager instance. I am guessing this is because the page defined in site definition is instantiated after the feature activation. Anyway, making it as an element file does not bother me since I will have to define many other Web part pages along with this feature.
Tricks for debugging Web Parts and Web pages in SharePoint
Monday, May 14th, 2007I decide to make this post as an ongoing one to record the tricks I come up with for debugging Web Parts and Web Part pages in ShaePoint.
1. Show call stack if exception occurs. Pages in SharePoint show "user friendly" messages when exception occurs. Often the messages are meaningless to developers. On the other hand the call stack on a normal aspx page upon exception is useful to determin where and what is wrong. So in the course of development, turn it on for SharePoint pages is a good choice.
How: find the tag <configuration><SharePoint><SafeMode>, set the attribute as CallStack="true"
2. Write in-line code block for easy debug. On an ASP.NET page, writing in-line code block is usually convenient especially with the full intellisense. After testing and debug, we can simply move the code block into the partial page class (code beside) and deploy it into GAC. However, SharePoint does now allow the code block on pages, unless we modify the web.config.
How: in tag <configuration><SharePoint><SafeMode><PageParserPaths>, add entry to allow the page contains code block, i.e. <PageParserPath VirtualPath="/list/matters/myitems.aspx" CompilationMode="Always" AllowServerSideScript="true" />
For details, see Serge's nice post.
Tricks for debugging Web Parts and Web pages in SharePoint
Monday, May 14th, 2007I decide to make this post as an ongoing one to record the tricks I come up with for debugging Web Parts and Web Part pages in ShaePoint.
1. Show call stack if exception occurs. Pages in SharePoint show "user friendly" messages when exception occurs. Often the messages are meaningless to developers. On the other hand the call stack on a normal aspx page upon exception is useful to determin where and what is wrong. So in the course of development, turn it on for SharePoint pages is a good choice.
How: find the tag <configuration><SharePoint><SafeMode>, set the attribute as CallStack="true"
2. Write in-line code block for easy debug. On an ASP.NET page, writing in-line code block is usually convenient especially with the full intellisense. After testing and debug, we can simply move the code block into the partial page class (code beside) and deploy it into GAC. However, SharePoint does now allow the code block on pages, unless we modify the web.config.
How: in tag <configuration><SharePoint><SafeMode><PageParserPaths>, add entry to allow the page contains code block, i.e. <PageParserPath VirtualPath="/list/matters/myitems.aspx" CompilationMode="Always" AllowServerSideScript="true" />
For details, see Serge's nice post.