Wednesday, September 23, 2009

Understanding SharePoint’s “ddwrt:DataBind” syntax

SharePoint takes a lot of criticism from developers but it is really impressive what you can do without writing backend code. In my last large SharePoint implementation, I was customizing some input fields for a form using SharePoint Designer.  I found myself looking at the XSLT for a DataFormWebPart wondering how to decipher the auto-generated _designer:bind attribute.

<SharePoint:FormField runat="server" id="ff1{$Pos}" 
    ControlMode="Edit" FieldName="Field1"
    __designer:bind="{
        ddwrt:DataBind(
            'u', 
            concat('ff1',$Pos), 'Value', 'ValueChanged'
            'ID', ddwrt:EscapeDelims(@ID), '@Field1'
        )}"
    />

Strangely, the method ddwrt:DataBind is absent from what is probably the best online overview of the ddwrt XSLT extension object.  So I threw the question onto Stack Overflow to see if any bright people could shed light on the mysterious method.  Not surprisingly, no one could answer the question.  So I decided to do some spelunking in Reflector to see what I could find.
I opened reflector, added a reference to Microsoft.SharePoint then opened the search window.  Using the “Member Search” (tip: CTRL+M) I searched for "DataBind" and found the internal class, Microsoft.SharePoint.WebPartPages.DataFormDdwRuntime.  Some parts of this assembly are obfuscated, but by guestimation this is likely the extension object that is available to us in the XSLT under the namespace "ddwrt".

Oddly enough, the DataFormDdwRuntime.DataBind method returns an empty string, so it’s likely that the “__designer:bind” attribute is just a dummy attribute for SharePoint Designer, if at all.  Remember that curly braces inside an attribute is an XSLT expression, so the computed result is actually __designer:bind=””.  The real magic is that what the DataBind method actually does.

Behind the DataBind Method

Turns out that the DataBind method is basically a wrapper to the DataFormWebPart method, AddDataBinding.  This method has the following signature:

public override void AddDataBinding(string op,
                                    string controlId,
                                    string propertyName,
                                    string eventName,
                                    string keyField,
                                    string keyValue,
                                    string dataField
                                    ) 

As the name implies, it creates a data binding and associates it with the hosted WebPart, but the msdn documentation leaves us guessing as to what values should be used.  Reflector provides us some guidance.

Note:  keyField, keyValue and dataField all refer to methods that are obfuscated.  The detail below for these fields is my best guess, so take it AS-IS with no warranty.  As always, your comments, suggestions and feedback are welcome.

Operation Type (“op”)

This is the type of operation that will be used when the form is submitted. There are three possible values:  "i” (Insert), “u” (Update") and “d” (Delete).  The logic falls back to “update” if it doesn’t recognize the value passed in.

Bound Control (“controlId”)

This the id of the control that will be will be bound to the SharePoint list item.  The auto-generated value attempts to duplicate the value of the “id” attribute using an xslt function concat.  You can use your own format, but the chosen format for the id’s are “ff + field index + record index” where the field index is an arbitrary number, usually the order in which the fields appear in the designer (1,2,3, etc), and the keyword $Pos is an XSLT variable defined earlier in scope, referring to the position() of the row in the result set.

Bound Control Property (“propertyName”)

This is the property on our bound control that will ultimately be used to represent the SharePoint field value.  For SharePoint FormField controls, this property is “Value”.  Most ASP.NET controls can be used, assuming that the propertyName and eventName are properly set.

Bound Control Event (“eventName”)

This the event on the bound control that you want to be handled when the form posts information back to the server.  How this works is really interesting: it uses reflection to get the Event of your control, then binds the postback event to the DataFormWebPart's HandleChangedField method.  This method essentially fetches the value of your control (via the PropertyName) and then tracks that the field has changed or needs to be inserted.

For SharePoint FormField controls, this event is “ValueChanged”.

Data Source Primary Key (“keyField”)

Based on examples, the “key field” refers to the attribute in the dsQueryResponse XML which represents the primary key for this record.  This is likely always going to be "ID".  (ie, /dsQueryResponse/Rows/Row/@ID)

Data Source Primary Key Value (“keyValue”)

The value of the primary key, “ID”.  The ddwrt:EscapeDelims method is used to convert certain characters (,=;'{}") to a SharePoint unicode string equivalent (ie, “,” is converted to “_x002C_”).  For SharePoint Designer XSLT, this is likely always going to be ddwrt:EscapeDelims(@ID).

Data Source Field Name (“dataField”)

This is the name of the SharePoint field that we want to bind.  Since we’re pulling this value from the dsQueryResponse XML, it’s represented as it appears in the dsQueryResponse XML.

Some Examples

Updating a SharePoint text field using an ASP.NET TextBox:

<asp:TextBox 
    runat="server" 
    id="myTextBox{$Pos}"
    Text="{@Field1}" 
    Value="{@Field1}"
    __designer:bind="{ddwrt:DataBind(
                'u', 
                concat('myTextBox', $Pos),
                'Text',
                'TextChanged',
                'ID', 
                ddwrt:EscapeDelims(@ID),
                '@Field1')}" />

Insert a SharePoint text field as a ASP.NET DropDownList:

<asp:DropDownList 
    runat="server"
    id="myDropDown{$Pos}"
    __designer:bind="{ddwrt:DataBind(
        'i',
        concat('myDropDown', $Pos),
        'SelectedValue', 
        'TextChanged',
        'ID',
        ddwrt:EscapeDelims(@ID),
        '@Field2'}" />

Conclusion

If you’re using the auto-generated XSLT, the default format works fine.  However, if you want to craft the markup by hand or roll your own customizations, understanding this syntax is helpful.

15 comments:

  1. Nice job, Bryan!

    Hey, I've got a customized NewForm2.aspx, and am looking at this line in the xslt.

    SharePoint:FormField runat="server" id="ff2{$Pos}" ControlMode="New" FieldName="Body" __designer:bind="{ddwrt:DataBind(
    'i',
    concat('ff2',$Pos),
    'Value',
    'ValueChanged',
    'ID',
    ddwrt:EscapeDelims(string(@ID)),
    '@Body')}"

    Ideally, I want to be able to suppress the original content from the body whenever someone replies.

    My first thought was to replace ddwrt:EscapeDelims(string(@ID)) with empty string ''. That didn't get me too far.

    Any suggestions?

    Cheers!

    Mark

    ReplyDelete
  2. Mark,

    Not sure I follow your meaning by whenever someone replies but if you want to suppress the body, you're probably best to let the XSLT do that.

    ie -

    <xsl:if test=".....condition....">
    <xsl:value-of select="@Body" disable-output-escaping="true" />
    </xsl:if>

    ReplyDelete
  3. Hi Bryan
    thanks for this great post
    I've searched a lot for this issue and found nothing till I found your post
    thanks

    ReplyDelete
  4. Hi Bryan,

    obviously late to the post, but this is more a philosiphical sharepoint developer question than specifically to do with "ddwrt:DataBind" and as a relative newcomer to this who shooting-match its this -

    I came across your blog which helped me out a huge amount whilst looking for info on the "ddwrt:DataBind" syntax - now this syntax is surely something that every sharepoint developer sees every single day they are developing - yet as far as I can see there is almost nothing out there which details this core part of the sharepoint system

    Is this becuase;

    1. fiddling with this is actually the 'wrong way' - as far as there is ever a wrong way - to get things done?

    2. alpha-sharepoint-developers who have worked it out dont want to share the inner secrets...

    3. There is a better way to get things done client side than fiddling with ddwrt:DataBind?

    Its all part of the Big Sharepoint Conspiracy to drive a sane developer nuts?

    Any thoughst greatly apprecaited!

    All the best

    ReplyDelete
  5. I have to say that this statement "but it is really impressive" is, well, still a matter of debate, no? Regardless, I thank you for your efforts. -- Mark Kamoski

    ReplyDelete
  6. I know this is an old post but I hoped you might know why the DataBind event doesn't detect when you change the field from JavaScript. For example if you modify the selection of a DVDropDownList through JavaScript, the ddwrt:DataBind function doesn't pick up that change. Please help!

    ReplyDelete
  7. @jabit - the ddwrt:databind is for the server control events which are triggered on postback. You may need to trigger the postback event from javascript in order to leverage client-side changes.

    ReplyDelete
  8. I get how this works in a DVWP, but what about a joined (linked) data source? I cant get this to work... i can view the data, but trying to update it produces an error. I'm thinking i have to specify which list the item that i want to update belongs to... I dont know how to do that and it doesnt look like any of these parameters hold a list id...

    ReplyDelete
  9. I'm able to get the values but it's adding "string;#" before the value from the DVWP. I was going to attempt JS but read jabit's entry that doesn't work. Any ideas how to fix?

    ReplyDelete
  10. @TeddyBearDog It's been a while since I've been in the SharePoint space (being doing a lot of WPF and mobile the last few years) but I have seen this syntax coming from SharePoint before. Certain SharePoint fields have complex structure and express their raw values with these delimiters (SPFieldUrlValue, SPFieldUser are a few good examples).

    You'll either have to adjust your query to return the values you want, or use some string parsing in the Xslt to string out the values you don't want. Note that Microsoft's Xslt is based on 1.0 so there isn't a simple Replace function...

    ReplyDelete
  11. Nice Article, Bryan! I am using Sharepoint 2010 . I created the custom Edit Form in sharepoint designer for List "Customer". "Customer" has lookup column "Area"
    I applied Filter on Dropdown list of "Area". but when I open Edit form of customer list Area is not selected Properly it the first Item of the "Area" Dropdown shows like that "alladin Clinic" how could I get the selected Value?

    ReplyDelete
  12. Thanks a lot. This helps to understand the __designer:bind

    ReplyDelete
  13. Hi Bryan,

    I have 2 dropdown fields and would like to filter the 2nd one based on the selection on top. Is it possible to do it? Appreciate any help on this !

    Thanks !

    Sam

    ReplyDelete
  14. Nice job man, great info.

    ReplyDelete
  15. I am using SharePoint Designer 2010, and I have an asp:textbox that is bound to a SharePoint External List (SQL table). I want the default of this textbox to be the user who is editing that record, which I found a way to do, however when the user clicks the Save button, this value does not save back to the list or SQL table. Do you have any suggestions?

    Thank you.

    ReplyDelete