En el SDK de SharePoint existe un control llamado GroupedItemPicker. Este control se usa en las páginas AddFieldFromTemplate.aspx y AddContentTypeToList.aspx.
Ayer tuve que crear un WebPart y necesite usar la funcionalidad que brinda este control. Me fue casi imposible configurarlo pues no existe mucha documentación sobre el tema por tanto me ayudé del código de la propia página AddFieldFromTemplate.aspx para ver como estaba configurado e hice exactamente lo mismo.
<SharePoint:GroupedItemPicker id="Picker" runat="server" GroupControlId="SelectGroup" CandidateControlId="SelectCandidate" ResultControlId="SelectResult" AddButtonId="AddButton" RemoveButtonId="RemoveButton" DescriptionControlId="DescriptionControl" />
Como resultado recibí varias excepciones, de las cuales obtuve algunas experiencias. Lo primero que descubrí es que las propiedades del control (GroupControlId, CandidateControlId, ResultControlId, AddButtonId, RemoveButtonId, DescriptionControlId) tienen que referenciar a los controles indicados. GroupControlId, CandidateControlId y CandidateControlId tiene que ser IDs de controles de tipo SPHtmlSelect. AddButtonId y RemoveButtonId tienen que ser de tipo HtmlButton , la propiedad DescriptionControlId tiene que ser de tipo HtmlGenericControl (en código markup equivalente a un span) y además el control “DescriptionControl” no puede tener su InnerHtml vacío. Una vez que estaban establecidas las propiedades pensé que mis problemas habían terminado, hasta que una nueva excepción me hizo cambiar de opinión (NullReferenceExpection en el método PreRender de GroupedItemPicker ). Esta detonó mi paciencia y es cuando vino la desición de “recrear” el GroupedItemPicker. Para ello necesité del fichero GroupedItemPicker.js, presente en el directorio …\12\TEMPLATE\LAYOUTS y de algunas “artes oscuras”. Lo mas interesante es que cuando ya había terminado de crear mi propio control, el cual me resolvió el problema, decidí probar el de SharePoint y para sorpresa mía, no me dió la excepción. Acá les muestro el código que hace uso de GroupedItemPicker .
public abstract class DefaultLayoutPicker : WebControl { protected GroupedItemPicker picker; protected SPHtmlSelect candidateItem, selectedItems; protected HtmlButton btt_add, btt_remove; protected HtmlSelect select_groups; protected HtmlGenericControl description; protected override void CreateChildControls() { // candidateItem = new SPHtmlSelect(); candidateItem.ID = "candidateItems"; candidateItem.Multiple = true; Controls.Add(candidateItem); selectedItems = new SPHtmlSelect(); selectedItems.ID = "selectedItems"; selectedItems.Multiple = true; Controls.Add(selectedItems); select_groups = new HtmlSelect(); select_groups.ID = "selectGroups"; Controls.Add(select_groups); btt_add = new HtmlButton(); btt_add.ID = "bttAdd"; btt_add.Attributes["class"] = "ms-buttonheightwidth"; btt_add.InnerHtml = "Add >"; Controls.Add(btt_add); btt_remove = new HtmlButton(); btt_remove.ID = "bttRemove"; btt_remove.InnerHtml = "< Remove"; btt_remove.Attributes["class"] = "ms-buttonheightwidth"; Controls.Add(btt_remove); description = new HtmlGenericControl(); description.ID = "description"; description.InnerText = "Description:"; Controls.Add(description); picker = new GroupedItemPicker(); picker.AddButtonId = btt_add.ID; picker.RemoveButtonId = btt_remove.ID; picker.ResultControlId = selectedItems.ID; picker.GroupControlId = select_groups.ID; picker.CandidateControlId = candidateItem.ID; picker.DescriptionControlId = description.ID; picker.Clear(); FillItem(); Controls.Add(picker); base.CreateChildControls(); } protected override void Render(HtmlTextWriter writer) { string temp = @"<table width= '450px'> <tr> <td class='ms-authoringcontrols' colspan='3'>"; writer.Write(temp); select_groups.RenderControl(writer); temp = @" </td> </tr> <tr> <td valign='bottom' class='ms-authoringcontrols' style='padding-right: 10px'> </td> <td style='padding-right: 10px'> </td> <td valign='bottom' class='ms-authoringcontrols' style='padding-right: 10px'> </br> </td> </tr> <tr> <td class='ms-authoringcontrols' style='padding-right: 10px'>"; writer.Write(temp); candidateItem.RenderControl(writer); temp = @" </td> <td align='center' class='ms-authoringcontrols' valign='middle' style='padding-right: 10px'>"; writer.Write(temp); btt_add.RenderControl(writer); temp = @" </br> </br>"; writer.Write(temp); btt_remove.RenderControl(writer); temp = @" </td> <td class='ms-authoringcontrols' style='padding-right: 10px'>"; writer.Write(temp); selectedItems.RenderControl(writer); temp = @" </td> </tr> <tr> <td class='ms-authoringcontrols' colspan='3'>"; writer.Write(temp); description.RenderControl(writer); temp = @" </td> </tr> </table>"; writer.Write(temp); picker.RenderControl(writer); } protected abstract void FillItem(); public virtual IEnumerable<string> SelectdIds { get { this.EnsureChildControls(); return this.picker.SelectedIds.Cast<string>(); } } }
En el listado de arriba, se muesta como configurar el control, estableciendo las propiedades correctamente en el método CreateChildControl y luego se da el mismo layout que en la pagina AddContentTypeToList.aspx. Note que la clase es abstracta, cuyo único método a implementar es el método FillItems. En el siguiente listado se muestra una posible implementación para este método. Todas las referencias que aparecen a clases de CSS estan definidas en …\12\TEMPLATE\LAYOUTS\1033\STYLES\core.css.
public class WebooStudentsPicker : DefaultLayoutPicker { protected override void FillItem() { using (SPSite site = new SPSite(SPContext.Current.Web.Url)) { using (SPWeb web = site.OpenWeb()) { SPList userList = web.SiteUserInfoList; foreach (SPListItem item in userList.Items) { object grupo = item["Grupo"]; if (grupo is string) { string g = grupo as string; if (!string.IsNullOrEmpty(g)) picker.AddItem(item.ID.ToString(), item.Name, item.Name, g); } } } } } }
Es este caso se extraen los usuario de la lista “User Information List” (la cual tiene un propiedad adicionada “Grupo”), para proveer los elementos al control GroupedItemPicker . La linea señalada en negritas es la que adiciona los elementos.
Así es como quedó mi WebPart que usó el control DefaultLayoutPicker (dentro del área marcada en rojo)
Resumiendo…
Para trabajar con el control GroupedItemPicker, se deben tener en cuenta tres aspectos:
1- Establecer correctamente las propiedades del control.
2- Implementar un layout para mostralo (opcional).
3- Proveer los elementos mediantes el método GroupedItemPicker.AddItem.
Ola Raimil,
Quiero saber como salvar los elementos del groupeditempicker en el SPListitem?
puedes darme el codigo para salvar los elementos??
Hola Robert:
En el ejemplo , lo que hago es asignar un grupo de estudiantes a un ejercicio. El WebPart que se ve en la imagen es el siguiente:
[csharp]
public class AsignPeopleToOrientation : System.Web.UI.WebControls.WebParts.WebPart
{
WebooStudentsPicker studentPicker;
DropDownList dd_orientations;
Button btt_asignar;
[WebBrowsable(true)]
[Personalizable(true)]
[WebDisplayName("Nombre de la lista")]
public string OrientationList { get; set; }
State State { get; set; }
string message;
public AsignPeopleToOrientation()
{
State = State.Normal;
}
protected override void CreateChildControls()
{
base.CreateChildControls();
dd_orientations = new DropDownList();
FillOrientations(dd_orientations);
Controls.Add(dd_orientations);
studentPicker = new WebooStudentsPicker();
Controls.Add(studentPicker);
btt_asignar = new Button();
btt_asignar.Text = "Asignar";
btt_asignar.Click += new EventHandler(btt_asignar_Click);
Controls.Add(btt_asignar);
}
private void FillOrientations(DropDownList dd_orientations)
{
try
{
using (SPSite site = new SPSite(SPContext.Current.Web.Url))
{
using (SPWeb web = site.OpenWeb())
{
SPList list = web.Lists[OrientationList ?? "Orientaciones"];
foreach (SPListItem item in list.Items)
{
dd_orientations.Items.Add(new ListItem() { Text = item.Name, Value = (item.ID).ToString() });
}
}
}
}
catch (Exception exp)
{
State = State.ErrorConfig;
message = exp.Message + "<–>" + exp.StackTrace;
}
}
protected override void Render(HtmlTextWriter writer)
{
if (State == State.ErrorConfig)
{
writer.Write("Configure correctament el WebPart");
writer.Write(message);
return;
}
writer.Write("Seleccione una Orientación");
dd_orientations.RenderControl(writer);
writer.Write("</br>");
studentPicker.RenderControl(writer);
writer.Write("</br>");
btt_asignar.RenderControl(writer);
if (State == State.Asigned)
writer.Write("Estudiantes Asignados Correctamente");
}
void btt_asignar_Click(object sender, EventArgs e)
{
using (SPSite site = new SPSite(SPContext.Current.Web.Url))
{
using (SPWeb web = site.OpenWeb())
{
web.AllowUnsafeUpdates = true;
SPList list = web.Lists[OrientationList ?? "Orientaciones"];
SPList userInfoList = web.SiteUserInfoList;
SPListItem item = list.GetItemById(int.Parse(dd_orientations.SelectedValue));
object p = item["Participantes"];
SPFieldUserValueCollection participantes;
if (p == null)
participantes = new SPFieldUserValueCollection();
else
participantes = p as SPFieldUserValueCollection;
foreach (string studentId in studentPicker.SelectdIds)
{
int id = int.Parse(studentId);
participantes.Add(new SPFieldUserValue(web, id, userInfoList.GetItemById(id)["ImnName"].ToString()));
}
item["Participantes"] = participantes;
item.Update();
list.Update();
web.Update();
web.AllowUnsafeUpdates = false;
}
}
State = State.Asigned;
}
}
enum State
{
Normal,Asigned,ErrorConfig
}
}
[/csharp]
Es el código del método “btt_asignar_Click” el que debe de interesarte.
Espero que te sea util.
Hola,
A que puede ser debido que esté cargando los elementos en el control candidateItems pero al visualizar el control en pantalla no aparecen.
Si añado otro SPHtmlSelect desenlazado al GroupedItemPicker si aparecen.
Gracias