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
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