Thursday, March 24, 2011

Silverlight4 - Printing fit to page

RAVAGE

In my project one of the feature we need to provide is the ability to print a page which holds multiple custom widgets. Printing ability need to be provided in two stages. One for printing the individual widget and the other one for printing the entire page with multiple widgets. Initially I started with developing the provision for printing individual widgets and achieved it simply than what I expected, using the print API in Silverlight 4.

SIMPLE PRINT

SNIPPET 1

public void Print(UIElement elementToBePrinted, string documentName)
{
    PrintDocument docToPrint = new PrintDocument();

    docToPrint.PrintPage += (s, args) =>
    {
        args.PageVisual = elementToBePrinted;
    };

    docToPrint.Print(documentName);
}

I tried printing widgets of normal sizes using the above code which worked and tried the same for printing the entire page with multiple widgets where I got caught. Upon printing entire page, some part will exceed the print area based on the paper size resulting in partial area got print. Simply speaking it won't fit into print area.

We can tranform the FrameworkElement to fit the paper size but upon doing so the element will be scaled visually while printing and the user experience will not be good. Upon searching the web I found that people who experienced similar issues have done workarounds something similar to the one specified in a blog by Jacek Jura.

Workaround is hiding the scaled screen and showing a custom text like "Priniting in progress". I implemented similar workaround and still my tech lead is not satisfied. Hence, I pushed out to find a better alternate for this.

After trying with several options I compromised with one workaround. Idea is getting a snapshot of the element / page and showing the snapshot by hiding the scaled / transformed element while printing. Yes as most of might guess, its what our actors do to trick out video surveillance (in movies like MissionImpossible, Speed, etc....).

I altered the code to fit my needs. Until the completion of the printing process, the UI thread will be blocked and hence the user can't perform actions on UI till then. Hence showing snapshot at that time won't deviate the user experience.

Pros and Cons

The advantage is, user will experience the original view(visually) even when the element to be printed is scaled.
Disadvantage is, since we're showing the snapshot, it'll be slightly blur, but compromisable.

Snippet 2

public class PrintUtility
{
    #region Class Level Variables

    private const string NullReferenceExceptionMessage = "PrintElement is null : Please specify the element to be printed.";
    private Grid parentGrid;
    private Border borderItem;
    private WriteableBitmap printElementImage;
    private ImageBrush printElementImageBrush;

    #endregion

    #region Properties

    public FrameworkElement PrintElement { get; private set; }

    public string DocumentName { get; private set; }

    public FrameworkElement TabletUIElement { get; set; }

    #endregion

    #region Constructors

    public PrintUtility(FrameworkElement elementToBePrinted, string documentName)
    {
        this.PrintElement = elementToBePrinted;
        this.DocumentName = documentName;
    }

    #endregion

    #region Event Handlers

    void DocToPrint_EndPrint(object sender, EndPrintEventArgs e)
    {
        this.PrintElement.RenderTransform = null;

        if (this.borderItem != null)
        {
            if (parentGrid.Children.Contains(this.borderItem))
            {
                parentGrid.Children.Remove(this.borderItem);
            }
            this.borderItem = null;
        }

        this.parentGrid = null;
        this.printElementImage = null;
        this.printElementImageBrush = null;
    }

    void DocToPrint_BeginPrint(object sender, BeginPrintEventArgs e)
    {
        FrameworkElement maskElement = this.TabletUIElement ?? this.PrintElement;

        if (maskElement.Parent is Grid)
        {
            parentGrid = (Grid)maskElement.Parent;
            parentGrid.Children.Add(BuildBorderWithImageFill());
        }
    }

    void DocToPrint_PrintPage(object sender, PrintPageEventArgs e)
    {
        double scale = 1;

        if (e.PrintableArea.Height < this.PrintElement.ActualHeight)
        {
            scale = e.PrintableArea.Height / this.PrintElement.ActualHeight;
        }

        if (e.PrintableArea.Width < this.PrintElement.ActualWidth && e.PrintableArea.Width / this.PrintElement.ActualWidth < scale)
        {
            scale = e.PrintableArea.Width / this.PrintElement.ActualWidth;
        }

        if (scale < 1)
        {
            ScaleTransform scaleTransform = new ScaleTransform();
            scaleTransform.ScaleX = scale;
            scaleTransform.ScaleY = scale;

            this.PrintElement.RenderTransform = scaleTransform;
        }

        e.PageVisual = this.PrintElement;
    }

    #endregion

    #region Public Methods

    public void Print()
    {
        if (this.PrintElement != null)
        {
            PrintDocument docToPrint = new PrintDocument();

            docToPrint.PrintPage += new EventHandler<PrintPageEventArgs>(DocToPrint_PrintPage);
            docToPrint.BeginPrint += new EventHandler<BeginPrintEventArgs>(DocToPrint_BeginPrint);
            docToPrint.EndPrint += new EventHandler<EndPrintEventArgs>(DocToPrint_EndPrint);

            docToPrint.Print(this.DocumentName);
        }
        else
        {
            throw new NullReferenceException(NullReferenceExceptionMessage);
        }

    }

    #endregion

    #region Private Methods

    private Border BuildBorderWithImageFill()
    {
        printElementImageBrush = new ImageBrush();
        borderItem = new Border();

        printElementImage = new WriteableBitmap(this.TabletUIElement ?? this.PrintElement, null);
        printElementImageBrush.ImageSource = printElementImage;

        borderItem.Background = printElementImageBrush;
        borderItem.Child = new TextBlock() { Text = "Printing...", HorizontalAlignment = HorizontalAlignment.Center, VerticalAlignment = VerticalAlignment.Center };

        return borderItem;
    }

    #endregion

}

As a prerequisite, we need a simple Grid immediately surrounding the printable element so as to place the snapshot in the Grid.

SUMMARY

Also I tried with creating an image from the printable element and scale the image to print. The resultant quality is good but not as good as printing the element directly. You can also try that if it suits for you. Snippet for creating an image of the printable element, scale the created image and then print the scaled image is specified below.

Snippet 3

public class PrintUtilityImageScaling
{
    #region Class Level Variables

    private const string NullReferenceExceptionMessage = "PrintElement is null : Please specify the element to be printed.";
    
    #endregion

    #region Properties

    public FrameworkElement PrintElement { get; private set; }

    public string DocumentName { get; private set; }

    #endregion

    #region Constructors

    public PrintUtilityImageScaling(FrameworkElement elementToBePrinted, string documentName)
    {
        this.PrintElement = elementToBePrinted;
        this.DocumentName = documentName;
    }

    #endregion

    #region Event Handlers

    void DocToPrint_PrintPage(object sender, PrintPageEventArgs e)
    {
        double scale = 1;
        WriteableBitmap writeableBitmap;

        if (e.PrintableArea.Height < this.PrintElement.ActualHeight)
        {
            scale = e.PrintableArea.Height / this.PrintElement.ActualHeight;
        }

        if (e.PrintableArea.Width < this.PrintElement.ActualWidth && e.PrintableArea.Width / this.PrintElement.ActualWidth < scale)
        {
            scale = e.PrintableArea.Width / this.PrintElement.ActualWidth;
        }

        if (scale < 1)
        {
            ScaleTransform scaleTransform = new ScaleTransform();
            scaleTransform.ScaleX = scale;
            scaleTransform.ScaleY = scale;

            writeableBitmap = new WriteableBitmap(this.PrintElement, scaleTransform);
            writeableBitmap.Invalidate();
        }
        else
        {
            writeableBitmap = new WriteableBitmap(this.PrintElement, null);
        }

        Image image = new Image();
        image.Source = writeableBitmap;

        Grid imageGrid = new Grid();
        imageGrid.Children.Add(image);

        e.PageVisual = imageGrid;
    }

    #endregion

    #region Public Methods

    public void Print()
    {
        if (this.PrintElement != null)
        {
            PrintDocument docToPrint = new PrintDocument();

            docToPrint.PrintPage += new EventHandler<PrintPageEventArgs>(DocToPrint_PrintPage);
            
            docToPrint.Print(this.DocumentName);
        }
        else
        {
            throw new NullReferenceException(NullReferenceExceptionMessage);
        }

    }

    #endregion

}

I prefer Snippet 2 over Snippet 3. But again its upto you to decide based on the scenario.

REFERENCES

http://jacekjura.blogspot.com/2010/11/silverlight-printing-fit-to-page.html

Monday, March 21, 2011

jQuery event subscription and UpdatePanel(AsyncPostBack)

We usually bind the events for controls to be handled with jQuery in the $(document).ready. But when I tried to try the same for the controls inside the UpdatePanel, I caught up with an issue. The event hooking works fine until the AsyncPostBack happens, but not after that. Its clear that the initial subscription is nomore valid, for which I searched for that and found out in the web.

PageRequestManager

The endRequest event of the PageRequestManager will provide the right space to solve the issue. We need to resubscribe to the events again upon AsyncPostBack. As per the msdn documentation, the endRequest event is Raised after an asynchronous postback is finished and control has been returned to the browser.
The following snippet will do this for you.

Snippet 1

$(document).ready(function () {
    initiateBinding();

    Sys.WebForms.PageRequestManager.getInstance().add_endRequest(EndRequestHandler);

});

function EndRequestHandler(sender, args) {
    initiateBinding();
}

function initiateBinding() {
    //Do the subscription here.
}


Have specified an example below which describes the above specified solution.

Snippet 2

<%@ Page Language="C#" AutoEventWireup="true" CodeFile="PageRequestManager.aspx.cs"
    Inherits="PageRequestManager" %>

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
    <title></title>
    <script type="text/javascript" src="http://code.jquery.com/jquery-1.5.1.min.js"></script>
    <script type="text/javascript">

        $(document).ready(function () {
            initiateBinding();

            Sys.WebForms.PageRequestManager.getInstance().add_endRequest(EndRequestHandler);

        });

        function initiateBinding() {
            $("input[id$='ClientClickButton']").click(function (event) {
                event.preventDefault();

                alert("Click event handling from jQuery");
            });
        }
        
        function EndRequestHandler(sender, args) {
            if (args.get_error() == undefined) {
                initiateBinding();
            }
            else {
                var errorMessage = args.get_error().message;
                args.set_errorHandled(true);

                //Do error handling here.
                alert("Error : " + errorMessage);
            }
        }
        
    </script>
</head>
<body>
    <form id="form1" runat="server">
    <asp:ScriptManager ID="ScriptManager1" runat="server">
    </asp:ScriptManager>
    <div>
        <asp:UpdatePanel ID="UpdatePanel1" runat="server">
            <ContentTemplate>
                <asp:Button ID="ClientClickButton" runat="server" Text="Client Click Button" />
                <br />
                <br />
                <asp:Button ID="AsyncPostBackTestButton" runat="server" Text="Async PostBack Test Button" />
            </ContentTemplate>
            <Triggers>
                <asp:AsyncPostBackTrigger ControlID="AsyncPostBackTestButton" EventName="Click" />
            </Triggers>
        </asp:UpdatePanel>
    </div>
    </form>
</body>
</html>


REFERENCE
http://msdn.microsoft.com/en-us/library/bb383810.aspx

Monday, February 28, 2011

Check / Uncheck all check boxes in ASP.Net using jQuery

jQuery make our scripting(javascript) life simple. One of the powerful things with jQuery is its Selectors. In ASP.Net while displaying list of data using GridView, DataList, ListView or Repeater control, one of the frequently expected thing is the check all option which is expected to happen on the client side.

To achieve those two things need to be done.

Case 1 : Upon the click of the header check box (CheckAll), all the row check boxes' status need to be changed based on the header check box. Case 2 : Upon click of the row check boxes, header check box status need to be set as checked when all of the row check boxes are checked sand vice versa. Following are the sample code to achieve that.

Snippet 1

<%@ Page Language="C#" AutoEventWireup="true" CodeFile="CheckAllWithJQuery.aspx.cs" Inherits="CheckAllWithJQuery" %>

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
    <title>CheckAll check boxes in DataList</title>
    <script type="text/javascript" src="http://code.jquery.com/jquery-1.5.1.min.js"></script>
    <script type="text/javascript">

        $(document).ready(function () {
            initiateCheckAllBinding();            
        });

        function initiateCheckAllBinding() {
            var headerCheckBox = $("input[id$='HeaderCheckBox']");
            var rowCheckBox = $("#StudentTable input[id*='RowItemCheckBox']");

            headerCheckBox.click(function (e) {                
                rowCheckBox.attr('checked', headerCheckBox.is(':checked'));
            });

            // To select CheckAll when all Item CheckBox is selected and 
            // to deselect CheckAll when an Item CheckBox is deselected.     
            rowCheckBox.click(function (e) {
                var rowCheckBoxSelected = $("#StudentTable input[id*='RowItemCheckBox']:checked");

                if (rowCheckBox.length == rowCheckBoxSelected.length) {
                    headerCheckBox.attr("checked", true);
                }
                else {
                    headerCheckBox.attr("checked", false);
                }
            });
            $("table[id$='StudentDataList'] input[id*='RowItemCheckBox']")
        }       
         
    </script>
</head>
<body>
    <form id="form1" runat="server">
    <div>
    <asp:Repeater ID="StudentRepeater" runat="server">
            <HeaderTemplate>
                <table border="1" cellpadding="5" cellspacing="0" id="StudentTable">
                    <tr>
                        <td>
                            <asp:CheckBox ID="HeaderCheckBox" runat="server" />
                        </td>
                        <td>
                            <b>Id</b>
                        </td>
                        <td>
                            <b>Name</b>
                        </td>
                    </tr>
            </HeaderTemplate>
            <ItemTemplate>
                <tr>
                    <td>
                        <asp:CheckBox ID="RowItemCheckBox" runat="server" />
                    </td>
                    <td>
                        <asp:Literal ID="IdLiteral" runat="server" Text='<%# Eval("Id") %>'></asp:Literal>
                    </td>
                    <td>
                        <asp:Literal ID="NameLiteral" runat="server" Text='<%# Eval("Name") %>'></asp:Literal>
                    </td>
                </tr>
            </ItemTemplate>
            <FooterTemplate>
                </table>
            </FooterTemplate>
        </asp:Repeater>
    </div>
    </form>
</body>
</html>

In the above snippet you find that I've used a repeater control which I've specified a template in such a way the final will output will be of a table structure with id "StudentTable". Also I've specified the header check box with id "HeaderCheckBox" and row check box with id "RowItemCheckBox".

In jQuery, in order to get the instance of the header check, I've used "Attribute Ends With Selector".
$("input[id$='HeaderCheckBox']")
Similarly for getting the instances of row check boxes, I've used "Descendant Selector", that is row item check box selector that is descendant of element with id as "StudentTable".
$("#StudentTable input[id*='RowItemCheckBox']")
This works fine for the above provided template. What need to be done if we have a GridView or DataList? Just we need to identify the html rendering of that control and tweek the selector matching that. To be more clear, in general, GridView and DataList will be rendered as html table by default. Think of a case that you have a DataList with id "StudentDataList", then you can modify the selector as
$("table[id$='StudentDataList'] input[id*='RowItemCheckBox']")
SUMMARY
To achieve Case 2, simply I'm evaluating based on the count of number of row check boxes with that of the number selected row check boxes.

REFERENCE
http://api.jquery.com/category/selectors/
http://www.devcurry.com/2009/08/check-uncheck-all-checkboxes-in-aspnet.html
 

Monday, January 24, 2011

RESTful WCF JSON Service

RAVAGE

Some of my friends inquired me on creating a WCF REST service that supports JSON data format. Observing the nature of their projects, I understood that the client that’s going to utilize/consume this service is going to be a mobile client (iPhone, Andriod,…). So I just created a proof of concept and handed over the service to them. Though this is simple, configuring and hosting the service even locally leads to several exceptions that will drive us ahead of the predicted time. Hence, in order to eradicate those confusions, I explain in this post on creating a simple WCF REST service with just the required configurations and implementations.

IDE : VisualStudio 2010
Framework: .Net 4.0
Assumption: Prior knowledge in .Net and WCF.


Above specified IDE and framework is just to understand the explanations specified in this post. This is also applicable for framework “.Net 3.5” and related IDE (VS 2008).

Projects in our Solution

Project TypePurposeReferences
WCF Service LibraryHolds the DataContract and ServiceContract.System.ServiceModel.Web.dll
WCF Service ApplicationSpecifies the hosting service.Our “WCF Service Library” project reference.
Console ApplicationActs as client application to verify our service in respective data format(JSON here).Our “WCF Service Library” project reference. WCF REST Starter Kit references Microsoft.Http.dll, Microsoft.Http.Extensions.dll and Microsoft.ServiceModel.Web.dll.

WCF Service Library

As we do with simple WCf service, need to create the DataContract.

While specifying the OperationContract, we need to add the WebGet or WebInvoke attribute based on the nature of the operation/ which HTTP method we're going to use . Simply, use WebGet if your service operation wants to respond to GET method, use WebInvoke otherwise.

In order to make use of WebGet or WebInvoke attribute, first we need to add reference to System.ServiceModel.Web.dll. If the dll is not available to add reference, do verify the target framework. If it is ".NET Framework 4 Client Profile", change it to ".NET Framework 4" and then try adding reference.

Snippet 1

namespace VerifcationWcfServiceLibrary
{
    [ServiceContract]
    public interface IVerificationService
    {
        [OperationContract]
        [WebInvoke(ResponseFormat = WebMessageFormat.Json, RequestFormat = WebMessageFormat.Json, UriTemplate = "/AuthCode/AuthReq/", Method = "PUT")]        
        AuthorizationResponse Authenticate(AuthenticationRequest request);

    }

    [DataContract]
    public class AuthenticationRequest
    {
        string userName = string.Empty;
        string authorizationCode = string.Empty;

        [DataMember]
        public string UserName
        {
            get { return userName; }
            set { userName = value; }
        }

        [DataMember]
        public string AuthorizationCode
        {
            get { return authorizationCode; }
            set { authorizationCode = value; }
        }
    }

    [DataContract]
    public class AuthorizationResponse
    {
        bool valid = false;
        string message = string.Empty;
        DateTime validToDate = DateTime.Now;
        string serverURL = string.Empty;

        [DataMember]
        public bool Valid
        {
            get { return valid; }
            set { valid = value; }
        }

        [DataMember]
        public string Message
        {
            get { return message; }
            set { message = value; }
        }

        [DataMember]
        public DateTime ValidToDate
        {
            get { return validToDate; }
            set { validToDate = value; }
        }

        [DataMember]
        public string ServerURL
        {
            get { return serverURL; }
            set { serverURL = value; }
        }
    }
}

Actually I’m trying to expose a service with a single method that uses a custom data type for input parameter(AuthenticationRequest) and another custom data type as return type(AuthorizationResponse).

WebInvoke attribute is the only new thing I’ve specified specific to REST. As you can infer from the above code, we need to explicitly specify the ResponseFormat, RequestFormat and UriTemplate. If we skip out the ResponseFormat and RequestFormat, then it’ll consider the data format as XML. UriTemplate is an important property which specifies the Uri template for the service operation, which is the access point for the service operation. Hence, whichever client going to call the service operation, needs to specify the URI of the specific operation only based on the UriTemplate.

Now create an implementation for the above defined operation contract.

Snippet 2


namespace VerifcationWcfServiceLibrary
{
    public class VerificationService : IVerificationService
    {        
        public AuthorizationResponse Authenticate(AuthenticationRequest request)
        {
            WebOperationContext.Current.OutgoingResponse.StatusCode = System.Net.HttpStatusCode.OK;

            AuthorizationResponse response = new AuthorizationResponse();
            response.Valid = true;
            response.ValidToDate = DateTime.Now.AddDays(7);
            response.Message = request.AuthorizationCode;
            response.ServerURL = "http://info.titodotnet.com/";

            return response;
        }

    }
}

We’re done with WCF Service Library(VerifcationWcfServiceLibrary here).

WCF Service Application

Create WCF Service(.svc) file (VerificationService.svc here).
Add reference to the project WCF Service Library(VerifcationWcfServiceLibrary).
Remove the “.svc.cs” code behind file and view the “.svc” markup.
Modify the “ServiceHost” directive as follows.

Snippet 3


<%@ ServiceHost Language="C#" Debug="true" 
    Service="VerifcationWcfServiceLibrary.VerificationService" 
    CodeBehind="VerifcationWcfServiceLibrary.VerificationService.cs" 
    Factory="System.ServiceModel.Activation.WebServiceHostFactory" %>


As specified, Service should be the fully qualified name of our service class, CodeBehind should be the file name of our service and the Factory should be “WebServiceHostFactory” in order to use WebServiceHost to host the service.

If you miss out the Factory attribute, it’ll throw “unsupported media type” exception. Instead you can also use “WebServiceHost2Factory” of WCF REST starter kit.

Here comes the main part – configuration.

Snippet 4

<system.serviceModel>
  <behaviors>
    <serviceBehaviors>
      <behavior name="DefaultServiceBehavior">
        <serviceMetadata httpGetEnabled="true"/>
        <serviceDebug includeExceptionDetailInFaults="false"/>
      </behavior>
    </serviceBehaviors>
    <endpointBehaviors>
      <behavior name="WebHttpBehavior">
        <webHttp/>
      </behavior>
    </endpointBehaviors>
  </behaviors>
  <services>
    <service behaviorConfiguration="DefaultServiceBehavior" name="VerificationService">
      <endpoint behaviorConfiguration="WebHttpBehavior" binding="webHttpBinding"
        bindingConfiguration="" contract="VerifcationWcfServiceLibrary.IVerificationService"/>
    </service>
  </services>
</system.serviceModel>

We need to specify the webHttp behavior on an endpoint with webHttpBinding. Most of the people get confused while specifying the behavior, whether to use webHttp or enableWebScript. WebScriptEnablingBehavior(enableWebScript) is specially targeted for ASP.NET AJAX client. Hence, do use WebHttpBehavior(webHttp) for pure RESTful WCF service.

Console Application

The purpose of the Console Application(JsonBehaviorTestConsoleApplication here) is to validate and verify the service we created and to run our unit test cases if any. As I specified earlier since the client can be any mobile client or any other application, we need to test our services by sending and receiving object in JSON(or XML) specific format. For accessing the service, we can make use of the WCF REST starter kit, which provides the required helper functions and wrapper classes.

Make sure that you add the references that I mentioned initially in this post.
The reason for having “WCF Service Library” project reference is because, we can create .Net CLR(managed) instance for the data contract we have in “WCF Service Library” project, assign the values we needed and convert the object into JSON(or XML) specific format.

Following is the snippet for accessing the above created service operation.

Snippet 5

namespace JsonBehaviorTestConsoleApplication
{
    class JsonTestClient
    {
        static void Main(string[] args)
        {
            AuthenticationRequest request = new AuthenticationRequest();
            
            request.UserName = "test username";
            request.AuthorizationCode = "test authcode";

            using (HttpClient restClient = new HttpClient())
            {
                HttpContent paramContent = HttpContentExtensions.CreateJsonDataContract(request);
                
                Console.WriteLine("*******************AuthCode/AuthReq*******************");
                HttpResponseMessage resp = restClient.Put("http://localhost.:1517/VerificationService.svc/AuthCode/AuthReq", paramContent);
                resp.EnsureStatusIsSuccessful();
                Console.WriteLine("*******************Header*******************");
                Console.WriteLine(resp.Headers.ToString());

                Console.WriteLine("*******************Content*******************");
                var result = resp.Content.ReadAsString();
                Console.WriteLine(result);

                Console.Read();
            }
        }
    }
}

The service operation need to be called only with the UriTemplate we specified in the WebInvoke attribute using the HttpClient instance with respective method based on the Method attribute we specified earlier.

As most of might have already noticed, I’ve used “localhost.:1517”(affix dot(.) at the end of "localhost") in the above snippet, in order to capture my localhost traffic in fiddler which otherwise won’t get caught. Using Fiddler for verifying the data format and actual values passed across the wire is very common and effective.

SUMMARY

You can also bypass WCF Service Library project and create the required contracts and services in WCF Service Application itself. But make sure that you don’t add direct WCF Service Application project reference in the Console Application in order to use the data contract. Instead copy the required data contract locally in your Console Application.

REFERENCE

http://msdn.microsoft.com/en-us/library/dd203052.aspx
http://msdn.microsoft.com/en-us/library/ee391967.aspx
http://blogs.msdn.com/b/endpoint/archive/2010/01/18/automatic-and-explicit-format-selection-in-wcf-webhttp-services.aspx

Saturday, January 8, 2011

Regulator / FlowControlManager Pattern

Regulator is the object which holds the responsibility of managing the control flow between group of related objects by handling the state change of the objects and passing messages between them.

Components

Regulator / FowControlManager : Holds the instances of related objects (Colleagues) and observes the Colleagues for change in their state.
 Colleague : Ability to notify the change in its state and focuses on its core responsibility. Wont hold reference to any of related objects or Regulator.

Scenario

Consideration : Silverlight application following MVVM.
Generally in Silverlight applications we use to separate the application into individual modules in order to create seperate XAP files and download to use it as and when needed. Let us imagine such a small module which has a landing page displaying some details along with few links(Detail1,Detail2). Upon clicking the links, the respective views(DetailView1, DetailView2) need to be displayed on the desiered location on the landing page itself. Also different messages need to be sent to different views based on the behavior and actions performed in other views and landing page.

Simply we can handle this by hand over the entire responsibility to the ViewModel of the landing page. If we do so, the landing ViewModel need to hold this control flow management and message passing mechanism in addition to its functionality.

Instead if we have an object that manages the flow control and message passing, then the respective ViewModels can just concentrate on their functionality without bothering about flow of control and message passing.

If Detail1 link is clicked on the landing page, Regulator takes this responsibility by observing Detail1 link got clicked on landing page, pass required details to the DetailView1.ViewModel and passes the DetailView1 to the LandingPage.

Component Responsibility

Colleagues just notifies about change of state which need to be observed.Regulator observes the state changes of the Colleagues as it holds their instances, passes the needed messages to respective instances and controls the flow.
State change can be notified through effective use of events and NotifyPropertyChanged mechanism we use in Silverlight.

Related patterns

Most of you might have already smelled the flavour of Mediator, Observer and Facade pattern.
Its true but the notion differs.
Unlike Mediator pattern, Colleagues in Regulator pattern won't hold reference to Regulator instance. Regulator itself subscribes to required state change notification of Colleagues.

Edit
10JAN2011
My friend upon having a glimpse at the title asked me where did I got the title from? Actually I should have specified about this at time of writing the article itself. I just named it based on the functionality and nature of implementation. Just thinking whether I've done anything unlawful.

Saturday, December 25, 2010

Pattern for Silverlight child window

RAVAGE


I just wanted to convey the enitre motivation of this article in a simple sentence which resulted in the 'keyword like' entry as the title "Pattern for Silverlight child window". As per my observation the UI approach (way of presentation to the end user) for Silverlight varies with that of ASP.Net UI. In most of the Silverlight applications that I visited, I didn't noticed the heavy usage of the child (popup) window for interaction purpose except the light usage of notification purpose such as confirmation / information display dialogs. If we do need to use the child window for interaction purpose, then the first idea that we need to plan is about how to share object between the parent and the child window.


I came across a similar situation in my project where my client wanted to present the child window for interaction purpose in order to produce a native UI approach. I thought of implementing this without breaking the MVVM pattern. To achieve that, first I thought of sharing the same ViewModel object between the parent and child. It got ruled out immediately due to well know reasons of maintainabiltiy and scalability. Not all the members of the object will be common for both parent and child window. Hence sharing same ViewModel / object won't be the right choice.

Next option is to have two seperate view models for parent and child dialog holding reference to each other. This is better than the previous one, but think of the case having more than one child window which will result in the parent holding reference to each of the child window and each child window holds reference to the parent. Going one step forward, think of a case having more than one parent view and multiple child view which are related, might end up with each object having reference to other as a worst case scenario. So, are there any pattern that provides solution for the above case? Yes, as many article over the web  suggests, Mediator pattern provides us the solution.

MEDIATOR FOR PARENT-CHILD VIEW

Mediator holds the instances of the parent and child objects and manage the interactions between the objects. The parent and child objects in turn holds the reference of the mediator and notifies the mediator about the relevant changes. Having received the change notification, the mediator will call the required update / action aginst the objects in relation to the change.

I implemented this change in my project and worked fine for me. But out of curiosity, I further fine tuned this implementation such that the parent and child view objects interact with the mediator using Observer pattern.

The implementation goes something like as follows:
I created an abstract class called Colleague which holds the instance for the Mediator as a property and an Update method which can be overriden by the class that inherits it.
Then I created an Mediator class which provides the methods to Attach, Detach and Notify. The Attach methods takes in an object of type Colleague as parameter and adds to a list of Colleague(to be notified). The Detach method takes in an Colleague type as parameter which removes from the list of Colleague. The Notify method iterates through the list of Colleague object and calls the Update method on each Colleague object.

The parent and child ViewModels inherits the Colleague class and overrides the Update method if needed. For example I was about to pass a selected item from my child ViewModel to my parent ViewModel. To achieve that upon selecting a particluar item from a list of items in the child view, I'll call the Notify method of my Mediator by passing the selected item. My parent ViewModel in its constructor will subscribe to receive the notification by calling and passing itself as paramter to the Attach method of the Mediator object. Upon the Mediator receives the notification from child view, it calls Update method of the Colleague (parent in this case) which got  subscribed to it.

STRUCTURE




SUMMARY

I actually have described an idea about the implementation in my project. Based on my project scenario, I had extended the same concept to handle multiple parent and child view models for which the usage of Observer pattern for interaction with the Medaitor helped me to produce a scalable and maintainable code. On the whole my idea is to implement a Mediator which acts as a change request manager holding the responsibility of interaction between the ViewModels. In my case, since I had only one change request manager (Mediator), I eliminated the introduction of abstract Mediator class. After completing my implementation, I realized that single instance for my Mediator holds good for my scenario. Hence, finally I also implemented Singleton pattern for my Mediator class.

Wednesday, December 1, 2010

System.Security.SecurityException: Dialogs must be user-initiated.

The Printing API in Silverlight 4 helps us a lot like printing the entire page, printing a specific portion of a page……

In order to get a quick idea on the print API do have a look at this(A look at the Printing API in Silverlight 4) article on silverlightshow.

While implementing this print API in my project I came across the following error in some scenarios.

System.Security.SecurityException: Dialogs must be user-initiated.

This error message initially leads to lot of confusion and upon search found the cause. As all the developers do, I was debugging the code by putting breakpoint. The problem is I put the breakpoint over the line where the object initiation for PrintDocument happens. Something like……
 
 
 
So when the exception “ System.Security.SecurityException: Dialogs must be user-initiated.” got raised while using print API for Silverlight 4 in debug mode, ensure that you didn’t put breakpoint over the code that invokes the Print method.

REFERENCE

http://johnpapa.net/silverlight/printing-tip-ndash-handling-user-initiated-dialogs-exceptions/
Creative Commons License
This work by Tito is licensed under a Creative Commons Attribution 3.0 Unported License.