Wednesday, November 7, 2012

De-fault Screens

If you have tried implementing something that required a mechanism more complex than the default one provided by LightSwitch regarding default screen selection, you already are familiar with the limitations.
For the ones that don’t get what I mean, a very common example is open the details screen of an object from a list containing items from a view (database view) that include the id of the original object. Imagine you have a Customer entity and a CustomerInfo entity that comes from a database view bringing aggregated data of the customer from various tables. Selecting and editing the CustomerInfo you want to edit the Customer not the CustomerInfo.
The above is only one fast example. Another would be having Add/Edit Screens i.e. screens that can handle both create or edit an existing object. In any case if you need to modify the default signature of a default screen (adding an extra parameter for example) causes this screen to be unselected as default screen because LightSwitch mechanism for default screens cannot handle it.
Going back to the Customer/CustomerInfo example, I have come across at least a couple of posts in threads trying to find a way to “override” the default click behavior of links in collections. In our example clicking on the link of the customer’s name (for example) will open a default screen for CustomerInfo.
So, one easy solution is creating a default screen for CustomerInfo where upon load you will open the Customers default screen passing the id of the customer and close the current one. But how do you handle allowing only one instance of a details screen per object? Or all other issues that might come come up? (Imagine you have more than one view entities for the Customer entity).
Anyway, this is my approach at solving this issue, that works fine for me in a big project whose development runs for almost a year now (and by the way is the reason for not posting very often –understatement of the year- here lately):
Reintroducing the ShowDefaultScreen method of the Application in the Client project along with an interface defined and implemented by all screens that need to be involved.
  • This is the interface definition:
public interface IDefaultScreen : IScreenObject
{
    bool Shows(IEntityObject instance);
    IEntityObject Object { get; }
}




  • This is the code in Application.cs:
public new void ShowDefaultScreen(IEntityObject instance) {
      this.ShowOrFocusDefaultScreen(instance);
}

public void ShowOrFocusDefaultScreen(IEntityObject entity) {
  foreach (IActiveScreen activeScreen in this.ActiveScreens) {
    if ((activeScreen.Screen is IDefaultScreen) &&
        (activeScreen.Screen as IDefaultScreen).Shows(entity)) {
          activeScreen.Activate();
          return;
        }
      }
  entity.Details.Dispatcher.BeginInvoke(() => {
    if (!HandleEntityDefaultScreen(entity))
      base.ShowDefaultScreen(entity);
  });
}

private bool HandleEntityDefaultScreen(IEntityObject entity) {
  switch (entity.GetType().Name) {
    case "CustomerInfo": {
        this.ShowEditCustomer((entity as CustomerInfo).Id);
        return true;
      }
      .
      .
      .
  }
  return false;
}
In the code displayed above the implementation in HandleEntityDefaultScreen code is just to demonstrate the concept. The ellipsis below the first case implies you can write whatever is required by your application.


  • And finally this is the code from the Customer details screen that implements IDefaultScreen:
public partial class EditCustomer : IDefaultScreen
{
   .
   .
   .
  #region IDefaultScreen Members
  public bool Shows(IEntityObject instance) {
    return (instance is CustomerInfo) &&
           (instance as CustomerInfo).Id.Equals(this.CustomerProperty.Id);
  }

  public IEntityObject Object {
    get { return this.CustomerProperty; }
  }
  #endregion
}

The careful reader will notice that the IEntityObject Object { get; } property exposed by IDefaultScreen is not used in the sample code. I ported the code exactly as I use it in my projects (apart from the changes made to demonstrate the Customer/CustomerInfo paradigm) where this property is used for a different purpose. You can either remove it or find a good use for it Winking smile.

5 comments:

  1. Very inventive post,... as always.
    Glad you are back in the LightSwitch blogosphere.
    Thanks for sharing Kostas !

    ReplyDelete
  2. Thanks for the kind words, Paul.
    The truth is I have so many things to share and so little time.
    I will try to be more "frequent" but I cannot promise...
    Thanks again.

    ReplyDelete
  3. You love using interfaces, don't you, lol..

    I echo Paul's "inventive" comment. :-)

    ReplyDelete
  4. This looks really promising; however, when I attempted to implement, adding a breakpoint to the "public new void ShowDefaultScreen(IEntityObject instance)" method reveals the method does not get called. I am using LightSwitch 2012. Have you tried your solution with LS 2012?

    ReplyDelete