I recently had the need to create a custom SharePoint application page (living in_layouts directory) that needed to display the BDC entity picker control, so that a user filling out this form could select a BDC entity instance, exactly like a user would in a business data column defined on a list. It took a while to figure it out, here are the steps to get it working:
Step 1 – Dissecting the ItemPicker control
The control that MOSS uses to render the BDC entity picker is called ItemPicker. You can add it to your aspx page by adding the following reference and code:
1 |
<%@ Register TagPrefix="Portal" Namespace="Microsoft.SharePoint.Portal.WebControls" Assembly="Microsoft.SharePoint.Portal, Version=12.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" %> |
1 |
<Portal:ItemPicker class="ms-input" ID="YourEntityPickerID" runat="server"/> |
Like the People Picker control (PeopleEditor), you have properties that you can set, such as AllowEmpty, MultiSelect, NoMatchesText, etc. However, there is one more property that you have to set, ExtendedData, that cannot be easily set in the aspx markup, so I create a handler in the markup for the Init event, and then set all the properties in code behind:
1 |
<Portal:ItemPicker class="ms-input" OnInit="YourEntityPickerID_Init" ID="YourEntityPickerID" runat="server" /> |
Step 2 – Setting ExtendedData
In order for the control to work properly, you have to set the ExtendedData property, using the ItemPickerExtendedData Class. This needs to be done via code, so be sure to create a reference to your control first, and handle the Init event of the control:
1 |
using Portal = Microsoft.SharePoint.Portal.WebControls; |
1 |
protected Portal.ItemPicker YourEntityPickerID; |
1 2 3 4 5 6 7 8 9 10 11 12 13 |
protected void YourEntityPickerID_Init(object sender, EventArgs e) { // Set extended data YourEntityPickerID.ExtendedData = GetExtendedData( Settings.Default.LOBInstanceName, Settings.Default.EntityName, Settings.Default.EntityDisplayNameColumn); // Set other properties YourEntityPickerID.AllowTypeIn = false; YourEntityPickerID.AllowEmpty = false; YourEntityPickerID.AutoPostBack = false; YourEntityPickerID.MultiSelect = false; } |
I’ve broken it out into a little helper method that takes in three parameters, the LOB Instance Name (name of your BDC LOB application instance), Entity name (the name of your Entity, e.g. "Customer"), and the entity display name column (the Name attribute in the TypeDescriptor of the column that acts as the display name column for your entity).
1 2 3 4 |
using Microsoft.Office.Server; using Microsoft.Office.Server.ApplicationRegistry; using Microsoft.Office.Server.ApplicationRegistry.MetadataModel; using Microsoft.Office.Server.ApplicationRegistry.Runtime; |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
private Portal.ItemPickerExtendedData GetExtendedData(string lobInstanceName, string entityName, string titleFieldName) { // Create a new ExtendedData object Portal.ItemPickerExtendedData data = new Portal.ItemPickerExtendedData(); // Get the LOB Instance LobSystemInstance lob = ApplicationRegistry.GetLobSystemInstanceByName(lobInstanceName); data.SystemInstanceId = lob.Id; // Get the entity Entity entity = lob.GetEntities()[entityName]; data.EntityId = entity.Id; // Set the primary column id (the id of the "Title" field) FieldCollection fields = entity.GetSpecificFinderView().Fields; List<int> secondaryColumnIds = new List<uint>(); foreach (Field field in fields) { if (string.Equals(field.Name, titleFieldName, StringComparison.OrdinalIgnoreCase)) { data.PrimaryColumnId = field.TypeDescriptor.Id; } else { secondaryColumnIds.Add(field.TypeDescriptor.Id); } } data.SecondaryColumnsIds = secondaryColumnIds.ToArray(); return data; } |
The important things happening here are:
- Setting the SystemInstanceId (this marries the control to a particular BDC application).
- Setting the EntityId (this marries the control to a particular entity).
- Setting the PrimaryColumnId (points the control to the TypeDescriptor that acts as the Identity column)
- Setting the SecondaryColumnIds (points the control to the TypeDescriptors for all the other columns you need to bring in).
Step 3 – Consuming the Data
To consume the data, perform some validation, and then try to get a PickerEntity object out of the Entities property of the control:
1 2 3 4 5 6 7 8 9 10 |
YourEntityPickerId.Validate(); if (YourEntityPickerId.Entities.Count <= 0) { SPUtility.TransferToErrorPage("You must pick an Entity."); } PickerEntity bdcEntity = (PickerEntity)YourEntityPickerId.Entities[0]; if (bdcEntity == null || !bdcEntity.IsResolved || bdcEntity.EntityData == null) { SPUtility.TransferToErrorPage("You must pick an Entity."); } |
Once you have a PickerEntity, you can get the ID of the entity instance via the Key property, the display name via the DisplayText property, and also get all the other column data via the EntityData property.
Note that the .Key property is the encoded ID of the entity instance (you can use EntityInstanceIdEncoder to encode/decode this ID). Note also that the EntityData comes back as a HashTable of TypeDescriptor ID/value pairs, so you can’t index into this with a friendly column name. You’ll have to traverse the Entity and its fields, get the TypeDescriptor ID of the column you are interested in, and then use that to index into the hash table. That can be a pain, so it’s almost easier to simply grab the .Key, decode it, and then call FindSpecific() on your Entity.
If you want to pre-populate the entity picker, check out this article:
http://www.myfatblog.co.uk/index.php/2009/07/pre-populating-the-bdc-item-picker-in-an-application-page/
For SharePoint 2010, and the BCS ItemPicker, see the following article:
http://blog.brianfarnhill.com/2012/09/04/using-the-bcs-picker-in-a-custom-application-page
Added to my bookmarks 🙂
nice job
Thanks, great post!
Did you ever try this out with multiselect enabled on the picker? If I do this, the selected keys in the picker dialog are encoded. Kind of “__bg40005300; __bg40008300; __bg800013002300”. How to show the keys in plain text on the dialog?
Thx 4 help 🙂
Hey Andrew,
You’re right, I got the same result for multiselect, and I wasn’t able to figure out how to get around that.
I even poked around with Reflector on the Business Data Filter web part from MS. It uses the BDC picker on a web form to select an entity, but it doesn’t allow for multi-select.
Luckily I didn’t need that.
List secondaryColumnIds = new List ();
Type or Namespace List Could nnot Be found
What went wrong here for me
Add:
using System.Collections.Generic
Hi,
i am trying this code. For some reason The browse button is disabled on the control rendered in my custom webpart for item picker, and even if I enter the data it says no exact match found.
But I have validated the entity by creating an external data column, where it works.
Any pointers on troubleshooting this?
Thanks