How to reset saved sort and column settings of the Silverlight datagrid

The LightSwitch Silverlight client has many nice features that you get for free.

One of these features is that it automatically saves Sort and Column user customizations on the datagrids. When a user sorts, resizes or reorders the columns of a datagrid, these customizations are stored in the IsolatedApplicationStorage.

For some cases, this feature isn’t that handy. If you have a query with an advanced order, you don’t want the client to remember any sort customizations. Luckily you can easily reset these settings by deleting these IsolatedApplicationStorage settings.

Example:
On initialize, delete all saved Sort and Column settings

partial void Application_Initialize()

{

    IsolatedStorageSettings appSettings = IsolatedStorageSettings.ApplicationSettings;

    var sortAndColumnSettings = appSettings.Where(

            setting => setting.Key.Contains("SortSettings") || setting.Key.Contains("ColumnSettings")).ToList();

    foreach (var setting in sortAndColumnSettings)

    {

        appSettings.Remove(setting.Key);

    }

}

If you want to do this more precise, this is the pattern of the Key names:

Column settings: Screen.DataGridControl.ColumnSettings
Sort settings: Screen.QueryName.SortSettings

Enjoy!
Michiel

Advertisements

extension methods for non-autoexecutable filters in the LightSwitch SilverLight client

Introduction

My current project is a LightSwitch SilverLight client that works on some rather large datasets. For certain screens in this application it’s desirable to set the query as non-autoexecutable because most users would want to fill in multiple filter fields before invoking the time consuming query.

However, when setting a query as non-autoexecutable you also lose some functionalities in the Silverlight client:

  • Sorting & Paging in the LightSwitch SilverLight grid doesn’t work.
  • When pressing the Enter key on one of the Filter fields, the query should be executed. (UI Guideline)

Sorting & Paging in the LightSwitch SilverLight grid doesn’t work

This is a known issue since the V1 beta. You can discuss about it being a bug in LightSwitch but the LightSwitch team says this is the correct behavior.
Luckily, Justin Anderson gave an easy solution for this in the following thread:

http://social.msdn.microsoft.com/Forums/vstudio/en-US/62ef7adf-3b34-403c-83b0-ad074023a076/getting-default-behavior-using-queries-with-autoexecute-false

When pressing the Enter key on one of the Filter fields, the query should be executed.

This can easily be solved by using the KeyUp event of the UIElement. You can add this on every filter field, but i prefer to use the GetModel method of the screen to find all fields in my Filter Group control.
By doing this, you won’t miss to add the event if you would later add some new filter fields.

Extension method

Let’s DRY both solutions into one practical ScreenExtensions class 🙂

using System;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using Microsoft.LightSwitch;
using Microsoft.LightSwitch.Framework.Client;
using Microsoft.LightSwitch.Presentation;
using Microsoft.LightSwitch.Presentation.Extensions;
using Microsoft.LightSwitch.Client;
using Microsoft.LightSwitch.Model;
using System.Linq;
using Microsoft.LightSwitch.Threading;
using System.ComponentModel;

namespace LightSwitchApplication
{
    public static class ScreenExtensions
    {
        /// <summary>
        /// Execute an action when the EnterKeyUp event has been fired on the given control or its children controls.
        /// </summary>
        /// <param name="control">The name of the control. This can also be a group control.</param>
        /// <param name="callback">The action that needs to be executed.</param>
        public static void ExecuteActionOnEnterKeyOnChildrenOfGroupControl(this IScreenObject screen,
                                                                           string control, Action callback)
        {
            IContentItemDefinition itemDef = screen.Details.GetModel().GetChildItems(true)
                                                         .OfType<IContentItemDefinition>()
                                                         .FirstOrDefault(c => c.Name == control);
            foreach (IContentItemDefinition item in itemDef.ChildContentItems)
            {
                if (item.Kind == ContentItemKind.Group)
                    ExecuteActionOnEnterKeyOnChildrenOfGroupControl(screen, item.Name, callback);
                else if (item.Kind == ContentItemKind.Details || item.Kind == ContentItemKind.Value)
                    ExecuteActionOnEnterKeyUp(screen, item, callback);
            }
        }

        private static void ExecuteActionOnEnterKeyUp(IScreenObject screen, IContentItemDefinition item, Action callback)
        {
            screen.FindControl(item.Name).ControlAvailable += (sender, args) =>
            {
                UIElement uiElement = args.Control as UIElement;
                uiElement.KeyUp += (sender1, args1) =>
                {
                    if (args1.Key == Key.Enter)
                    {
                        screen.Details.Dispatcher.BeginInvoke(callback);
                        args1.Handled = true; //this also fixes the Modal Window Enter Key bug. Set to false if you want the event to bubble.
                    }
                };
            };
        }

        /// <summary>
        /// Execute an action when Paging or Ordering occurs on the given collection.
        /// </summary>
        /// <param name="collectionProperty">The INotifyPropertyChanged property of the collection.</param>
        /// <param name="callback">The action that needs to be executed.</param>
        public static void ExecuteActionOnPagingOrSorting(this IScreenObject screen, INotifyPropertyChanged collectionProperty, Action callback)
        {
            Dispatchers.Main.Invoke(() =>
            {
                collectionProperty.PropertyChanged += (sender, e) =>
                {
                    switch (e.PropertyName)
                    {
                        case "PageNumber": // Changed when current page is changed
                        case "SortDescriptors": // Changed when sorting is changed
                            screen.Details.Dispatcher.BeginInvoke(callback);
                            break;
                    }
                };
            });
        }
    }
}

Use it in your Created method of the screen:

public partial class SearchScreen
    {
        private const string _controlFilterGroupBox = "FilterGroupBox";

        partial void SearchScreen_Created()
        {
            this.ExecuteActionOnEnterKeyOnChildrenOfGroupControl(_controlFilterGroupBox, ReloadCustomers);
            this.ExecuteActionOnPagingOrSorting(this.Details.Properties.Customers, ReloadCustomers);
        }

        private void ReloadCustomers()
        {
            this.Customers.Refresh();
        }
    }

Conclusion

This extension class now solves 2 issues when working with manual execution queries.
Enjoy!

Concurrent Ria Service (and Web Api) requests in LightSwitch

Introduction

Recently I’ve developed a LightSwitch SilverLight web app with some expensive queries in a RIA Service.
In a certain screen, I had some filter criteria from a RIA Service.
The first filter was a rather expensive query (~2s) which I couldn’t easily optimize. The other filter criteria were cascading from that first filter and thus didn’t need any processing time (if filter1 is null returns null).

What I noticed is that all these RIA Service requests were executed at the same time, and also responded at the same time. All these filter drop down lists spinned for more then 2 seconds :S

Problem

If you have a screen in which certain RIA Service requests are executed consecutively, you may notice that one expensive query can hold up any following queries of a RiaService. This occurs in both the SL as the HTML5/Sharepoint client.

Example setup: 2 requests consecutively executed in 1 LightSwitch SL screen.

public class CustomerDomainService : LightSwitchDomainServiceBase
{
    [Query(IsDefault = true)]
    public IQueryable<CustomerDTO> GetDefault()
    {
        return null;
    }
    [Query(IsDefault = false)]
    public IQueryable<CustomerDTO> SlowRequest()
    {
        //emulate a slow request
        System.Threading.Thread.Sleep(2000);
        return from cust in Context.Customers
                select new CustomerDTO
                {
                    FirstName = cust.FirstName,
                    LastName = cust.LastName,
                    FullName = cust.FirstName + " " + cust.LastName
                };
    }

    [Query(IsDefault = false)]
    public IQueryable<CustomerDTO> FastRequest()
    {
        return from cust in Context.Customers
                select new CustomerDTO
                {
                    FirstName = cust.FirstName,
                    LastName = cust.LastName,
                    FullName = cust.FirstName + " " + cust.LastName
                };
    }
}

Both requests take +2s (IE9 – F12 Developer Tools)

image

This behavior doesn’t occur when working with the intrinsice LightSwitch dataservice, which is actually also a WCF Data Service. Strange…

Solution

Read about the ASP.NET Session State in MSDN, section Concurrent Requests and Session State.

This implies that if a client executes 3 consecutive requests, the server will process the requests serially because by default the session state is locked. Luckily, you can set the session state read-only so that this locking doesn’t occur but you still have access to the session state for any security implementations. This is how:

  1. Create a Global.asax file, if you don’t have one already.
  2. Set the sessionstatebehavior to readonly for any WCF Ria Service requests:
  3. protected void Application_BeginRequest(object sender, EventArgs e)
    {
        if (Context.Request.CurrentExecutionFilePath.EndsWith(".svc"))
        {
            Context.SetSessionStateBehavior(System.Web.SessionState.SessionStateBehavior.ReadOnly);
        }
    }
    

Now all requests to your WCF Ria Services will have a read-only sessionstate, and can happen in parallel 🙂

image

What about Web API Request?

This problem also occurs with Web API Requests, the solution can be easily applied by adding an attribute to your controller:

[SessionState(System.Web.SessionState.SessionStateBehavior.ReadOnly)]
public class ParallelController : Controller
{
    //...
}

Conclusion

Happy performance tuning! 🙂

Shrinking a LightSwitchV3 project for file transfer

Introduction

A small LightSwitch solution can take quite some diskspace, ~150MB.

Since the LSv1 beta version, there’s a batch file made by Oscar Agreda for shrinking some redundant folders that Visual Studio creates when building in debug or release.

With the current version of LightSwitch, you have 2 options for cleaning your project: using an update of this batch, or by using the Clean Solution functionality of Visual Studio.

LSCleanV3.bat

Here’s an update of the script that works with the current version of LightSwitch, having a Desktop and/or a HTML Client:

 rd /q /s Bin\Debug
 rd /q /s Bin\Release
 rd /q /s _Pvt_Extensions

for /d %%p in (*Client) do (
 rd /q /s "%%p\Bin\Debug"
 rd /q /s "%%p\Bin\Release"
 rd /q /s "%%p\obj\Debug"
 rd /q /s "%%p\obj\Release"
 )

for /d %%p in (*Server) do (
 rd /q /s "%%p\Bin\Debug"
 rd /q /s "%%p\Bin\Release"
 rd /q /s "%%p\obj\Debug"
 rd /q /s "%%p\obj\Release"
 )

Remember to close your solution first, then run the script from your LS project folder.
With this script you can shrink the project folder to ~15MB, compressable to 3MB.

Clean Solution

With the first LightSwitch version, cleaning up the solution didn’t work for the LightSwitch project.
The current version has fixed this so you can also remove the redundant build files by cleaning your solution, do this once for each build configuration you used. This also cleans up the build files of additional projects, for instance your RIA Service class library project, which the batch file doesn’t.

After the clean, you can manually delete the _Pvt_Extensions folder.

Enjoy!
Michiel

Computed Fields in the HTML5 client

Introduction

The motivation of LS has always been to make typical LOB patterns easy to implement without limiting the more advanced functionalities.

With the introduction of the HTML Client however, some comforts when developing LS Silverlight applications have disappeared. Many of you feel like JavaScript is holding you back. JavaScript is a powerful language, but definitely requires some studying when you’re accustomed to a strongly typed class-based OOP language like C# or VB.NET.

So let’s start with learning how to implement computed fields in the HTML Client.

Computed Properties in the HTML Client

Currently, there are 2 ways to do this:
– By using a RIAService
– By using the contentItem databinding

Implementing computed properties by extending your entity with a RIA Service is well described in this post by Michael Washington. It is great when having a more complex computation or when you’ll also want to sort or query on the property. However, it also adds some complexity to your code and has a negative impact on maintainability.

Computed properties by using the contentItem databinding is a great option when you’re not afraid of some JavaScripting. So let’s found out a good pattern, in a Object-Oriented way!

Create a simple application with a entity Customer. A customer has a First Name and a Last Name and … a computed full name.
At the Server-side and the SL Client, you can use a shared function for this.


public partial class Customer
{
  partial void FullName_Compute(ref string result)
  {
    result = String.Format("{0} {1}",FirstName,LastName);
  }
}

At the HTML Client however, you can’t add a computed field by using the designer. So let’s do some JavaScripting! (add your own emotion ;))

Open up the Customer.js file and add the following function to the Customer’s prototype.

myapp.Customer.prototype.getFullName = function () {
  /// <summary>Computes the full name of the person.</summary>
  /// <returns type="String">The full name.</returns>
  if (this.FirstName && this.LastName) {
    return this.FirstName + " " + this.LastName;
  }
  return "";
};

By doing this, you’ve added a ‘public’ function to the Customer objects (instantiated after or before this line!). If you’re not familiar with JavaScript, MDN offers some nice documentation, including a introduction to Object-Oriented JavaScript. If you frown when you hear OOP and JavaScript in the same sentence, you’ve got some reading to do 🙂

Note that we’ve also added some Intellisense documentation. Every time we are using a Customer object, we can now see some documentation about our added computed fields if we reference this js file.

Now add a Browse screen and a Add/Edit screen for your Customers.
At the browse screen, use the postrender event of the Customer rowtemplate to replace the summary text shown:

/// <reference path="../GeneratedArtifacts/viewModel.js" />
/// <reference path="Customer.js" />
myapp.BrowseCustomers.RowTemplate_postRender = function (element, contentItem) {
  function updateCustomerRow() {
    /// <var type="myapp.Customer"></var>
    var customer = contentItem.value;
    $(element).text(customer.getFullName());
  }
  contentItem.dataBind("value.FirstName", updateCustomerRow);
  contentItem.dataBind("value.LastName", updateCustomerRow);
};

Note that the Intellisense for JavaScript doesn’t provide an accurate list of identifiers for the contentItem object.
Intellisense

However you can help Intellisense by declaring the variable as a Customer object. By doing this and referencing the added functions to the Customer prototype, you should now see your computed property function:
Intellisense2

This was easy, up to the Add/Edit screen. Drag the Customer data item to the first rows layout and change the added control to a summary control. Change the display name to Full Name and data bind analogously as with the RowTemplate in the Browse screen.
AddEditCustomer

Some improvements

This pattern is a basic building block and can easily be improved.

  • It would make sense to override the Customer toString function with a custom summary property. However, at first sight the entity’s summary property doesn’t make use of the toString function.
  • You can DRY the dependency data binding by implementing a function like this on the Customer prototype:
    myapp.Customer.prototype.bindToFullName = function (contentItem,callbackFn) {
      /// <summary>Bind to the full name</summary>
      /// <param name="contentItem" type="Element">The contentItem element.</param>
      /// <param name="callbackFn" type="function">The function to call back when the data binding is triggered.</param>
      contentItem.dataBind("value.FirstName", callbackFn);
      contentItem.dataBind("value.LastName", callbackFn);
    };
    

    This would be especially useful when you are dealing with a dependency chain!

Conclusion

We now have 2 good solutions for having a computed property. One does not supersede the other, evaluate case by case which solution you’ll use!

Lightswitch has made a switch!

I’ve met LightSwitch in november 2011. Back then it was marketed as a ‘Citizen developer’ framework.
With LightSwitchV1 you could easily migrate from Access and get a scalable 3-tier application based on modern technologies, which is fantastic for developers in the category of ‘Citizen developers’, whatever that may be.

Though I don’t classify in this category, my first experiences with LightSwitchV1 back then were positive. Ok, any junior developer can quickly build its own LightSwitch application. That’s good, but suprisingly the framework simplyness didn’t limit some more advanced functionalities. You can write your own queries in LINQ and thanks to RIA Services you can do your own plumbing, if there’s the need for it. The MVVM pattern works really great and the automated dependency tracking for computed properties and validations are superb! Since Q1 2012 I’ve developed several small-to-medium sized LOB applications, and I’ve never hit a brick wall.
Of course LS had some downsides, in my experience there were 3. Localization, the SL client (for customer facing applications) and multi-developer support.

And then LightSwitchV2 came out in Visual Studio 2012. At first sight, the version V2 didn’t improve that much. The UI was a bit better and a OData Service is exposed instead of a hard-to-expose RIA service. The latter is actually a bigger improvement then I first assumed. It gives the possibility to consume your data from other applications that can consume OData like PowerPivot, Java applications, your company’s Drupal website and many others! This interoperability heavily improves the maturity of the LS framework.

And now LightSwitchV3 is released within VS 2012 Update 2. With this update you can easily create simple mobile HTML5 companion apps for tablets and mobile phones based on jQuery UI and other standard libraries. You can also create Sharepoint apps and integrate all this goodness in office 365! The LightSwitch team also built-in a easter gift:  localization support.

If you follow the history of Lightswitch, you can clearly state that the framework has made a switch. From a simple and medium rich citizen developer framework to a equally simple but far richer framework in which any developer can easily build a interoperable and multi-platform LOB application!

Now the time has come to stop bragging how quick you can build a small Lightswitch application. Start bragging how rich, usable and qualitative your LS applications can be created without much effort and costs.

Lightwitch is a framework of the new digital norm!

If you follow the history of LS, you can state that Microsoft has put much effort in Lightswitch. They believe in Lightswitch and so do I. So I’ll start blogging about it!