ASP.NET AJAX och SharePoint / MOSS 2007

I onsdags medverkade jag på Innovation Day här på Microsoft, en seminariedag riktad mot ISV:er som handlade om nyheterna i SharePoint och MOSS 2007. Jag höll ett kortare föredrag om ASP.NET AJAX och berättade lite om vilka utmaningar och möjligheter som finns i dagsläget när det gäller att använda vårt AJAX-ramverk tillsammans med SharePoint-plattformen.

SharePoint stödjer i dagsläget inte all funktionalitet i ASP.NET AJAX och det är framförallt serverkontrollen UpdatePanel som inte fungerar utan viss handpåläggning. Vårt SharePoint Team har skrivit en mycket bra blogpost om detta: https://sharepoint.microsoft.com/blogs/mike/Lists/Posts/Post.aspx?ID=3 . De goda nyheterna är att detta kommer åtgärdas i och med ServicePack 1 för SharePoint, då fullt stöd för alla delar i ASP.NET AJAX har utlovats.

Om det råkar vara så att du såg mitt föredrag, eller kommer att se det på MSDN-TV i efterhand: när jag talade om AJAX-ramverket och kompatibilitet så sa jag att jag trodde att även Opera stöddes, även om den inte fanns med  i listan över webbläsare som vi stödjer. Jag skulle vilja förtydliga det genom att citera Scott Guthrie:

"FireFox, Safari and IE work for all scenarios.  Opera is now supported for almost all scenarios - although there are a few edge cases with more advanced scenarios (for example: dynamically nested updatepanels, etc) where Opera lacks some JavaScript browser features to support these scenarios.
We'll be putting out a more detailed whitepaper that details these scenarios, so that you can know if anything you are doing will run into issues.
All of the AJAX Control Toolkit controls also now support Opera, so you should be able to use them with Opera as well (please report any issues you find with them)."

När jag valde scenario för den demo jag skulle hålla tänkte jag att det kunde vara lämpligt att visa hur man bygger ut en befintlig sökfunktion i en SharePoint-sajt med den AutoComplete Extender-kontroll som följer med ASP.NET AJAX Toolkit. En Extender-kontroll är en kontroll som tillför någon form av AJAX-funktionalitet till en befintlig ASP.NET-kontroll. AutoComplete-kontrollen utökar en vanlig TextBox med funktionalitet för att anropa en webservice för att få förslag på sökresultat i takt med att söktexten skrivs in i textfältet. Ett utmärkt exempel på detta finns här: https://quotiki.com/

I den färdiga demo-sajt som jag tänkte använda fanns det en sökfunktion som använde sig av Microsoft.SharePoint.Portal.WebControls.SearchBoxEx - en av de standard-WebPart's för sökning som följer med SharePoint. Hur enkelt som helst vid en första anblick - bara att ärva från denna, göra override på de metoder som krävs och haka på AutoComplete Extender:n på den - det här blir en promenad i parken, tänkte jag och plockade fram Luz Roeder´s .NET Reflector för att se hur kontrollen var implementerad.

Naturligtvis var det inte så enkelt: eftersom de inbyggda kontrollerna innehåller funktionalitet som är tajt integrerad med Sharepoints speciella sätt att rendera sidor och den koden är skyddad med DotFuscator (duh!), vilket gör det i princip omöjligt att se hur den är implementerad, så lyckades jag aldrig få den approachen att fungera.

Det jag gjorde var istället att bygga en egen WebPart, vilket är mycket smidigt med hjälp av Visual Studio Extension för Sharepoint, som än så länge endast finns tillgänglig i en CTP-version. Med denna får du tillgång till en särskild Visual Studio-mall för SharePoint Webpart's:

 Sharepoint VS-extension

Använder du denna mall får du ett projekt skapat med de referenser och namespace-angivelser som behövs, samt en klass skapad som ärver från System.Web.UI.WebControls.WebParts.WebPart och som från början endast innehåller en tom Render-metod. Det som är smidigt är även att du även får en ny flik i Visual Studio (under Project -> Properties -> Sharepoint) för att ange den konfigureringsinformation som behövs för din WebPart, inget mer pillrande i .webpart eller .dwp-filer alltså. Den genererar även en GUID-identifierare åt dig automatiskt så du slipper tänka på det:

SPWebPartConfig

Efter att jag skapat mitt WebPart-projekt var det bara att lägga till en referens till ASP.NET AJAX Toolkit och sedan lägga till kod för att skapa de ASP.NET-kontroller, inklusive AutoCompleteExtender, som jag ville använda i metoden CreateChildControls:

        protected override void CreateChildControls()

        {

            try

            {

                // Label som skriver ut namn på kontrollen

                Literal label = new Literal();

                label.Text = "Lithware Suggest Search: ";

                this.Controls.Add(label);

                // Textbox för texten vi ska söka efter

                TextBox textbox = new TextBox();

                textbox.Attributes.Add("class", "ms-sbplain");

                textbox.ID = "SearchText";

                this.Controls.Add(textbox);

                // Bild med sök-ikon

                Image img = new Image();

                img.ImageUrl = "/_layouts/images/gosearch.gif";

                img.Attributes.Add("onClick", "javascript:SuggestSearch();");

                this.Controls.Add(img);

                // Lägg till AJAX AutoCompleteExtender till Controls

                AutoCompleteExtender autoComplete = new AutoCompleteExtender();

                autoComplete.MinimumPrefixLength = 1;

                autoComplete.ServicePath = "https://www.litwareinc.com/WebServices/SearchSuggest.asmx";

                autoComplete.ServiceMethod = "GetSearchSuggestions";

                autoComplete.TargetControlID = "SearchText";

                this.Controls.Add(autoComplete);

            }

            catch (Exception ex)

            {

                Literal errMsg = new Literal();

                errMsg.Text = ex.Message;

                this.Controls.Add(errMsg);

            }

        }

En sak som var lite trixigt var att SharePoints WebPartManager i runtime ersätter id:n som sätts på kontroller i designläge med id:n som bygger på automatgenererade GUID:s, för att garantera att alla kontroller som genereras på en sida får ett unikt id. I just det här fallet ville jag i runtime veta vilket id:som det specifika sök-textfältet hade, för att kunna fånga värdet av sökfältet och anropa sökfunktionen i SharePoint när ett förslag på sök-tern väl hade valts. Det gick att lösa med ett Javascript som loopade igenom alla kontroller på sidan och plockade ut den som innehöll "$SearchText" (när WebPartManager bygger upp id:n lägger den till kontrollens originalid på slutet, separerat med ett $-tecken). Det går naturligtvis att bygga en mycket snyggare lösning än denna för att utföra själva sökningen, framförallt en som är mer integrerad med standardsökningen i SharePoint, men min intention var inte att visa hur man bygger en helt ny sökfunktion utan att mer visa på möjlighterna med ASP.NET AJAX.

En annan utmaning när det gäller att använda ASP.NET AJAX i SharePoint är hur du väljer att placera den ScriptManager-kontroll som hanterar de JavaScript som skickas till klienten, t.ex. klientsides-proxy:n för den webservice som används och de olika komponenterna i ASP.NET AJAX Library. Denna kontroll får endast förekomma en gång i en och samma sida, vilket gör det vansklig att placera den i en WebPart eftersom det inte finns något som garanterar att denna inte kan läggas in flera gången i samma SharePoint-sida.

Ett mycket intressant Shared Source-projekt som hanterar detta problem är Smart Part. Smart Part gör det möjligt att lägga AJAX-funktionalitet i en Web User Control (ASCX) och sedan lägga denna kontroll till en SharePoint WebPart. Denna WebPart håller sedan reda på ifall en ScriptManager redan finns skapad för SharePoint-sidan du använder den i, vilket gör att du slipper lägga den i en Master Page. Väl värt titta på ifall du är intresserad av att undersöka möjligheterna med ASP.NET AJAX i din SharePoint-miljö.