SharePoint: SPLongOperation inside a Modal Dialog Window

September 10th, 2012 No comments

    In one of my posts I wrote about using SPLongOperation. When a lengthy operation has been accomplished the usual behavior of the SPLongOperation is to redirect user to another web page. In SharePoint 2010, however, the web page containing long operation could be opened in a Modal Dialog Window. In this case, instead of redirection, we often need to close the dialog right after the long operation is completed.

SPLongOperation.EndScript method

Fortunately, the SPLongOperation class provides us with a new useful method, EndScript, which notifies server that the operation has been completed and sends a specified JavaScript into the user browser. A typical use is presented below:

using (SPLongOperation operation = new SPLongOperation(this.Page))
{ 
    operation.LeadingHTML  = "Long operation";
    operation.TrailingHTML = "Please wait while the long operation is in progress";
 
    operation.Begin();
 
    // the code of the lengthy operation
    ...
 
    // check if the current page is opened in dialog
    if(SPContext.Current.IsPopUI)
        // signal that the lengthy operation is completed and 
        // send a script to close the current dialog
        operation.EndScript("window.frameElement.commitPopup();");
    else
        // signal that the lengthy operation is completed and 
        // redirect to another page
        operation.End("anotherPage.aspx");
}

The window.frameElement.commitPopup() script allows to close the dialog and pass SP.UI.DialogResult.OK as the result. The SPContext.Current.IsPopUI flag indicates if the current page is going to be opened in dialog and should be rendered in an appropriate way.

Wrapper around SPLongOperation

In the same old post I demonstrated a handy wrapper around SPLongOperation. Taking into account the dealing with dialogs, the updated version of the wrapper looks as follows:

public class SPLongOperationExecutionParams
{
    public string LeadingHtml  { get; set; }
    public string TrailingHtml { get; set; }
    public string RedirectUrl  { get; set; }
    public bool   CloseDialog  { get; set; }
}

public static void DoInSPLongOperationContext(SPLongOperationExecutionParams executionParams, Action action)
{
    if (HttpContext.Current.CurrentHandler is Page)
    {
        using (SPLongOperation operation = new SPLongOperation(HttpContext.Current.CurrentHandler as Page))
        {
            operation.LeadingHTML  = !string.IsNullOrEmpty(executionParams.LeadingHtml)  ? executionParams.LeadingHtml  : "Long operation";
            operation.TrailingHTML = !string.IsNullOrEmpty(executionParams.TrailingHtml) ? executionParams.TrailingHtml : "Please wait while the long operation is in progress";

            operation.Begin();

            if (action != null)
                action();

            try
            {
                if (executionParams.CloseDialog)
                    operation.EndScript("try { window.frameElement.commitPopup(); } catch (e) {}");
                else
                    operation.End(executionParams.RedirectUrl, SPRedirectFlags.DoNotEndResponse | SPRedirectFlags.Trusted, HttpContext.Current, "");
            }
            catch (ThreadAbortException)
            {
                // This exception is thrown because the SPLongOperation.End
                // calls a Response.End internally
            }
        }
    }
    else
        throw new ApplicationException("Couldn't find a host page!");
}

Below is an example of how to use it:

void startLongOperationButton_Click(object sender, EventArgs e)
{
    SPLongOperationExecutionParams executionParams = new SPLongOperationExecutionParams() 
    { 
        LeadingHtml  = "Creation of a new list item", 
        TrailingHtml = "Please wait while the item is being created", 
        CloseDialog  = SPContext.Current.IsPopUI, // if the page is in a dialog, the latter will be closed
        RedirectUrl  = "anotherPage.aspx" // if it's not a dialog, user will be redirected to that page
    };

    DoInSPLongOperationContext(executionParams, delegate()
    {
        // the code of the lengthy operation
        Thread.Sleep(5000);
    });
}

ASP.NET: How to trigger a validator on the client-side

August 27th, 2012 No comments

ValidatorValidate

The easiest way to trigger a validator on the client-side is to use the ValidatorValidate method provided by ASP.NET as a part of a Validation client-side API. You don’t need to include any js-files so as the all required scripts will be included automatically when you add a validator to your page. The typical use is something like this

var validator = document.getElementById('ctl00_PlaceHolderMain_info_textbox').Validators[0]; // Page_Validators[0];

ValidatorValidate(validator); // fire validation
ValidatorUpdateIsValid(); // update the global Page_IsValid flag

The ValidatorValidate makes the passed validator examine the input element bound to it and updates the validator‘s display (shows or hides its error message). I also recommend to call the ValidatorUpdateIsValid method, which updates the global variable Page_IsValid and by that may prevent the Html-form submit when the validator failed.

Below is a simple JavaScript function I use to force validation of a specified input. The function gets the input by the passed id, then goes through the all validators linked to the input and activates each of them. Finally, it updates the global Page_IsValid flag.

function ForceInputValidation(inputId) {
    var targetedControl = document.getElementById(inputId);    
    
    if (typeof(targetedControl.Validators) != "undefined") {        
        var i;
        for (i = 0; i < targetedControl.Validators.length; i++)
            ValidatorValidate(targetedControl.Validators[i]);
    }

    ValidatorUpdateIsValid();
}

ASP.Net places the initialization of validators at the end of the web page, so, it makes sense to handle the input‘s Validators-collection after the page has been loaded entirely. The following example demonstrates how to fire validation of the specified input right after the page has been loaded. jQuery is a best helper in that.

$(document).ready(function () {    
    ForceInputValidation('ctl00_PlaceHolderMain_info_textbox');
});

ValidatorEnable

As some blogs and forums recommend, you can also use another built-in function – ValidatorEnable, the code of which is shown below:

function ValidatorEnable(validator, enable) {
    validator.enabled = (enable != false);
    ValidatorValidate(validator);
    ValidatorUpdateIsValid();
}
// ...
// the typical use to fire validation is
var validator = document.getElementById('ctl00_PlaceHolderMain_info_textbox').Validators[0]; // Page_Validators[0];
ValidatorEnable(validator, true);

The ValidatorEnable calls sequentially the same ValidatorValidate and ValidatorUpdateIsValid functions we use above. Thus to activate one validator you can use, with no difference, either ValidatorEnable or combination of ValidatorValidate and ValidatorUpdateIsValid. But activating several validators (like in ForceInputValidation function) I advise employing the pair of the ValidatorValidate and ValidatorUpdateIsValid functions. That is because the ValidatorUpdateIsValid loops through all validators on the page. So, for best performance we should fire the all required validators and then call ValidatorUpdateIsValid once.

ps here is good source of information about the client-side validation.

SharePoint: How to pass parameters into a Modal Dialog Window and then access them

August 3rd, 2012 No comments

Passing parameters into a Modal Dialog Window

Opening a web page in a Modal Dialog Window we can pass parameters to it through the args property of dialog options. A typical JavaScript might look like

function OpenDialog() {
  var opt = {
    url     : 'somePage.aspx',
    autoSize: true,
 
    /* additional parameters */
    args: { arg1: 'The second argument is ', arg2: 12345 },
 
    dialogReturnValueCallback:
      function (res, retVal) {
        if (res === SP.UI.DialogResult.OK) { /* do something */ }
        else { /* do something else */ }
      }
  };
  SP.UI.ModalDialog.showModalDialog(opt);
}

Now let’s take a look how these parameters can be accessed from within the Parent-page and the page opened in the dialog.

Accessing the arguments from within the page opened in the dialog

In general case, when opening a SharePoint-based page, the passed arguments could be reached using a script similar to the following:

function DoSomethingWithArgs() {
  var passedArgs = SP.UI.ModalDialog.get_childDialog().get_args(); /* get access to the passed parameters */
  alert(passedArgs.arg1 + passedArgs.arg2);
}

The key method here is SP.UI.ModalDialog.get_childDialog, which allows to get the current dialog, or, to be more precise, the current SP.UI.Dialog object. The retrieved object, in turn, exposes the get_args method to access the passed parameters.

A possible use of the DoSomethingWithArgs function from the sample above is

<a href="#"
  onclick="ExecuteOrDelayUntilScriptLoaded(DoSomethingWithArgs, 'sp.ui.dialog.js'); return false;">
Show me the passed arguments
</a>

In case the web page opened in the dialog knows nothing about SharePoint and doesn’t include appropriate SharePoint JavaScript files, use the following version of the code:

function DoSomethingWithArgs() {
  var passedArgs = window.frameElement.dialogArgs; /* get access to the passed parameters */
  alert(passedArgs.arg1 + passedArgs.arg2);
}

Where the dialogArgs as a property of the window.frameElement object is provided by SP Dialog framework and returns the passed arguments.

Assuming the SharePoint‘s ExecuteOrDelayUntilScriptLoaded function isn’t available as well, the possible use of the DoSomethingWithArgs is quite straightforward:

<a href="#" onclick="DoSomethingWithArgs(); return false;">
Show me the passed arguments
</a>

All other conditions being equal, I recommend the approach with the window.frameElement object as it works always and doesn’t require the page in the dialog to be bound to SharePoint somehow.

Accessing the passed arguments from within the Parent-page

The Parent-page is a place where we define parameters and pass them to the dialog. So, the most direct way is to store the required values into some variable(s) to access them later. Another way is to rely on Dialog framework and use the code already mentioned in the previous section, namely

//...
var passedArgs = SP.UI.ModalDialog.get_childDialog().get_args();
//...

The more interesting case, however, is to access the passed arguments inside the dialogReturnValueCallback function. The listing below contains an example of one of such dialogReturnValueCallback definitions:

function OpenDialog() {
  var opt = {
    url     : 'somePage.aspx',
    autoSize: true,

    /* additional parameters */
    args: { arg1: 'The second argument is ', arg2: 12345 }, 

    dialogReturnValueCallback:
      function (res, retVal) {
        if (res === SP.UI.DialogResult.OK) {                    
          var passedArgs = this.get_args(); /* get access to the passed parameters */
          alert(passedArgs.arg1 + passedArgs.arg2);
        }
      }
  };
  SP.UI.ModalDialog.showModalDialog(opt);
}

The dialogReturnValueCallback function will be called in the context of the SP.UI.Dialog object associated with the current Modal Dialog Window. Therefore, inside the function the passed arguments can be accessed using this-keyword.

A possible use of the OpenDialog is shown below:

<a href="#" 
  onclick="ExecuteOrDelayUntilScriptLoaded(OpenDialog, 'sp.ui.dialog.js'); return false;">
Open Modal Dialog Window
</a>

ps There is a quite detailed article about dealing with Dialogs in SharePoint 2010. So, read to learn more about Dialog framework.

WebLoading Library: Downloading data from RESTful sources

July 23rd, 2012 No comments

    I often run into necessity to load data from sources published in the Internet. In most cases the sources are RESTful web-services, thus the data they emit is in xml or json formats. More and more web-services providers prefer spreading their data in an easier-to-use REST formats as a simpler alternative to SOAP and WSDL based web-services.

WebLoading Library Overview

For downloading data via the Http protocol I implemented a set of generic classes, let’s call it WebLoading Library. Almost every class in the library is a loader, which operates at a certain level of abstraction. The library allows to easily derive a new class from one of the exposed loaders, defining a type of the data to be loaded and the type of the ready-to-use data to be returned out of the loader. See the class diagram below.

Class Diagram of WebLoading Library

I single out such basic steps of getting data as raw data loading, verification and conversion into a ready-to-use view. All loaders exposes the LoadData method aimed at performing these steps.The listing below demonstrates the LoadData‘s base implementation in the BaseLoader class. BaseLoader is a root base class for all loaders in the WebLoading Library hierarchy.

BaseLoader Source

using System;

namespace WebLoading
{
    /// <summary>
    /// Abstracts the data loading from a specified source. Provides such basic steps as data loading, verification and interpretation.
    /// </summary>
    /// <typeparam name="TRawData">A type of data to be loaded</typeparam>
    /// <typeparam name="TParsedData">A type of the analyzed data to be returned</typeparam>
    public abstract class BaseLoader<TRawData, TParsedData>
        where TRawData    : class
        where TParsedData : class
    {
        /// <summary>
        /// Loads data from a specified source, verifies it and returns its ready-to-use view.
        /// </summary>
        /// <returns>The ready-to-use view of the loaded data</returns>
        public virtual TParsedData LoadData()
        {
            TRawData rawData = LoadRawData();

            if (!IsRawDataValid(rawData))
                return default(TParsedData);
            
            TParsedData parsedData = ParseData(rawData);            

            return parsedData;
        }

        /// <summary>
        /// Loads data from a specified source. Has to be overridden in a derived class
        /// </summary>
        /// <returns>The loaded raw data</returns>
        protected abstract TRawData LoadRawData();

        /// <summary>
        /// Verifies the response retrieved from the specified source
        /// </summary>
        /// <param name="rawData">The raw data to be verified</param>
        /// <returns>True if the response is correct. Otherwise, False</returns>
        protected virtual bool IsRawDataValid(TRawData rawData)
        {
            return true;
        }       

        /// <summary>
        /// Analyzes the raw data and returns its ready-to-use view. Has to be overridden in a derived class
        /// </summary>
        /// <param name="rawData">The raw data</param>
        /// <returns>The ready-to-use view of the data</returns>
        protected abstract TParsedData ParseData(TRawData rawData);
    }
}

BaseLoader abstracts the loading from any source. Unlike BaseLoader, its direct descendant, HttpLoader, implies loading from a source supporting the Http protocol. See the listing of the HttpLoader class below.

HttpLoader Source

using System;
using System.Net;
using System.IO;

namespace WebLoading
{
    /// <summary>    
    /// Abstracts the data loading from a specified source using Http. Provides such basic steps as data loading, verification and interpretation.
    /// </summary>
    /// <typeparam name="TRawData">A type of data to be loaded</typeparam>
    /// <typeparam name="TParsedData">A type of the analyzed data to be returned</typeparam>
    public abstract class HttpLoader<TRawData, TParsedData> : BaseLoader<TRawData, TParsedData>
        where TRawData    : class
        where TParsedData : class
    {
        /// <summary>
        /// An initial url to load data from
        /// </summary>
        protected readonly string _requestUrl;

        /// <summary>
        /// Initializes a new instance of the HttpLoader class
        /// </summary>
        /// <param name="requestUrl">An initial url to load data from</param>
        public HttpLoader(string requestUrl)
        {
            _requestUrl = requestUrl;
        }

        /// <summary>
        /// Initializes a new instance of the HttpLoader class
        /// </summary>
        public HttpLoader()
        {            
        }

        /// <summary>       
        /// Loads data from a specified source using Http, returns a ready-to-analyze data view
        /// </summary>
        /// <returns>A ready-to-analyze view of the data</returns>
        protected override TRawData LoadRawData()
        {
            HttpWebRequest httpWebRequest = PrepareRequest();

            TRawData rawData;
            using (HttpWebResponse httpWebResponse = (HttpWebResponse)httpWebRequest.GetResponse())
                using (Stream responseStream = httpWebResponse.GetResponseStream())
                    rawData = ReadResponseStream(responseStream);

            return rawData;
        }

        /// <summary>
        /// Creates and initializes a Http request object
        /// </summary>
        /// <returns>A Http request object</returns>
        protected virtual HttpWebRequest PrepareRequest()
        {
            string url = PrepareUrl();

            HttpWebRequest httpWebRequest = (HttpWebRequest)WebRequest.Create(url);
            httpWebRequest.UserAgent      = @"Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1)";
            return httpWebRequest;
        }

        /// <summary>
        /// Prepares and returns an url the data should be loaded from. If the parameterless constructor is used, the method has to be overridden in a derived class
        /// </summary>
        /// <returns>An url to load the data from</returns>
        protected virtual string PrepareUrl()
        {
            return _requestUrl;
        }

        /// <summary>
        /// Reads from a stream and returns the received data in a ready-to-analyze view. Has to be overridden in a derived class
        /// </summary>
        /// <param name="stream">A data stream</param>
        /// <returns>The ready-to-analyze view of the data</returns>
        protected abstract TRawData ReadResponseStream(Stream stream);        
    }
}

The next level in the hierarchy comprises such loaders as ImageLoader, XmlLoader and JsonLoader. I’ll dwell upon the last two a bit later. As regards the first one, ImageLoader is a quite simple class derived from HttpLoader that downloads an image from a specified source via Http. Being initially intended for dealing with REST formats, the loaders family can be easily extended to any data formats. So, the ImageLoader is a good example to that. The class is shown below:

ImageLoader Source and How to use

using System;
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;

namespace WebLoading
{
    /// <summary>
    /// Downloads an image from a specified source using Http
    /// </summary>
    public class ImageLoader : HttpLoader<Image, Image>
    {
        /// <summary>
        /// Initializes a new instance of the ImageLoader class
        /// </summary>
        /// <param name="url">An url to load image from</param>
        public ImageLoader(string url) : base(url)
        {
        }

        /// <summary>
        /// Initializes a new instance of the ImageLoader class. Intended for use in a derived class
        /// </summary>
        protected ImageLoader()
        {
        }

        /// <summary>
        /// Reads an image from a stream
        /// </summary>
        /// <param name="stream">A data stream</param>
        /// <returns>An Image object</returns>
        protected override Image ReadResponseStream(Stream stream)
        {
            Image image = Image.FromStream(stream);
            return image;
        }

        /// <summary>
        /// Does nothing. Just returns a loaded image.
        /// </summary>
        /// <param name="rawData">The loaded image</param>
        /// <returns>The loaded image with no changes</returns>
        protected override Image ParseData(Image rawData)
        {
            return rawData;
        }
    }
}

//...
// "how to use" sample
ImageLoader imageLoader = new ImageLoader("http://dotnetfollower.com/wordpress/wp-content/uploads/2012/06/All_Site_Content_Links.png");
Image img = imageLoader.LoadData();

WebLoading Library Overview: XmlLoader

XmlLoader is an abstract class derived from HttpLoader and intended for xml data downloading. The downloaded raw xml is presented as a XDocument object. Create a descendent of the XmlLoader class to verify and turn the XDocument into any another desired view-object. The XmlLoader looks as follows:

XmlLoader Source

using System;
using System.IO;
using System.Xml.Linq;

namespace WebLoading
{
    /// <summary>    
    /// Abstracts the xml data loading from a specified source using Http. Provides such basic steps as data loading, verification and interpretation.
    /// </summary>
    /// <typeparam name="TParsedData">A type of the analyzed data to be returned</typeparam>
    public abstract class XmlLoader<TParsedData> : HttpLoader<XDocument, TParsedData> where TParsedData : class
    {
        /// <summary>
        /// Initializes a new instance of the XmlLoader class
        /// </summary>
        /// <param name="url">An initial url to load data from</param>
        public XmlLoader(string url) : base(url)
        {
        }

        /// <summary>
        /// Initializes a new instance of the XmlLoader class
        /// </summary>
        public XmlLoader()
        {
        }

        /// <summary>        
        /// Reads from a stream and returns the received xml data
        /// </summary>
        /// <param name="stream">A data stream</param>
        /// <returns>The received xml data</returns>
        protected override XDocument ReadResponseStream(Stream stream)
        {
            XDocument xDoc = XDocument.Load(stream);
            return xDoc;
        }
    }
}

As a simple example, below is a class-descendent demonstrating how to get the expected maximum temperatures for the next 7 days. The data source is http://graphical.weather.gov, which provides with xml data like the following:

Xml Data Sample

<?xml version="1.0"?>
<dwml version="1.0" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="http://graphical.weather.gov/xml/DWMLgen/schema/DWML.xsd">
  <head>
    <product srsName="WGS 1984" concise-name="time-series" operational-mode="official">
      <title>NOAA's National Weather Service Forecast Data</title>
      <field>meteorological</field>
      <category>forecast</category>
      <creation-date refresh-frequency="PT1H">2012-07-13T02:13:29Z</creation-date>
    </product>
    <source>
      <more-information>http://graphical.weather.gov/xml/</more-information>
      <production-center>Meteorological Development Laboratory<sub-center>Product Generation Branch</sub-center></production-center>
      <disclaimer>http://www.nws.noaa.gov/disclaimer.html</disclaimer>
      <credit>http://www.weather.gov/</credit>
      <credit-logo>http://www.weather.gov/images/xml_logo.gif</credit-logo>
      <feedback>http://www.weather.gov/feedback.php</feedback>
    </source>
  </head>
  <data>
    <location>
      <location-key>point1</location-key>
      <point latitude="28.50" longitude="-81.37"/>
    </location>
    <moreWeatherInformation applicable-location="point1">http://forecast.weather.gov/MapClick.php?textField1=28.50&amp;textField2=-81.37</moreWeatherInformation>
    <time-layout time-coordinate="local" summarization="none">
      <layout-key>k-p24h-n7-1</layout-key>
      <start-valid-time>2012-07-13T08:00:00-04:00</start-valid-time>
      <end-valid-time>2012-07-13T20:00:00-04:00</end-valid-time>
      <start-valid-time>2012-07-14T08:00:00-04:00</start-valid-time>
      <end-valid-time>2012-07-14T20:00:00-04:00</end-valid-time>
      <start-valid-time>2012-07-15T08:00:00-04:00</start-valid-time>
      <end-valid-time>2012-07-15T20:00:00-04:00</end-valid-time>
      <start-valid-time>2012-07-16T08:00:00-04:00</start-valid-time>
      <end-valid-time>2012-07-16T20:00:00-04:00</end-valid-time>
      <start-valid-time>2012-07-17T08:00:00-04:00</start-valid-time>
      <end-valid-time>2012-07-17T20:00:00-04:00</end-valid-time>
      <start-valid-time>2012-07-18T08:00:00-04:00</start-valid-time>
      <end-valid-time>2012-07-18T20:00:00-04:00</end-valid-time>
      <start-valid-time>2012-07-19T08:00:00-04:00</start-valid-time>
      <end-valid-time>2012-07-19T20:00:00-04:00</end-valid-time>
    </time-layout>
    <parameters applicable-location="point1">
      <temperature type="maximum" units="Fahrenheit" time-layout="k-p24h-n7-1">
        <name>Daily Maximum Temperature</name>
        <value>92</value>
        <value>93</value>
        <value>93</value>
        <value>94</value>
        <value>92</value>
        <value>94</value>
        <value>93</value>
      </temperature>
    </parameters>
  </data>
</dwml>

The GraphicalWeatherLoader class accepts a zip code of the region you need to get forecast for. See the next listing:

GraphicalWeatherLoader Source and How to use

using System;
using System.Collections.Generic;
using System.Xml.Linq;
using System.Xml.XPath;
using WebLoading;

namespace TestApp
{
    public class GraphicalWeatherLoader : XmlLoader<List<GraphicalWeatherLoader.DailyMaximumTemperature>>
    {
        public class DailyMaximumTemperature
        {
            public DateTime Day         { get; set; }
            public string   Temperature { get; set; }
        }

        protected static string _urlTemplate = "http://graphical.weather.gov/xml/SOAP_server/ndfdXMLclient.php?whichClient=NDFDgenMultiZipCode&lat=&lon=&listLatLon=&lat1=&lon1=&lat2=&lon2=&resolutionSub=&listLat1=&listLon1=&listLat2=&listLon2=&resolutionList=&endPoint1Lat=&endPoint1Lon=&endPoint2Lat=&endPoint2Lon=&listEndPoint1Lat=&listEndPoint1Lon=&listEndPoint2Lat=&listEndPoint2Lon=&zipCodeList={0}&listZipCodeList=&centerPointLat=&centerPointLon=&distanceLat=&distanceLon=&resolutionSquare=&listCenterPointLat=&listCenterPointLon=&listDistanceLat=&listDistanceLon=&listResolutionSquare=&citiesLevel=&listCitiesLevel=&sector=&gmlListLatLon=&featureType=&requestedTime=&startTime=&endTime=&compType=&propertyName=&product=time-series&begin=2004-01-01T00%3A00%3A00&end=2016-06-27T00%3A00%3A00&Unit=e&maxt=maxt&Submit=Submit";

        public GraphicalWeatherLoader(string zipCode) : base(string.Format(_urlTemplate, zipCode))
        {
        }

        protected override List<GraphicalWeatherLoader.DailyMaximumTemperature> ParseData(XDocument rawData)
        {
            List<DailyMaximumTemperature> res = new List<DailyMaximumTemperature>();

            XElement dataElement = rawData.XPathSelectElement("dwml/data");

            IEnumerator<XElement> dates        = dataElement.XPathSelectElements("time-layout/start-valid-time").GetEnumerator();
            IEnumerator<XElement> temperatures = dataElement.XPathSelectElements("parameters/temperature/value").GetEnumerator();
            
            while(dates.MoveNext() && temperatures.MoveNext())
            {
                DailyMaximumTemperature dailyMaximumTemperature = new DailyMaximumTemperature();
                dailyMaximumTemperature.Day         = DateTime.Parse(dates.Current.Value);
                dailyMaximumTemperature.Temperature = temperatures.Current.Value;
                res.Add(dailyMaximumTemperature);
            }            

            return res;
        }

        protected override bool IsRawDataValid(XDocument rawData)
        {
            string data = rawData.ToString();
            if(!string.IsNullOrWhiteSpace(data))
                return !data.Contains("SOAP ERROR");
            return false;
        }
    }
}

//...
// "how to use" sample
GraphicalWeatherLoader graphicalWeatherLoader = new GraphicalWeatherLoader("32801");
List<GraphicalWeatherLoader.DailyMaximumTemperature> temperatures = graphicalWeatherLoader.LoadData();
if (temperatures != null)
    foreach (GraphicalWeatherLoader.DailyMaximumTemperature temperature in temperatures)
        Console.WriteLine(string.Format("{0} - {1} Fahrenheit", temperature.Day.ToShortDateString(), temperature.Temperature));

WebLoading Library Overview: JsonLoader

Like XmlLoader, the JsonLoader class is abstract and derived from HttpLoader too. It’s dedicated for downloading json data. The downloaded json is processed by the JsonParser borrowed from the fastJSON project by Mehdi Gholam. Undoubtedly it’s the fastest json parser I’ve ever met. So, many thanks to Mehdi. The parsed json data is presented as a Dictionary<string, object>, which, besides simple objects, may contain arrays and other dictionaries nested on different levels. To have an ability to verify and transform the dictionary into something different, just create a descendent of the JsonLoader. Below is the listing of the JsonLoader:

JsonLoader Source

using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using fastJSON;

namespace WebLoading
{
    /// <summary>    
    /// Abstracts the json data loading from a specified source using Http. Provides such basic steps as data loading, verification and interpretation.
    /// </summary>
    /// <typeparam name="TParsedData">A type of the analyzed data to be returned</typeparam>
    public abstract class JsonLoader<TParsedData> : HttpLoader<Dictionary<string, object>, TParsedData> where TParsedData : class
    {
        /// <summary>
        /// Initializes a new instance of the JsonLoader class
        /// </summary>
        /// <param name="url">An initial url to load data from</param>
        public JsonLoader(string url) : base(url)
        {
        }

        /// <summary>
        /// Initializes a new instance of the JsonLoader class
        /// </summary>
        public JsonLoader()
        {
        }

        /// <summary>        
        /// Reads json data from a stream and represents it as a tree of key-value collections
        /// </summary>
        /// <param name="stream">A data stream</param>
        /// <returns>A root key-value collection</returns>
        protected override Dictionary<string, object> ReadResponseStream(Stream stream)
        {
            StreamReader reader = new StreamReader(stream);
            string jsonText = reader.ReadToEnd();

            JsonParser parser = new JsonParser(jsonText, false);
            return parser.Decode() as Dictionary<string, object>;
        }

        /// <summary>
        /// Obtains object from a tree of key-value collections using a xpath-like path (for example, "set/items/item")
        /// </summary>
        /// <param name="dictionary">A root key-value collection</param>
        /// <param name="path">A xpath-like path</param>
        /// <returns>A found object</returns>
        protected static object SelectObject(Dictionary<string, object> dictionary, string path)
        {
            List<object> res = SelectObjects(dictionary, path, true, true);
            return res.Count > 0 ? res[0] : null;
        }

        /// <summary>
        /// Obtains object from a tree of key-value collections using a xpath-like path (for example, "set/items/item")
        /// </summary>
        /// <param name="dictionary">A root key-value collection</param>
        /// <param name="path">A xpath-like path</param>
        /// <param name="returnNull">Determines if nulls should be returned or skipped</param>
        /// <returns>A found object</returns>
        protected static List<object> SelectObjects(Dictionary<string, object> dictionary, string path, bool returnNull)
        {
            return SelectObjects(dictionary, path, returnNull, false);
        }

        /// <summary>
        /// Obtains object from a tree of key-value collections using a xpath-like path (for example, "set/items/item")
        /// </summary>
        /// <param name="dictionary">A root key-value collection</param>
        /// <param name="path">A xpath-like path</param>
        /// <param name="returnNull">Determines if nulls should be returned or skipped</param>
        /// <param name="onlyFirstFound">Determines if the search should be stopped after the first found object</param>
        /// <returns>A found object</returns>
        private static List<object> SelectObjects(Dictionary<string, object> dictionary, string path, bool returnNull, bool onlyFirstFound)
        {
            List<object> res = new List<object>();

            string[] pathPatterns = path.Split(new string[] { "/" }, StringSplitOptions.RemoveEmptyEntries);

            List<Dictionary<string, object>> dicts = new List<Dictionary<string, object>>() { dictionary };
            for (int i = 0; i < pathPatterns.Length; i++)
            {
                string pattern = pathPatterns[i];

                Dictionary<string, object>[] dictsToProcess = dicts.ToArray();
                dicts.Clear();

                foreach (Dictionary<string, object> currentDictionary in dictsToProcess)
                {
                    if (currentDictionary.ContainsKey(pattern))
                    {
                        object obj = currentDictionary[pattern];

                        if (i == pathPatterns.Length - 1) // is it last pattern
                        {
                            if (obj != null || returnNull)
                                res.Add(obj);
                        }
                        else // move forward
                        {
                            if (obj is Dictionary<string, object>)
                                dicts.Add(obj as Dictionary<string, object>);
                            else
                                if (obj is IEnumerable)
                                    foreach (object childObj in obj as IEnumerable)
                                        if (childObj is Dictionary<string, object>)
                                            dicts.Add(childObj as Dictionary<string, object>);
                        }
                    }

                    if (onlyFirstFound && res.Count > 0)
                        break;
                }

                if (dicts.Count == 0)
                    break;
            }

            return res;
        }
    }
}

Pay attention to the SelectObjects methods that obtain an specified object(s) from a tree of arrays and dictionaries using a xpath-like path (for example, “set/items/item”).

Let’s take a look at yet another example, this time it’s a descendent of JsonLoader. The derived class is quite pointless and just gets names of some capital-cities. The http://api.geonames.org service returns the json data in the following form:

Json Data Sample

{
   "geonames":[
      {
         "fcodeName":"capital of a political entity",
         "toponymName":"Mexico City",
         "countrycode":"MX",
         "fcl":"P",
         "fclName":"city, village,...",
         "name":"Mexiko-Stadt",
         "wikipedia":"",
         "lng":-99.12766456604,
         "fcode":"PPLC",
         "geonameId":3530597,
         "lat":19.428472427036,
         "population":12294193
      },
      {
         "fcodeName":"capital of a political entity",
         "toponymName":"Manila",
         "countrycode":"PH",
         "fcl":"P",
         "fclName":"city, village,...",
         "name":"Manila",
         "wikipedia":"",
         "lng":120.9822,
         "fcode":"PPLC",
         "geonameId":1701668,
         "lat":14.6042,
         "population":10444527
      },
      {
         "fcodeName":"capital of a political entity",
         "toponymName":"Dhaka",
         "countrycode":"BD",
         "fcl":"P",
         "fclName":"city, village,...",
         "name":"Dhaka",
         "wikipedia":"",
         "lng":90.40743827819824,
         "fcode":"PPLC",
         "geonameId":1185241,
         "lat":23.710395616597037,
         "population":10356500
      }
   ]
}

The next listing is the GeoNamesCitiesLoader class:

GeoNamesCitiesLoader Source and How to use

using System;
using System.Collections;
using System.Collections.Generic;
using WebLoading;

namespace TestApp
{
    public class GeoNamesCitiesLoader : JsonLoader<List<GeoNamesCitiesLoader.City>>
    {
        public class City
        {
            public string  FCodeName   { get; set; }
            public string  ToponymName { get; set; }
            public string  CountryCode { get; set; }
            public string  Name        { get; set; }
            public string  Wikipedia   { get; set; }
            public decimal Population  { get; set; }
            public double  Longitude   { get; set; }
            public double  Latitude    { get; set; }            
        }

        protected override bool IsRawDataValid(Dictionary<string, object> rawData)
        {
            return base.IsRawDataValid(rawData);
        }

        protected override string PrepareUrl()
        {
            return "http://api.geonames.org/citiesJSON?north=44.1&south=-9.9&east=-22.4&west=55.2&lang=de&username=demo";
        }

        protected override List<City> ParseData(Dictionary<string, object> rawData)
        {
            List<City> res = new List<City>();

            IEnumerable arrayOfDictionaries = SelectObject(rawData, "geonames") as IEnumerable;
            foreach (Dictionary<string, object> dict in arrayOfDictionaries)
                res.Add(CreateCity(dict));

            return res;
        }
       
        private City CreateCity(Dictionary<string, object> metadata)
        {
            City res = new City();

            foreach (KeyValuePair<string, object> pair in metadata)
            {
                switch (pair.Key.ToLower())
                {
                    case "fcodename":   res.FCodeName   = Convert.ToString(pair.Value);  break;
                    case "toponymname": res.ToponymName = Convert.ToString(pair.Value);  break;
                    case "countrycode": res.CountryCode = Convert.ToString(pair.Value);  break;
                    case "name":        res.Name        = Convert.ToString(pair.Value);  break;
                    case "wikipedia":   res.Wikipedia   = Convert.ToString(pair.Value);  break;
                    case "lng":         res.Longitude   = Convert.ToDouble(pair.Value);  break;
                    case "lat":         res.Latitude    = Convert.ToDouble(pair.Value);  break;
                    case "population":  res.Population  = Convert.ToDecimal(pair.Value); break;                    
                }
            }            

            return res;
        }
    }
}

//...
// "how to use" sample
GeoNamesCitiesLoader geoNamesCitiesLoader = new GeoNamesCitiesLoader();
List<GeoNamesCitiesLoader.City> cities = geoNamesCitiesLoader.LoadData();
if (cities != null)
    foreach (GeoNamesCitiesLoader.City city in cities)
        Console.WriteLine(string.Format("{0} [Population: {1}]", city.ToponymName, city.Population));

The source code and examples are available to download here.

Categories: C#, JSON, XML Tags: , ,

SharePoint: How To Maximize a Modal Dialog Window

July 17th, 2012 No comments

    In SharePoint 2010 by default a list item is opened in a Modal Dialog Window. The dialog adjusts its size depending on the size of the contained content and provides users with buttons to maximize and close itself. So, I was asked to maximize the dialog when opening some list items.

Creating and Initializing of a Modal Dialog Window

The JavaScript responsible for the dialog rendering is in the SP.UI.Dialog.js file (usually located in C:\Program Files\Common Files\Microsoft Shared\Web Server Extensions\14\TEMPLATE\LAYOUTS). Clicking on a link-title, for example, in a list view leads to creating and displaying a new Modal Dialog Window in a manner similar to the following:

var options = SP.UI.$create_DialogOptions();

options.url           = 'display-page corresponding to the current list item';
options.title         = 'list item title';
options.allowMaximize = true;
options.showClose     = true;

// possible option, but it's not used in the place in question
//options.showMaximized = true;

var modalDialog = SP.UI.ModalDialog.showModalDialog(options);

The commented line demonstrates one of the possible settings, showMaximized, which apparently does exactly what we need. So, it would be a good solution if we had control under creation of the dialog. Unfortunately, we don’t have. Modifying scripts in SharePoint system js-files runs counter to all known “best practices”, therefore I don’t even consider it. Another possible way is override the client-side OnClick-events of the link-title fields everywhere (in list views, web parts and so on) so that your own script would create and display the dialog in a required manner. Obviously, it’s a very time-consuming solution. Moreover, every time when creating a list view, web part or something else you always have to keep in mind the possible need to rewrite their OnClick-events. In my opinion the only acceptable approach is allow a web page itself to maximize the dialog window it’s placed in.

Script to Maximize a Modal Dialog Window

For that, first of all, we need to have a JavaScript to maximize the current dialog. As SP.UI.Dialog.js doesn’t provide a function/method intended for use outside, we have to consume undocumented internal functions. A suitable script is described in the blog post – “How to maximize a Modal Dialog in JavaScript?“. With slight changes the JavaScript looks like the following

function _maximizeWindow() {
    var currentDialog = SP.UI.ModalDialog.get_childDialog();
    if (currentDialog != null && !currentDialog.$S_0)
        currentDialog.$z();    
    }
    ExecuteOrDelayUntilScriptLoaded(_maximizeWindow, 'sp.ui.dialog.js');

_maximizeWindow gets the current dialog window instance and tries to maximize it if it hasn’t been done before. ExecuteOrDelayUntilScriptLoaded, in turn, is applied to ensure the _maximizeWindow is called after the sp.ui.dialog.js has been completely loaded.

Script Applying

Having the script, we can insert it into the end of a web page to be maximized. It could be done through SharePoint Designer or by modifying a physical aspx-file defined as a display/new/edit form for a list or a content type. However, I decided to add the script dynamically in a page’s code-behind. So, the resultant code is shown below:

using System;
using System.Web.UI;
using Microsoft.SharePoint;

//...

public class DisplayListItem : Page
{
    protected override void OnLoad(EventArgs e)
    {
        base.OnLoad(e);
        if(!IsPostBack)
            AddMaximizeWindowScript();
    }

    //...

    protected virtual void AddMaximizeWindowScript()
    {
        if (SPContext.Current.IsPopUI)
        {
            const string scriptKey = "MaxWinwScript";
            if (!ClientScript.IsClientScriptBlockRegistered(GetType(), scriptKey))
            {
                const string jsScript =
                    @"function _maximizeWindow() {
                        var currentDialog = SP.UI.ModalDialog.get_childDialog();
                        if (currentDialog != null && !currentDialog.$S_0)
                            currentDialog.$z();    
                        }
                        ExecuteOrDelayUntilScriptLoaded(_maximizeWindow, 'sp.ui.dialog.js');";

                ClientScript.RegisterClientScriptBlock(GetType(), scriptKey, jsScript, true);
            }
        }
    }
    
    //...
}

You may ask why I use RegisterClientScriptBlock instead of RegisterStartupScript or window.onload, or jQuery‘s $(document).ready (the latest two arise on client side). The reason is that initially the dialog window is created with the autoSize option. So, it’s very reasonable to maximize the window as soon as possible to prevent the content’s size calculating, which apparently happens after the page has been loaded entirely.