Social Icons

twitter google plus linkedin rss feed

Pages

8.9.11

Filter Provider Web Part with a Silverlight Tree in a ModalDialog window

I started with this a couple of days ago… I didn’t want to do it, because I knew it was going to be painful… But they made me… An then I thought it would be a perfect post for the blog because of the painful.
What we wanted to achieve was to filter the items in a List View Web Part by Entity. We have an entity hierarchy, so we thought it would be a good idea to have a web part showing the hierarchy as a tree.

Alright so we need web part with a TreeView that is able to send the selected entity as a filter to a LVWP. Great. I built it… And no-one liked it. They wanted it to be Silverlight, and not only that, they wanted it to be in a pop up window. Yes, they got me. I have never done anything like that. But hey, where’s your spirit of adventure?

It is a lot of code, most of it ugly. So I’ll only post the most tricky parts (basically the parts related to the communication of the pages and the Silverlight) and the URL’s from where I got the ideas.

The firs thing is to get a proper Filter Provider Web Part working. To do so, I followed the instructions from here. My first try was with IWebPartRow, but it wasn’t what I was looking for, so I went for ITransformableFilterValues. It’s pretty straightforward so I won’t comment anything here.
The second thing is to create the PopUp Window. After googling a while I found this post a good point to start. My code in the web part ended like this:
        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);
        }

I had problems retrieving the values from the ModalDialog. I was not able to find the controls in the web part because the IDs and the Titles of the were being dynamically. The trick I used to fix this was to create two hidden fields in the CreateChildControls:
        protected override void CreateChildControls()
        {
            base.CreateChildControls();

            ...

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

            ...
        }

Hey Chan, have you noticed that you could use the session and forget about the HiddenFields? Don’t ask. I’m warning you…

The next part is to get the new aspx page for the modal dialog. I created a new directory in the layouts folder and created my page there. It looks like this:
<%@ 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>

I have also created a code behind class as you can see in the first line of the 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;
        }
    }

What I am doing here is getting the parameters and passing them to the Silverlight. I use the CurrentWeb to tell the web services I use in the Silverlight what’s the context and the SelectedEntityID to highlight the previously selected Entity in case it’s not null. This is the code of the 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);
        }
    }
}

Now we have all the components.

The way it works is: You chose an entity in the Silverlight tree, then the Silverlight calls the javascript function in the modal window that returns the parameters to the parent web part window and closes the ModalDialog. And then the web part sends the filter to the List View Web Part.


If you try to use this code you’ll be missing some methods, but what I wanted to share here is the solution I've used mainly because I don’t want to think about this again if I’m asked to do something similar in the future.



I would love to share some pics but the web parts haven’t been retouched by the skilled hand of Adam, our designer, and your eyes could pop out of their sockets.