09 tháng 7, 2010

SharePoint workflow API

In this post I'd like to write down several important notes on working with workflow API in SharePoint.

Snippet code to assign an workflow template to a List or Content Type


Because the SharePoint feature schema doesn't support in assigning a workflow template to a specific List or Content Type, we must do it with code. A convenient place to do it is in Feature Activated event. Before looking at the code, you could check out Workflow Object Model Overview in SharePoint Foundation for some basic related-workflow classes. The snippet code below is what you could use to assign (associate) workflow template to List or Content Type


using System.Xml.Linq; // Include this namespace to use LINQ to XML

// ....

// When creating a workflow for list or content type, it needs two other list to operate: a list for tasks and a list for history
spWeb.Lists.Add("Workflow Tasks", string.Empty, SPListTemplateType.Tasks);
spWeb.Lists.Add("Workflow History Tasks",string.Empty, SPListTemplateType.WorkflowHistory);

// Retrieve Approval workflow template with it Guid defined in ReviewWorkflows feature
var approvalWFTemplate = spWeb.WorkflowTemplates[new Guid("C6964BFF-BF8D-41ac-AD5E-B61EC111731C")]; /* This guid is Approval Workflow , you can find it in ReviewWorkflows folder in Features */
var wfAssociation = SPWorkflowAssociation.CreateListAssociation(approvalWFTemplate, "Sample Approval Workflow", "Workflow Tasks","Workflow History Tasks");
// If you want to assign workflow to content type in list, call CreateListContentTypeAssociation()
// var wfAssociation = SPWorkflowAssociation.CreateListContentTypeAssociation(approvalWFTemplate, "Sample Approval Workflow", "Workflow Tasks","Workflow History Tasks");
wfAssociation.AllowManual = false;

XNamespace MyNamespace = @"http://schemas.microsoft.com/office/infopath/2003/myXSD";
XNamespace XsiNamespace = @"http://www.w3.org/2001/XMLSchema-instance";

// The AssociationData property represents for the settings that user see in 2nd screen when creating workflow in SharePoint UI
var associationDataXml = XElement.Parse(wfAssociation.AssociationData);
associationDataXml.SetElementValue(MyNamespace+"CreateTasksInSerial", false); // Run workflow in parallel
associationDataXml.SetElementValue(MyNamespace + "Description", "Please review this task");
// Continue to modify appropriate xml elements for desired settings. See following paragraph to know elements and they are mapped to which settings on UI

// The element is little tricky
// It is by default
// If we want to set its value to true, remove the attribute first
var elementNameWithNS = MyNamespace + "StopOnAnyReject";
associationDataXml.Element(elementNameWithNS).Attribute(XsiNamespace + "nil").Remove();
associationDataXml.SetElementValue(elementNameWithNS, true);

// Reviewers is Approvers in UI
associationDataXml.Element(MyNamespace + "Reviewers").Add(
new XElement(MyNamespace + "Person",
new XElement(MyNamespace + "DisplayName", "Managers"),
new XElement(MyNamespace + "AccountId", "Managers"),
new XElement(MyNamespace + "AccountType", "SharePointGroup")
)
);

// CC in UI
associationDataXml.Element(MyNamespace + "CC").Add(
new XElement(MyNamespace + "Person",
new XElement(MyNamespace + "DisplayName","Managers"),
new XElement(MyNamespace + "AccountId", "Managers"),
new XElement(MyNamespace + "AccountType", "SharePointGroup")
)
);

// Update modifed xml segment back to WorkflowAssociation object
wfAssociation.AssociationData = associationDataXml.ToString();
var list = spWeb.Lists["Unapproved Document Library"];
list.AddWorkflowAssociation(wfAssociation);
// If you want to assign workflow template to content type in list, run below code
// list.ContentTypes["Custom ContentType"].AddWorkflowAssociation(wfAssociation);


The main idea here is that I modified the XML segment in AssociationData property which represents for settings in workflow association form. So how the XML segment in AssociationData looks like ? Here you're :

<my:myFields xml:lang="en-us" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:my="http://schemas.microsoft.com/office/infopath/2003/myXSD">

<my:Reviewers>
<my:Person>
<my:DisplayName>Managers</my:DisplayName>
<my:AccountId>Managers</my:AccountId>
<my:AccountType>SharePointGroup</my:AccountType>
</my:Person>
</my:Reviewers>

<my:CC></my:CC>
<my:DueDate xsi:nil="true"></my:DueDate>
<my:Description xsi:nil="true"></my:Description>
<my:Title></my:Title>
<my:DefaultTaskType>1</my:DefaultTaskType>
<my:CreateTasksInSerial>true</my:CreateTasksInSerial>
<my:AllowDelegation>false</my:AllowDelegation>
<my:AllowChangeRequests>true</my:AllowChangeRequests>
<my:StopOnAnyReject xsi:nil="true"></my:StopOnAnyReject>
<my:WantedTasks xsi:nil="true"></my:WantedTasks>
<my:SetMetadataOnSuccess>false</my:SetMetadataOnSuccess>
<my:MetadataSuccessField></my:MetadataSuccessField>
<my:MetadataSuccessValue></my:MetadataSuccessValue>
<my:ApproveWhenComplete>false</my:ApproveWhenComplete>
<my:TimePerTaskVal xsi:nil="true"></my:TimePerTaskVal>
<my:TimePerTaskType xsi:nil="true"></my:TimePerTaskType>
<my:Voting>false</my:Voting>
<my:MetadataTriggerField></my:MetadataTriggerField>
<my:MetadataTriggerValue></my:MetadataTriggerValue>
<my:InitLock>true</my:InitLock>
<my:MetadataStop>false</my:MetadataStop>
<my:ItemChangeStop>false</my:ItemChangeStop>
<my:GroupTasks>true</my:GroupTasks>
</my:myFields>


Looking that XML, we have no idea which XML element goes with which setting. I figured out some of them. In below pictures, I point out settings and which XML is mapped to











Programming starting a workflow


Starting a workflow is not hard, just call StartWorkflow() method with appropriate parameters. But the third parameter is pretty tricky because the MSDN doesn't give any document for it. After soul searching, I found out this actually is the XML in AssociationData property. Here the sample code

// Get workflow association of the list
SPWorkflowAssociation approvalAssociation = null;
var requestDetailList = spWeb.Lists["Unapproved Document Library"];
foreach (SPWorkflowAssociation wfAssociation in requestDetailList.WorkflowAssociations)
{
if (wfAssociation.Name == "Sample Approval Workflow")
{
approvalAssociation = wfAssociation;
break;
}
}

if (approvalAssociation != null)
{
spSite.WorkflowManager.StartWorkflow(spWeb.Lists["Unapproved Document Library"].Items[0], approvalAssociation, approvalAssociation.AssociationData /* We can change AssociationData as our needs before giving to StartWorkflow() */ );
}


Just a few notes, hope it help

03 tháng 6, 2010

Customize SharePoint standard forms with Data View web part

The Data View webpart is encapsulated control for presenting data and uses a SharePoint data source server control to load data items from List or Document Library or multiple List. This web part also allows user to add / edit / remove data entries in data source. SharePoint Designer is used to add (and modify) Data View web part to an existing .aspx page. In this post, I'd like to point out notably things we need to care when working with Data View web part.

Here is the common scenario we may see client's need that the Data View web part is likely a solution: they want to use SharePoint to store invoice data. They have two List :
  • Invoice : store data for an invoice. The possible columns for this list are : Client (could be lookup field but here I use textbox for simple) , Created Date , Payment Status (Completed / Pending / Invalid)
  • Invoice Detail : store items that going with the an invoice. The columns for this list are : Invoice ID (hidden and managed by our code) , Item Name (text for simple) , Price
And they want something that supports them in entering invoice easily. I thought it's not easy to implement a solution that user could be able to enter invoice details when they're creating a new Invoice in the NewForm.aspx of Invoice . So I went for an alternative way : I will customize the DispForm.aspx of Invoice so that Invoice Detail grid shows up below the Invoice item and user can enter invoice details within in DispForm.aspx. Below is some sample pictures I captured from the demo project (download link is in the end of this post)













Before I start, if you have no idea what's Data View web part, kindly read this blog first to get the insight on how to play with Data View webpart: http://www.endusersharepoint.com/2009/05/12/data-view-web-part-the-basics-insert-a-dvwp-on-your-page/

Now I will create a SharePoint-template project for demonstration and use it through this post. From above analysis, List Definition project will be used







After create the project, I organized the folder structure as 12-hive





From the structure, you see that the feature has 2 list definition (and its instances) for Invoice and InvoiceDetail. To start with, I add some fields on two lists and deploy the feature to web. Below is Invoice and Invoice Detail list after installed.









Ok, Now I'll use SharePoint Designer (SPD) to create Data View WebPart in DispForm.aspx of Invoice. First, I open the DispForm.aspx page of Invoice list





Second, I find the default List Form webpart and put the cursor right after that where the new Data View webpart will be added. You can see that two webparts are within the default WebPartZone control in page.





Then I start adding Data View webpart and apply some settings
  • Add InvoiceID column
  • Allow sorting and filtering on header
  • Allow insert/edit/remove




Select Invoice Detail list as source




Select three columns that represent for invoice detail and insert them to list as Multiple Item View





Now we need to do some customization. Open Common Data View Task dialog, click on Edit Columns link to add InvoiceID column which is used to associate invoice detail to a invoice






Click on Data View properties link




Allow sorting / filtering and inserting ...







At this point, the webpart show all invoice detail but we only want it to show invoice detail going with current viewing invoice. To do that, add filtering value on data source. Let create a parameter to represent for ID of Invoice








Name the new parameter: InvoiceID and make it get data from query string




Configure filtering




Only retrieve Invoice Detail that its InvoiceID field is same value as ID parameter in query string








Because the Data View webpart needs to have list id of Invoice Detail in order to retrieve query data as well as perform update to. By default the SPD creates a parameter that contains the current list id Invoice Detail but we should add some code so that the parameter can work on other environment. Let modify the ListID parameter




We will tell the data view webpart to get the list id from our server control (created soon)




Enter the server control ID, this control isn't available in the .aspx page but we'll create soon




Open the DispForm.aspx in Invoice folder in Visual Studio, on the beginning of PlaceHolderMain, add an ASP.NET label control with ID lblInvoiceDetailId

<asp:Content ContentPlaceHolderId="PlaceHolderLeftNavBar" runat="server"/>
<asp:Content ContentPlaceHolderId="PlaceHolderMain" runat="server">

<asp:Label ID="lblInvoiceDetailId" runat="server" style="display:none;"></asp:Label>

<table cellpadding=0 cellspacing=0 id="onetIDListForm">



We also need to make this page inherit from our class instead of default SharePoint. Go to top of page, modify the Inherits attribute to our code behind (created soon)

<%@ Page language="C#" MasterPageFile="~masterurl/default.master"   
Inherits="DataViewWebPartDemo.Invoice.DispForm,DataViewWebPartDemo,Version=1.0.0.0,Culture=neutral,PublicKeyToken=9f4da00116c38ec5"
meta:webpartpageexpansion="full" meta:progid="SharePoint.WebPartPage.Document" %>



Ok, now we'll create code behind for DispForm.aspx. Add new class in Invoice folder and name it DispForm.aspx.cs








Fill up the file with below code, remember to add Microsoft.SharePoint and System.Web assembly

using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.SharePoint;

namespace DataViewWebPartDemo.Invoice
{
public class DispForm : Microsoft.SharePoint.WebPartPages.WebPartPage
{
protected global::System.Web.UI.WebControls.Label lblInvoiceDetailId;

public void Page_Init(object sender, EventArgs e)
{
lblInvoiceDetailId.Text = SPContext.Current.Web.Lists["Invoice Detail"].ID.ToString();
}
}
}



In the code, we assign the current Id of list named Invoice Detail to control so that the data view webpart then use it to retrieve data

You need to modify the web.config (adding SafeControl) after deploying the feature and assembly in order the DispForm.aspx can be run in SharePoint


Save all changes before going on





SPD will generate XSL definition based on what we've done on the UI, so we need to pack those definition to the feature. This can be done by using webpart exporting option on SharePoint UI. Let just create a test item on Invoice list and view it in display view





Now we're going to export what we have done so far on data view webpart






Temporarily save Invoice_Detail.webpart to Desktop




Open schema.xml file of Invoice list in feature





Modify the Form (Type="DisplayForm") element as below
<Forms>
<Form Type="DisplayForm" Url="DispForm.aspx" WebPartZoneID="Main">
<WebParts>
<AllUsersWebPart WebPartZoneID="Main" WebPartOrder="2">
<![CDATA[

<!-- Exported webpart definition goes here -->

]]>
</AllUsersWebPart>
</WebParts>
</Form>
<Form Type="EditForm" Url="EditForm.aspx" WebPartZoneID="Main"/>
<Form Type="NewForm" Url="NewForm.aspx" WebPartZoneID="Main"/>
</Forms>



Then open the downloaded Invoice_Detail.webpart, copy whole content and put inside CDATA. It looks like below

<Forms>
<Form Type="DisplayForm" Url="DispForm.aspx" WebPartZoneID="Main">
<WebParts>
<AllUsersWebPart WebPartZoneID="Main" WebPartOrder="2">
<![CDATA[

<!-- Exported webpart definition goes here -->
<webparts>
<webpart xmlns="http://schemas.microsoft.com/WebPart/v3">
<metadata>
<type name="Microsoft.SharePoint.WebPartPages.DataFormWebPart, Microsoft.SharePoint, Version=12.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c">
<importerrormessage>Cannot import this Web Part.</importerrormessage>
</type></metadata>
<data>
<properties>
<property name="MissingAssembly" type="string">Cannot import this Web Part.</property>
<property name="FireInitialRow" type="bool">True</property>
<property name="TitleIconImageUrl" type="string">
............
]]>
</AllUsersWebPart>
</WebParts>
</Form>
<Form Type="EditForm" Url="EditForm.aspx" WebPartZoneID="Main"/>
<Form Type="NewForm" Url="NewForm.aspx" WebPartZoneID="Main"/>
</Forms>



We have finished 80% work so far. Next we have one important thing and it's a little bit problematic: Automate filling the ID of current Invoice item to the Invoice ID field of detail when user enters new detail


This is where SharePoint Designer team comes to help :-). They wrote a blog dealing with problem and it's very helpful :-). Please go and check this blog before going on:
http://blogs.msdn.com/b/sharepoint/archive/2007/06/21/using-javascript-to-manipulate-a-list-form-field.aspx


Now we will employ a little bit JavaScript code to automate filling the value. We will put the javascript code to DispForm.aspx page in Invoice folder in Visual Studio.

<script type="text/javascript">

/////////////////////////////////////////////////////////////
// How they do that: http://blogs.msdn.com/b/sharepoint/archive/2007/06/21/using-javascript-to-manipulate-a-list-form-field.aspx
/////////////////////////////////////////////////////////////

_spBodyOnLoadFunctionNames.push("fillDefaultValues"); // Use JS SharePoint API to register onload event

function fillDefaultValues() {
// Parse the key/value from query string
var qs = location.search.substring(1, location.search.length);
var args = qs.split("&");
var vals = new Object();
for (var i = 0; i < args.length; i++) {
var nameVal = args[i].split("=");
var temp = unescape(nameVal[1]).split('+');
nameVal[1] = temp.join(' ');
vals[nameVal[0]] = nameVal[1];
}

// Assign ID query string to InvoiceID field
setTextFromFieldName("InvoiceID", vals["ID"]);
}

// @fieldName: the field display name
function setTextFromFieldName(fieldName, value) {
// If the webpart isn't in Insert/Edit Mode, the control doesn't exist so using try/catch to prevent error js icon on browser
try {
var theInput = getTagFromIdentifierAndTitle("input", "TextField", fieldName);
if (theInput != null) {
theInput.value = value;
}
}
catch (e) { }
}

function getTagFromIdentifierAndTitle(tagName, identifier, title) {
var len = identifier.length;
var tags = document.getElementsByTagName(tagName);
for (var i = 0; i < tags.length; i++) {
var tempString = tags[i].id;
if (tags[i].title == title && (identifier == "" || tempString.indexOf(identifier) == tempString.length - len)) {
return tags[i];
}
}
return null;
}</script>



From the JavaScript code, You see that we set the ID in query string to the field called "InvoiceID" (of course we can set the value on lookup field too (as original code showed).


Here is the whole picture of what we've done: when the DispForm.aspx run, the DataView web part look for server control with Id lblInvoiceDetailId and use the GUID to know where the Invoice Detail list. Then when user enters new record for detail, its Invoice ID field is filled automatically by javascript. That's interesting :-).

This is end of the post. Here is download link for the source of sample I presented

23 tháng 5, 2010

How to win friends and influence people

How to Win Friends and Influence People is well-known book for everyone who wants to improve their social skills (Vietnamese version). I read it but sometime it's not easy to remember and follow its ideas. Today I found a mind map picture that summarizes all aspects in the book and I think it would be helpful for other




From Internet

20 tháng 5, 2010

Use Feature to create custom list definition with custom fields and views

Recently I worked a SharePoint project and the client wanted to create some lists with their fields. On a list, they wanted to see items grouped by a particular one of their field. Obviously that the client needed custom list definition with fields and views. At first I thought this project would be not hard but it turned out that there are tricks that I need to know about creating custom list definition. So I'd like to note down those tips here to help out others who may be in trouble like me.

Assume that you looked at How to create custom list definition on MSDN.

1. Custom content type
There is a important note that people usually miss (me too :-) . Let say you create content type ABC which inherits from SharePoint standard Item content type (0x01), you must do two things in your content type definition and list definition
  • In content type ABC definition, remember to include fields which belong to the parent content type (Item) whereas adding custom fields for content type ABC
ContentTypeADefinition.xml

<Elements xmlns="http://schemas.microsoft.com/sharepoint/">
<ContentType ID="0x01008B7EF87D021E44fbBD83C21212A0B047"
Name="ABC ContentType"
Description="Custom ABC ContentType inherits from Item" >
<FieldRefs>
<!-- IMPORTANT: Remember to include fields that parent content type (Item for this case) uses -->
<Field ID="{fa564e0f-0c70-4ab9-b863-0177e6ddd247}" /> <!-- Title column is required by Item content type so here we include it in definition of ABC content type -->

<!-- Add custom fields here -->
<FieldRef ID="{7FD2A507-E6CB-4516-8C9A-C73E3C747524}" />
</FieldRefs>
</ContentType>
</Elements>
  • Do the same for list definition: include fields required by parent content type
schema.xml

<List xmlns:ows="Microsoft SharePoint" Title="Custom list"
Id="bc53ee2c-46b3-41ea-8deb-e75013c92eed" FolderCreation="FALSE"
Direction="$Resources:Direction;" BaseType="0">
<MetaData>
<Fields>
<!-- IMPORTANT: Add fields required by parent content type (Item) -->
<Field Name="Title" ID="{fa564e0f-0c70-4ab9-b863-0177e6ddd247}" DisplayName="$Resources:core,Last_Name;" Sealed="TRUE" SourceID="http://schemas.microsoft.com/sharepoint/v3" StaticName="Title"><!-- _locID@DisplayName="camlid1" _locComment=" " -->
</Field>

<!-- Add custom fields here -->
<Field ID="{7FD2A507-E6CB-4516-8C9A-C73E3C747524}" DisplayName="Item Name"
Name="ItemName" Type="Text" Group="Custom Columns" Required="TRUE"
SourceID="http://schemas.microsoft.com/sharepoint/v3" />
</Fields>

<!-- Other stuffs -->

</Metadata></List>

You can use SharePoint Manager to browse the SharePoint content types and its fields for getting field info easier.


2. Custom view
In order to define custom view, you probably need to know CAML and View schema . But there is another easier way to make it by making use of SharePoint Manager .
First, you create a custom view on desired list on SharePoint UI.



Second, after the view is created, you use SharePoint Manager to get the view definition by copy value of property HtmlSchemaXml in SharePoint Manager to schema.xml to make a new custom view. And there is one more important step





On the <View> element, make sure that those attributes are inserted

<View BaseViewID="2" Type="HTML" WebPartZoneID="Main" DisplayName="Custom view" SetupPath="pages\viewpage.aspx" ImageUrl="/_layouts/images/events.png" Url="CustomView.aspx" >

  • BaseViewID : this is an integer, make the attribute value uniquely with other View in schema.xml
  • WebPartZoneID="Main"
  • SetupPath : should be pages\viewpage.aspx so that the viewpage.aspx (this page is available in \\12\TEMPLATE\Pages\) is called when user uses your view

3. Use standard SharePoint forms in custom list

Normally, a custom list definition template created by WSS 3.0 Tools for Visual Studio usually has several files such as NewForm.aspx, DispForm.aspx, AllItems.aspx , ... And all those files are totally same as standard form in SharePoint. So if you don't have any plan to customize them, you can remove them from definition and tell SharePoint to use its standard form instead. The tip here is to add SetupPath attribute on <Form> element. Look at below xml fragment in schema.xml

schema.xml

<Forms>

<!-- I have a customized Display Form so that I keep the file DispForm.aspx in definition and SharePoint will called my customized DispForm.aspx when it renders Display Form -->
<Form Type="DisplayForm" Url="DispForm.aspx" WebPartZoneID="Main" />

<!-- For other forms, I don't change any on them so I use SetupPath attribute to tell SharePoint that calls its standard forms instead -->
<Form Type="EditForm" Url="EditForm.aspx" SetupPath="pages\form.aspx" WebPartZoneID="Main" />
<Form Type="NewForm" Url="NewForm.aspx" SetupPath="pages\form.aspx" WebPartZoneID="Main" />
</Forms>

4. Use Lookup field on list instance
You're creating two list definition called AL and BL. In AL you have a lookup field that references to Title field in BL. But the problem is that both AL and BL are custom lists and doesn't exist in site. It means you don't have List ID of BL required for lookup field.

SharePoint support another way to solve this problem by allowing user to put an relative URL on lookup field. How nice it is :-). On our AL and BL, the trick is to provide the correct URL of BL instance in lookup field definition in AL. And the safe way to ensure the correctness of URL is that you use <ListInstance> to create a new list instead of letting user do.

ListInstances.xml

<ListInstance Id="InstanceOfBL" Title="BL List" TemplateType="10010" Url="Lists/BLInstance01">

You can see that I instruct SharePoint to create a new instance of BL template with Lists/BLInstance01 URL. So I will provide this value into lookup list definition in AL and that solves our problem

schema.xml in AL

<Field Type="Lookup" DisplayName="Lookup Field 1" Required="FALSE" List="Lists/BLInstance01" ShowField="Title"
EnableLookup="TRUE" UnlimitedLengthInDocumentLibrary="FALSE" ID="{7FD2A507-E6CB-4516-8C9A-C73E3C747523}"
SourceID="http://schemas.microsoft.com/sharepoint/v3" Name="LookupField1" />


The List attribute is supposed to have a GUID but here we use relative URL (because the list instance haven't existed yet) and SharePoint accepts the url too.

Read more here

5. Create custom List Template as document library

If you're creating a document library as list template. Remember adding two attributes in the definition. Look at below xml fragment

<ListTemplate
Name="CustomDocLib"
DisplayName="Custom Document Library"
BaseType="1"
Type="101"
OnQuickLaunch="TRUE"
SecurityBits="11"
Sequence="1004"
DocumentTemplate="101" />

  • BaseType="1" : this attribute announces SharePoint that you're creating a document library, not a list. BaseType="1" means document library, BaseType="0" means list
  • DocumentTemplate="101" : this attribute makes the New Item menu item appear in New menu in toolbar. If you don't include this, you won't see New Item :-)
6. How to get the SharePoint item (SPItem) that is associated with the webpart editor in NewForm.aspx , EditForm.aspx and DispForm.aspx ?

There is sometime you need to customize editor forms so that you want to retrieve the SPItem which is processed within the form (i.e. to support for your business). E.x: every time user creates new item, you want to update the lookup field in other list that refers to newly created SPItem.

So in this case, we need a customized NewForm.aspx. Create NewForm.aspx (via Windows SharePoint Services 3.0 Tool) for the list definition. Then replace the code behind of NewForm.aspx with yours (it's created soon)

<%@ Page language="C#" MasterPageFile="~masterurl/default.master"
Inherits="MyAssembly.Features.NewCustomForm" %>


Then create new class called NewCustomForm that inherits from Microsoft.SharePoint.WebParPages.WebPartPage class

namespace MyAssembly.Features
{
public class NewCustomForm : WebPartPage
{
// This is the web part declared by default in NewForm.aspx
protected global::Microsoft.SharePoint.WebPartPages.WebPartZone Main;
}
}
}
}


Now we have a customized NewForm.aspx, before go on. Let I show you how we can retrieve the SPItem in editor first. Here the snipet code to get the SP item go with this web part and this code is going to put on our NewCustomForm class.

// By default, this page only has one webpart
// If user has customized it, we stop
if (this.Main.WebParts.Count == 1)
{
var webpartForm = this.Main.WebParts[0] as ListFormWebPart;
if (webpartForm != null)
{
var listItem = webpartForm.ItemContext.Item; // Got the SPItem going with the webpart editor
// do any thing here
// ...
}
}

You can put above code in Page_Load for example to get the item fields, or in Page_Unload to get newly created item (Note, because when a new item is saved, the page is redirected to Source param and it doesn't fire next events such as PreRender, Render so we have to use Unload event, another way is to override Dispose() method)

In short, by using ListFormWebPart.ItemContext.Item property, we can get the associated SPItem within editor and this property is very helpful in many scenarios.


Other tips

16 tháng 5, 2010

C# 4.0

Main reference at: http://msdn.microsoft.com/en-us/vcsharp/ff628440.aspx

The great feature introduced in C# 4.0 is dynamic programming which is also presented in MSDN lately. And some other new features:
  • Named and optional parameters
  • Variance
How named and optional parameter feature works

The C# compiler supports this feature and the runtime have no idea about named and optional parameter at all. The main idea is that the default value of parameter is transformed into attributes which is marked on parameter itself. And when the C# compiler see any invocation using optional or named feature, it will look into the source definition (method, delegate ...) and use info from attributes on optional parameter and then adjust the invocation again.

Below is the sample code using optional feature



And next picture is the disassembled code. It's observed that C# compiler 4.0 generated automatically Optional and DefaultParameterValue attribute on those parameters. And when any invocation to the method DoThing1() occurs, the C# compiler sees these attributes and it knows what value shoud be put to the missing parameter in the invocation.




In nutshell, how to play with covariance and contravariance

Look at below code snipe

Covariance
IList<Stream> streams = new List<FileStream>();


Contravariance
IList<FileStream> streams = new List<Stream>();

And the reason why this magic can happen are available on net. But I can understand it in this way: two these features allow us to cast generic interface or method to relative its subclass or superclass.

And dynamic

dynamic gives C# wings to make it more stronger. Before NET 4.0, C# is statically typed language. Now with Dynamic Language Runtime (a bunch of new API) and C# 4.0 compiler, you can use coding JavaScript style happily in C#.

Here is summary: dynamic is a type. That means I can declare a variable /property / parameter with type dynamic, return a type dynamic in method / property ... I'm even able to use dynamic with some operators like as , is . The distinct feature of dynamic type is that all operators / invocations on dynamic type isn't checked in compiled-time, it's done in runtime. That's all.

Reference for dynamic:

30 tháng 4, 2010

Opening Ceremony of Researching

I create this blog to share and discuss everything that I learn from researching .NET technology at my company (LinkedIn profile). I know this is the time to start new age, pretty interesting work in my career: researching and deployment.

This is the opening post for this remarkable moment. Thank for reading ^_^

Have a nice day