Tuesday, June 23, 2009

How to set a SharePoint web part's list at run time

Let's say you have a web part that must have a particular custom list you created in order for the web part to function. For instance, in the web part's CreateChildControls() routine, maybe you have some code similar to this:


'get the site the web part is running on
Dim oWeb As SPWeb
oWeb = Microsoft.SharePoint.WebControls.SPControl.GetContextWeb(Context)

'get the list we are going to work with
Dim oList As SPList
oList = oWeb.Lists("Contact List")

...other code that loops through oList and displays the different field values...

So in the above example, the required custom list is called "Contact List". You may run into a few problems with this:
  1. What if the required list doesn't exist?
  2. What if the list exists, but it doesn't have the fields needed by the web part?
  3. Every time you drop the web part on a page, it must have that list when it might be beneficial to have some flexibility with the list the web part is pointing to. A good example of this would be a site with a Marketing page and a Sales page. On the Marketing page, you want to display Marketing Contacts and on the Sales page you want to display Sales Contacts. Marketing Contacts and Sales Contacts are two separate lists.
My solution to this has a few steps.

First off, we need to create a custom property for your Contact List web part. Below is the code for the custom property.


Dim strContactListDataSource As String = ""

<WebBrowsable(True), Personalizable(True), FriendlyName("Contact List Data Source")> _
Property ContactListDataSource() As String
Get
Return strContactListDataSource
End Get
Set(ByVal value As String)
strContactListDataSource = value
End Set
End Property

So, the above code will add a new property called Contact List Data Source to the web part's properties you see when you click the web part in Edit Mode and click Modify Shared Web Part. Scroll down to the section Miscellaneous and you will see it. (Currently it is simply a textbox but it is possible to make it a dropdown list that lists available lists on the site).

Now, if we go back to the first code snippet above that gets the list, we just change it to reference our new property:


'get the site the web part is running on
Dim oWeb As SPWeb
oWeb = Microsoft.SharePoint.WebControls.SPControl.GetContextWeb(Context)

'get the list we are going to work with
Dim oList As SPList
oList = oWeb.Lists(ContactListDataSource)

...other code that loops through oList and displays the different field values...

While this makes my web part dynamic in terms of what list to look at, I still have a problem. What if the user just added the web part to a page? The property is going to be empty initially until the user sets it. So what does the user see? Or, what if the list the user specifies in the property does not exist or is an invalid list?

So, the next thing I do is create a function called ContactListIsValid() which will test to make sure the ContactListDataSource property the user specified is valid. If it is, the function will return true and the above code snippet will fire. If it is not valid, the function will return false and code will fire off that displays a message to the user telling them they need to set the ContactListDataSource property to a valid list.

First, you will see ContactListIsValid(), then below that I will show you how I use it for displaying error messages. Note that this function verifies the list exists and has the required fields.


Private Function ContactListIsValid() As Boolean
'PURPOSE: returns true if specified contact list is valid for the web part

Try
'get the site the web part is running on
Dim oWeb As SPWeb
oWeb = Microsoft.SharePoint.WebControls.SPControl.GetContextWeb(Context)

'get the list specified by user in property, if list is not found in site, flow will hit Catch
Dim oList As SPList
oList = oWeb.Lists(ContactListDataSource)


'check if fields exist in list

Dim blnContactNameFound As Boolean = False
Dim blnJobTitleFound As Boolean = False
Dim blnEmailAddressFound As Boolean = False
Dim blnPhoneFound As Boolean = False
Dim blnFaxFound As Boolean = False

'loop through the fields in the list and check to see if all the required fields are in the list
For Each oField As SPField In oList.Fields
If Not oField.Hidden And Not oField.ReadOnlyField And oField.Type <> SPFieldType.Attachments Then

If oField.Title = "Contact Name" Then
blnContactNameFound = True
End If

If oField.Title = "Job Title" Then
blnJobTitleFound = True
End If

If oField.Title = "Email Address" Then
blnEmailAddressFound = True
End If

If oField.Title = "Phone" Then
blnPhoneFound = True
End If

If oField.Title = "Fax" Then
blnFaxFound = True
End If

End If
Next

'if all fields were found, return true
If blnContactNameFound And blnJobTitleFound And blnEmailAddressFound And blnPhoneFound And blnFaxFound Then
Return True
Else
Return False
End If

Catch ex As Exception
Return False

End Try

Finally, below is the code on how I use this in CreateChildControls().


'if ContactListSource is a valid list, display controls
If ContactListIsValid() Then

Try
'get the site the web part is running on
Dim oWeb As SPWeb
oWeb = Microsoft.SharePoint.WebControls.SPControl.GetContextWeb(Context)

'get the list we are going to work with
Dim oList As SPList
oList = oWeb.Lists(ContactListDataSource)

...other code that loops through oList and displays the different field values...

Catch ex As Exception
Dim lblErrorMessage As New Label
lblErrorMessage.ForeColor = Drawing.Color.Red
lblErrorMessage.Text = "Error: " & ex.Message & _
"<BR><BR>" & "Details: " & ex.ToString
Controls.Add(lblErrorMessage)

End Try

Else
'else display message telling user they must configure the ContactListDataSource property
Dim lblConfigureMessage As New Label
lblConfigureMessage.ForeColor = Drawing.Color.Red
lblConfigureMessage.Text = "<BR> <BR>" & "You must configure the Contact List Data Source property. " & _
"<BR> <BR>" & "Please click Modify Shared Web Part -> Miscellaneous -> Contact List Data Source " & _
"and set it to a list based on the ContactListTemplate list template."
Controls.Add(lblConfigureMessage)
End If

A final thought. Notice the error message tells the user to set the property to a list based on the ContactListTemplate list template. I created a list template from the Contact List custom list, so that way the user can create their contact list from the list template to make sure they have all the necessary fields. To do this, I went to the List Settings of Contact List and selected Save As List Template.

Well, that's it! I wouldn't be surprised if people have their own way of doing all this, but I like to go for the simplest, most direct approach. I hope this helps!

Wednesday, June 17, 2009

How to create connected web parts: Part 1

While this may be lengthy, my hope is that this is made as easy as possible. I begin this with a high level overview of connected web parts (Part 1), followed by a step-by-step tutorial on creating connected web parts where all you need to do is click along and paste code (Part 2).

Let's say you have two web parts: one that takes a zip code and displays the weather for that zip code, and another web part that takes a zip code and displays movies playing in movie theaters in or near that zip code. They both require a zip code textbox with appropriate validation. Plus, what if you need to develop another web part that also requires a zip code and then displays other information based on that zip code?

Eliminate redundancy and welcome connected web parts! Instead of the above, we could make a zip code web part which acts as a provider, providing a zip code to another web part. Then our weather web part and movie web part could act as consumers, consuming a zip code fed to them by the zip code provider web part.

Essentially, this involves creating a zip code interface that all three web parts will understand. Then we make each web part, specifying in the code that the zip code web part is a provider web part while specifying the weather and movies web parts are consumer web parts.

Once the web parts and interface are built and deployed to a SharePoint site, a user can add the zip code web part to a page, add the weather web part to a page, then finally connect the two web parts via the Modify Shared Web Part option for either web part.

In Part 2 of this post is a tutorial on how to create Contact List/Email Contact connected web parts, along with entire code to be copy and pasted. I recommend following the tutorial to get the web parts functional, then look at the comments in the code to understand what everything is doing. Also, I will include a summary explaining the details of whats happening. Good luck!

Tuesday, June 16, 2009

RequiredFieldValidator in connected web parts

I plan on posting a tutorial on how to easily create connected web parts, but I wanted to write a short post on a unique issue I just came across, along with a simple solution.

Normally, when you add a RequiredFieldValidator to a form, it triggers when there is an attempted postback for that form. However, I have a scenario where I have two web parts, one called Contact List (this is the provider web part; it simply pulls data from a custom list library called Contact List and displays it) and another web part called Email Contact (this is the consumer webpart; it has a form where a user can enter their name, email, and message they want to email a contact). The Contact List web part provides a person while the Email Contact web part consumes a person. So when a user clicks a person in the Contact List, a postback is performed and the Email Contact web part will display that person's information above the form. A user could then enter their message, then click the Send button and it would email to the selected contact person.

Ok, so did you notice the problem yet? If the Email Contact web part has required field validators on its form fields (ie required on the submitter's name, email, and message), then when a user clicks a person in the Contact List web part, the requiredfieldvalidators in the Email Contact web part will prevent the postback from occurring, thus the person is never sent from the Contact List web part to the Email Contact web part. (Note: this scenario assumed that Contact List and Email Contact are on the same web page in the same WebPartZone).

The solution ended up being pretty simple. I am not going into the details of my specific implementation because the concept here is what it important (plus, I will post the code for this web part anyway in my article on connected web parts that I will do very soon). The solution is to use the ValidationGroup property of the RequiredFieldValidators and the Send button that saves/sends the message. To get this to work, I did the following:

In the Email Contact web part (all this is done in CreateChildControls):
1. create the RequiredFieldValidator controls for each field that needs it
2. add the RequiredFieldValidator controls to the web part's Controls collection
3. set their properties like ErrorMessage and ControlToValidate as you normally would
4. set their ValidationGroup property to "EmailContactValidationGroup"
5. when you create the form's button that saves/sends the email, set the button's ValidationGroup property also to "EmailContactValidationGroup"

The key here is to make sure the ValidationGroup is the same for the button and validators. This way, the validators will only fire off when the button is pressed!

Saturday, June 13, 2009

How to populate a custom Contact List from AD

In a recent project at work, I was required to create a custom Contact List. The only problem was that I didn't want my SharePoint admins to have to go to the list, then click New -> Item and have to sit there and enter in the person's name, email address, title, etc, when all this information is already sitting over in AD (Active Directory) to begin with.

So, I wanted to provide my SharePoint site admins with the ability to search AD, select a person (or people), then with the click of a button, add them into my Contact List, which is a custom list library. My solution ended up being quite straightforward - create a web part with two controls, 1. a PeopleEditor control (the standard SharePoint control for selecting people from AD; it has those two little buttons in the lower right for checking the name you enter and a browse button to let you do searches) and 2. a Button control which will take whatever people have been entered into the PeopleEditor and save them into the Contact List.

First off, this assumes you have created a custom list called "Contact List" on your SharePoint site. The fields are all text fields, called "Name", "Job Title", and "Email Address".

1. Load Visual Studio 2008
2. Click File -> New -> Project... -> Visual Basic -> SharePoint -> Web Part
3. For the name, I called mine AddContactFromAD
4. Notice the web part was named WebPart1. Do a search and replace across the whole project and change WebPart1 to AddContactFromAD wherever it is found. While that just changed the contents of the files in the project, you will notice there is still a folder called WebPart1 with files inside all named WebPart1 with different file extensions. Manually rename the folder and the base names of those files (leaving the extensions alone for each).
5. Copy and paste the code below.
6. In the Solution Explorer, right click the project and click Properties, click the Debug tab on the left, then in "Start browser with URL:" type in the name of the SharePoint site you want to deploy the web part to.
7. Hit F5 to build and deploy the web part.

Code for AddContactFromAD.vb:

1. under your inherits statement and above your constructor, copy and paste:

WithEvents btnAddUsers As New Button
Dim peADLookUp As New PeopleEditor

2. in CreateChildControls(), copy and paste:

'create and add message to web part
Dim lblMessage As New Label
lblMessage.Text = "Add contact(s) from Active Directory into Contact List"
Controls.Add(lblMessage)

'add PeopleEditor control to web part (it was created above)
peADLookUp.Rows = 1
Controls.Add(peADLookUp)

'add button to web part (it was created above)
btnAddUsers.Text = "Add user(s) to Contact List"
Controls.Add(btnAddUsers)

3. below CreateChildControls(), copy and paste the following subroutine.

Sub btnAddUsers_Click() Handles btnAddUsers.Click
'PURPOSE: add selected users to Contact List

'get the site the web part is running on
Dim oWeb As SPWeb
oWeb = Microsoft.SharePoint.WebControls.SPControl.GetContextWeb(Context)

'get the list where we want to save the person to
Dim oList As SPList
oList = oWeb.Lists("Contact List")


Dim strDisplayName As String = ""
Dim strEmail As String = ""
Dim strTitle As String = ""

'loop through each entity in peADLookUp (note DisplayName,
' Email, and Title are all properties in AD)
For Each account As PickerEntity In peADLookUp.ResolvedEntities
'get values of current entity
strDisplayName = account.EntityData("DisplayName").ToString()
strEmail = account.EntityData("Email").ToString()
strTitle = account.EntityData("Title").ToString()

'create a new list item in Contact List
Dim oNewItem As SPListItem
oNewItem = oList.Items.Add()

'populate the new list item with values from the form
oNewItem.Item("Name") = strDisplayName
oNewItem.Item("Job Title") = strTitle
oNewItem.Item("Email Address") = strEmail

'update the list with the new item
oNewItem.Update()
Next

End Sub

Welcome to Simple SharePoint Solutions!

Welcome to Simple SharePoint Solutions! The point of this site is the sharing of simple, easy to understand SharePoint solutions. Despite the fact that SharePoint has all kinds of wonderful built in functionality, we just about always have unique requirements that are not out of the box. In steps us SharePoint developers, but sadly, there are a lot of posts out there that are not as straightforward as most of us would like. So this site is dedicated to all of us who just want a simple, easy to understand solution to our SharePoint problems! I am actively working on several SharePoint projects and will be posting many more how-to's very soon, so please check in and give some feedback on how helpful these articles are.

D-J