Social Icons

jueves, 8 de septiembre de 2011

Web Part Proveedor de Filtro con un árbol Silverlight en un ModalDialog

Empecé con esto hace un par de días... No quería hacerlo, porque sabría que iba a ser doloroso... pero me obligaron... Y entonces pensé que sería un post perfecto para el blog por lo de painful.

Lo que queríamos conseguir era filtrar elementos en un List View Web Part por Entidad. En nuestra solución tenemos una jerarquía de entidades por lo que pensamos que sería buena idea que el web part mostrase la jerarquía como un árbol.
Bien, así que necesitamos un web part con un TreeView que sea capaz de mandar la entidad seleccionada como filtro a un LVWP. Fantástico. Lo hice... Y no le gustó a nadie. Lo querían en Silverlight y no solo eso, lo querían en una ventana pop up. Sip, me cogieron, nunca había hecho nada parecido pero, ¿Quién dijo miedo?

Es un montón de código, la mayor parte feo así que solo postearé la parte interesante (básicamente la parte relacionada con la comunicación entre las páginas y el Silverlight) y las URLs de donde cogí las ideas.
Lo primero es poner a funcionar un Filter Provider Web Part. Para ello seguí las instrucciones de aquí. Primero lo intenté con un IWebPartRow, pero no era lo que yo quería así que cambié a ITransformableFilterValues. Esta parte es bastante simple así que no comentaré nada más.
La segunda parte es crear una ventana emergente. Después de googlear si googlear te parece un palabro raro deberías escucharme decir overridar o rollupear un rato me enconté con este post, que me pareció un buen sitio para empezar. Mi código en el web part terminó así:

        protected override void OnLoad(EventArgs e)
        {
            if (Page.IsPostBack)
            {
                if (!string.IsNullOrEmpty(GetFormValue("HiddenEntityName")))
                {
                    Page.Session["SelectedEntityName"] = Page.Request.Form["HiddenEntityName"];
                    Page.Session["SelectedEntityID"] = Page.Request.Form["HiddenEntityID"];

                    RenderHeader();

                    //SelectedEntityText.Text = string.Format("{0}  ", Page.Request.Form["HiddenEntityName"], Page.Request.Form["HiddenEntityID"]);
                }
                else
                    RenderHeader();
            }

            string CurrentWeb = SPContext.Current.Web.Url;
            string height = "500";
            string width = "500";
            string page = "/_layouts/stratex/EntityTree.aspx";

            // use next line for direct with  between  and  
            string scrp = @"
                            ";

            Type t = this.GetType();
            if (!Page.ClientScript.IsClientScriptBlockRegistered(t, "bindWebserviceToAutocomplete"))
                Page.ClientScript.RegisterClientScriptBlock(t, "bindWebserviceToAutocomplete", scrp);
        }
Tuve problemas trayéndome los valores del ModalDialog. No era capaz de encontrar los ID ni los Titles de los controles porque son creados dinámicamente. El truco que usé fue crear dos campos ocultos en el CreateChildControls:
        protected override void CreateChildControls()
        {
            base.CreateChildControls();

            ...

            Page.ClientScript.RegisterHiddenField("HiddenEntityName", "");
            Page.ClientScript.RegisterHiddenField("HiddenEntityID", "");

            ...
        }
Oye Chan, ¿Te has dado cuenta que podrías usar solo la sesión y olvidarte de los HiddenFields? No preguntes, te lo advierto…
La próxima parte es crear la página aspx para el modal dialog. La guardé en un directorio que me creé en _layouts. El código de la página quedó así:
<%@ Page Language="C#" Inherits="StratExFramework.EntityTree,StratExFramework,Version=2.2.0.0,Culture=neutral,PublicKeyToken=311246df7412ca98" %>

<html>
<head>
<title>Select Entity for filtering</title>
<script type='text/javascript'>
    function PassParameterAndClose(EntityName, EntityID) {

        window.returnValue = new Array( EntityName, EntityID) ;

        var version = parseFloat(navigator.appVersion.split('MSIE')[1]);
        if (version >= 7) 
            { window.open('', '_parent', ''); }
        else
            { window.opener = self; }

        window.close();
    }
</script>
</head>
<body></body>
</html>
También me creé una clase de code behind como puedes ver en la primera línea del aspx... :
    public class EntityTree : WebPartPage
    {
        string CurrentWeb;
        string SelectedEntityID;

        protected void Page_Load(object sender, System.EventArgs e)
        {
            CurrentWeb = Request.Params["CurrentWeb"];
            SelectedEntityID = Request.Params["SelectedEntityID"];
        }

        protected override void CreateChildControls()
        {
            base.CreateChildControls();

            string Source = CurrentWeb + "/Lists/XAPLibrary/SilverlightEntityTreeSelector.xap";
            string SilverlightHeight = "515";
            string SilverlightWidth = "500";

            LiteralControl obj = new LiteralControl();
            obj.Text = "<object id='silverlightHost' style='height: " + SilverlightHeight + "; width: " + SilverlightWidth + @"; margin: 0; padding: 0;' data='data:application/x-silverlight-2,' type='application/x-silverlight-2'>
                            <param name='Source' value='" + Source + @"' />
                            <param name='MinRuntimeVersion' value='3.0.40624.0' />
                            <param name='Background' value='#FFFFFFFF' />
                            <param name='initParams' value='" +
                                string.Format("{0}={1}", "site", HttpUtility.UrlEncode(CurrentWeb)) +
                                string.Format(", {0}={1}", "selectedentityid", HttpUtility.UrlEncode(SelectedEntityID)) +
                                @"' />
                            </object>";
            this.Controls.Add(obj);
        }

        public override void VerifyRenderingInServerForm(Control control)
        {
            return;
        }
    }
Lo que hago aquí es coger los parámetros del entorno en donde se ejecuta el web part y pasárselos al Silverlight. Uso el CurrentWeb para decirle a los web services del Silverlight cual es el contexto y el SelectedEntityID para resaltar la entidad que fue seleccionada la última vez que se abrió la ventana modal. Este es el código del Silverlight.
namespace SilverlightEntityTreeSelector
{
    public partial class MainPage : UserControl
    {
        public MainPage()
        {
            InitializeComponent();

            Tree.PropertyChanged += new System.ComponentModel.PropertyChangedEventHandler(Tree_PropertyChanged);

            Tree.Show(string.Empty);
        }

        void Tree_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
        {
            if (e.PropertyName == "SelectedEntityID")
                HtmlPage.Window.Invoke("PassParameterAndClose", Tree.SelectedEntityName, Tree.SelectedEntityID);
            else if (e.PropertyName == "TreeLoaded")
                if (Application.Current.Resources.Contains("selectedentityid"))
                    if (!string.IsNullOrEmpty(Application.Current.Resources["selectedentityid"] as string))
                        Tree.ChangeSelectedItemTo(Application.Current.Resources["selectedentityid"] as string);
        }
    }
}

Ya tenemos todos los componentes.
Esto funciona de la siguiente manera: Seleccionas una entidad en el árbol de Silverlight, luego el Silverlight llama a la función javascript de la ventana modal y que pasa los parámetros al web part padre y cierra el popup. Finalmente el web part manda el filtro al List View WebPar.
Si usas este código te faltarán algunos métodos, pero lo que quería compartir aquí es el método que he seguido para conseguirlo básicamente porque no quiero tener que volver a pensarlo si alguna vez me vuelven a pedir que haga algo parecido en el futuro.



Me encantaría enseñaros algunas fotos, pero los web parts no han sido retocados por las hábiles manos de Adam, nuestro diseñador, y se os podrían salir los ojos de las órbitas.

No hay comentarios:

jueves, 18 de agosto de 2011

Creando una aplicación Windows Forms en SharePoint 2010

En mi tónica habitual, creo que me he tropezado con cada posible problema antes de conseguir ejecutar mi aplicación.

Lo que en MOSS2007 era todo risas y alegría se ha convertido en SharePoint 2010 en una tarea que me llevó por lo menos una hora… Y tuve suerte porque en mi tónica habitual, otra vez he llegado bastante tarde a 2010 y la mayoría de los problemas han sido ya resueltos y comentados en los foros…

Bueno, contaré la experiencia, así no se me olvidan los pasos necesarios la próxima vez con un poco de suerte.

Añadí la Microsoft.SharePoint.dll como siempre a las referencias y empecé a teclear. Escribí mi aplicación de tres lineas (sólo quería una aplicación para ayudarme a programar otra cosa) y le di a F5.

“The type or namespace name 'SharePoint' does not exist in the namespace 'Microsoft' (are you missing an assembly reference?)”

Bien, no se me había olvidado nada, el problema era que tienes que apuntar la solución al .Net Framework 3.5…

image

Esa fue fácil. Había un par de sitios en donde puedes encontrar esa información. Vale, F5 otra vez.

“The Web application at http://localhost could not be found. Verify that you have typed the URL correctly. If the URL should be serving existing content, the system administrator may need to add a new request URL mapping to the intended application”


- ¡Oh venga ya! ¡Está ahí! ¿Por qué no lo encuentras? + Porque no me has apuntado a 64 bits y no estoy funcionando en modo nativo. - Ups, perdona. + No pasa nada, solo tienes que leer mis mensajes de error que te indicarán perfectamente lo que tienes que hacer.
image

Esta fue un poco más difícil de encontrar, pero después de cambiar eso, le di felizmente a F5 otra vez y… ¡Sorpresa!

“The Web application at http://localhost could not be found. Verify that you have typed the URL correctly. If the URL should be serving existing content, the system administrator may need to add a new request URL mapping to the intended application”


Si, el mismo error otra vez… Y ya no encontraba más información en internet… ¿Y si es un problema de permisos?
image

Arranqué el Visual Studio como administrador y funcionó. ¡Por fin!

No hay comentarios: