Showing posts with label Under the Hood. Show all posts
Showing posts with label Under the Hood. Show all posts

Wednesday, May 8, 2013

CLASS Services

The next version of CLASS extensions is going to be available by the end of summer with some really interesting goodies added. But, with HTML client (even before it’s official release) being in the center of interest, I feel like Silverlight extensions, are going to be soon, if not already, out of demand.
So, until I find the time to catch up with HTML client, I though on focusing more to stuff that are client implementation independent. That’s why, apart from the next version of CLASS Extensions for Silverlight clients, I plan on releasing a Server/Common side extension pack called CLASS Services.
If you are one of the blessed people Angel that purchased CLASS Extensions, you already know of File Server service included, that provides file based content access to your server.
Two more of the services that are going to be included in this new CLASS Services pack can be found in this sample I posted in MSDN. Ok, I am not crazy, I know that if I share the code no-one will buy the extensions, but these two simple (as regards to the coding effort) services, are just a part of what CLASS Services is going to include. Either way, I believe my Lightswitch community contribution, even if not as extensive and steady as other people’s, makes clear that the idea is sharing and not making money out of it.
Regarding the sample, I must underline that the application itself is just an example. And I want to stress this point, because some would say that, what the sample application displays is not DI. The idea of the sample was not to display DI in action but just to display how you bind the RIA services together, to do whatever you like. MEF is used as a DI framework by the RIA Service to provide the required functionality.
Also, I have to admit that if properly implemented, by the book, restrictions and conventions in imports and exports of MEF (or any other DI framework for that matter) would make any additional configuration (like the one my sample suggests) pointless.
Nevertheless, IMHO, you really have to be a DI purist, not to recognize the potential provided by these two services. An example: In Computer Life, we plan (George stop calling me names Smile with tongue out) to implement a generic (extension) way to support EAV model in our Lightswitch applications. It’s so obvious how useful the Domain Types RIA service will be for this implementation that I won’t even bother writing anything to prove it.
Anyway, I hope you find these two RIA services useful. I realize you have to go far beyond the “Coding is optional” motto to make good use of this kind of services, but I believe the Lightswitch development community, has already given the answer to that (not at all well-intentioned) “It’s the next MS-Access” hype…

Tuesday, February 5, 2013

Can I delete this?

In a previous post I suggested a way to override default LightSwitch add/edit behavior. In this post I will suggest an override to default delete behavior.
It has happened many times to delete entries, only to get an error message, when trying to save, informing me about reference constraints being violated and the action cannot be completed. This is not much of problem when you try to delete one record (apart from the fact this message is ugly and not one that I would allow my end users to deal with, for that matter). But when trying to delete many records you have no clue which was the one (or more) record(s) that cannot be deleted. In this case all you can do is delete-save until you find the record that cannot be deleted. And when you find it, you have to refresh your screen or undo the changes of the current record (a future article will suggest a way to do this).
What I needed was a good generic way to be able to set Delete_CanExecute result. This is my suggestion using (what else) extension methods, interfaces (Yann will love me for this Smile) and an attribute.
First the interface to help decide if an instance can be deleted or not:
public interface IDependencyCheck : IEntityObject
{
  bool CanDelete { get; }
}



Quite simple you must admit. Keep in mind interfaces you want to use in your common project have to be accessible both by Server and Client project also. If someone here thinks of EntityName_CanDelete, someone forgets that it is a server side hook.

Now an attribute to help us decide which of the dependencies an entity might have are strong ones, meaning that the referential integrity of the database or your business logic if you don’t use database constraints, would not allow an instance to be deleted.
[AttributeUsage(AttributeTargets.Class)]
public class StrongDependencyAttribute : Attribute
{
  public StrongDependencyAttribute(string dependencies) {
    this.dependencies = dependencies.ToStringArray();
  }

  public string[] Dependencies {
    get { return dependencies; }
  }

  private readonly string[] dependencies;
}

This attribute takes a list of strong reference property names as comma delimited string (or whatever delimiter you like for that matter) using these simple extensions:
public static string[] ToStringArray(this string strings) {
   return strings.ToStringArray(',');
}

public static string[] ToStringArray(this string strings, char delimiter) {
   return (from string item in strings.Split(new char[] { delimiter }, StringSplitOptions.RemoveEmptyEntries)
               select item.Trim()).ToArray();
}



If you want to use another delimiter just replace
this.dependencies = dependencies.ToStringArray();



in the constructor with (for example):
this.dependencies = dependencies.ToStringArray(';');

Or if you want, you can have one instance of the attribute for each property you want to check and avoid delimited strings altogether. Plenty of choices…Smile.

Keep in mind that if you declare no instance of this attribute but you implement IDependencyCheck then ALL dependencies will be considered as strong ones and checked for integrity.

This attribute needs to be accessible by the Common project only.

Now, all that said, there are two extension methods that will help us do the job.

The first one needs to be accessible by the Client project only:
public static bool CanDeleteSelection(this IScreenObject screen, string collectionName) {
   if (!screen.HasSelection(collectionName))
     return false;
   IVisualCollection collection = screen.Details.Properties[collectionName].Value as IVisualCollection;
   if (collection.SelectedItem is IDependencyCheck)
     return (collection.SelectedItem as IDependencyCheck).CanDelete;
   return true;
}



Please note I am using IScreenObject.HasSelection extension that has already been introduced in a previous post. That’s why I am not checking if the cast to IVisualCollection is successful (not null).

The second one has to be accessible by the Common project only:
    public static bool CanDelete(this IEntityObject entity) {
      IEnumerable<IEntityCollectionProperty> collectionProperties = 
        entity.Details.Properties.All()
        .Where(p => p.GetType().GetInterfaces()
          .Where(t => t.Name.Equals("IEntityCollectionProperty"))
          .FirstOrDefault() != null)
        .Cast<IEntityCollectionProperty>();
      if (collectionProperties == null)
        return true;
      List<string> strongDependencies = new List<string>();
      IEnumerable<StrongDependencyAttribute> dependencies = 
        entity.GetType()
        .GetCustomAttributes(typeof(StrongDependencyAttribute), false)
        .Cast<StrongDependencyAttribute>();
      foreach (StrongDependencyAttribute dependency in dependencies)
        strongDependencies.AddRange(dependency.Dependencies);
      bool hasDependencies = strongDependencies.FirstOrDefault() != null;
      bool canDelete = true;
      foreach (IEntityCollectionProperty property in collectionProperties) {
        if (hasDependencies &&strongDependencies.FirstOrDefault(d => d.Equals(property.Name)) == null)
          continue;
        IEnumerable value = entity.GetType()
          .GetProperty(string.Format("{0}Query", property.Name))
          .GetValue(entity, null) as IEnumerable;
        try {
          if (value != null && value.GetEnumerator().MoveNext()) {
            canDelete = false;
            break;
          }
        }
        catch {
          continue;
        }
      }
      return canDelete;
    }



Although it’s obvious at first glance what the code does Hot smile, I will give brief explanation:

If there is not a StrongDependencyAtttibute defined for the entity then all reference properties are checked and if at least one has members then the entity cannot be deleted. If a StrongDependencyAtttibute is defined for the entity then only reference properties included in the attribute are checked. That’s all…

If you manage to read the code (I am not very proud about the absence of comments) you will notice that only one-to-many and many-to-many references are handled. In my world one-to-one references mean inheritance and in this case both objects should be deleted. But what if the base object can be deleted (has no direct references) and the derived object has? Again in my world, if you are trying to delete the base object you are already doing it wrong! Anyway if someone lives in a world other than mine (I am very democratic guy Be right back) and wants to support one-to-one relations all he/she has to do is find where IEntityCollectionProperty definition is and look for the respective property type (I believe it is IEntityReferenceProperty but I am not quite sure).

And for the end an example so that anyone can see what all of the above end up to:

Suppose you have a Customer entity. And this Customer entity has a collection of Orders. The property of the Customer entity that holds these orders is called CustomerOrders. In your Datasource you right-click Customer entity and select View Table Code. A partial class implementation file is created (if it does not already exist). Modify the definition of you class as follows:
[StrongDependency("CustomerOrders")]
public partial class Customer : IDependencyCheck {
...

  #region IDependencyCheck members 
  public bool CanDelete {
    get { return this.CanDelete(); }
  }
  #endregion IDependencyCheck members
}



Remember to reference (using) the namespace where your extension method (CanDelete) is declared.

Please note that IDependencyCheck gives you the potential to write whatever else hardcoded (or not) check you want in your CanDelete property implementation. In the code above I just call the extension method I introduced earlier. But you can do whatever you want. You can even skip the dependencies mechanism suggested altogether. The client side extension will still work.

So in the screen that you have your list of Customers right click the Delete command of the list or grid and in the CustomersDelete_CanExecute just write:
partial void CustomersDelete_CanExecute(ref bool result){
  result = this.CanDeleteSelection("Customers");
}



As partial implementation of Execute write:
partial void CustomersDelete_Execute(){
  this.Customers.DeleteSelected();
}



I know some of you have already noticed the overhead of potentially loading the dependent objects the first time you select an item of your list or grid. I cannot argue with that, except for the fact that my approach is suggested for intranet implementations (I am not sure I would do something like that over the web) and the fact that the time under these circumstances is an acceptable price to pay in order to avoid the annoying referential integrity message. At least in my world Smile. 

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.

Wednesday, July 11, 2012

Application Logo (tailored on customer demand)

This is the most recent comment in the Application Logo Sample post:
“Hi Kostas,
I wanted to find out if there is a way of opening an active screen as soon as I click on the logo. I have tried but my attempt was in vain. If it is possible, could you please help me out?
Thanks,
Darryn”

My first reaction was to ask for details (typical way of buying time Smile). But then I though I could do the changes required and post a new article. So simply put, this is the answer I propose to Darryn:
public static void AddLogo(this Microsoft.LightSwitch.Client.IClientApplication application, System.Windows.HorizontalAlignment alignment, string screenName) {
  Microsoft.LightSwitch.Threading.Dispatchers.Main.BeginInvoke(() => { LogoPlacement.AddLogo(application, System.Windows.Application.Current.RootVisual, alignment, screenName); });
}


private static class LogoPlacement
{
  internal static void AddLogo(Microsoft.LightSwitch.Client.IClientApplication application, UIElement element, System.Windows.HorizontalAlignment alignment, string screenName) {
    if (rcb != null) return;

    for (int i = 0; i < System.Windows.Media.VisualTreeHelper.GetChildrenCount(element); i++) {
      if (rcb != null)
        return;
      UIElement child = (UIElement)System.Windows.Media.VisualTreeHelper.GetChild(element, i);
      AddLogo(application, child, alignment, screenName);
    }
    if (element is Microsoft.LightSwitch.Runtime.Shell.Implementation.Standard.RibbonCommandBar) {
      rcb = element as Microsoft.LightSwitch.Runtime.Shell.Implementation.Standard.RibbonCommandBar;
      Image myImage = new Image() {
        Stretch = System.Windows.Media.Stretch.Uniform,
        Margin = new Thickness(2, 8, 14, 8),
        HorizontalAlignment = alignment,
        Cursor = System.Windows.Input.Cursors.Hand
      };
      myImage.SetValue(ComponentViewModelService.ViewModelNameProperty, "Default.LogoViewModel");
      myImage.SetBinding(Image.SourceProperty, new System.Windows.Data.Binding { Path = new PropertyPath("Logo") });
      if (!string.IsNullOrWhiteSpace(screenName))
        myImage.MouseLeftButtonUp += (s, e) => { application.Details.Dispatcher.BeginInvoke(() => application.Details.Methods[string.Format("Show{0}", screenName)].CreateInvocation().Execute()); };
      myImage.SizeChanged += (s, e) => {
        double left = (s as Image).HorizontalAlignment == HorizontalAlignment.Left ? e.NewSize.Width + 10.0 : 0.0;
        double right = (s as Image).HorizontalAlignment == HorizontalAlignment.Right ? e.NewSize.Width + 10.0 : 0.0;
        rcb.Padding = new Thickness(left, 0, right, 0);
      };
      ((Grid)rcb.Parent).Children.Add(myImage);
    }
  }




With bold I have marked the additions/changes I had to make to accommodate Darryn’s request.
I hope Darryn is happy Smile with tongue out.

Friday, June 29, 2012

Simple Extension Methods (part 2)

In the previous post I presented an extension method used mostly for overriding the edit and delete commands of a collection. One may ask “why do I want to do this?”. Apart from any other requirements/business logic dependent reason one might want to implement, for me there is one simple yet important reason: I don’t like at all (to be kind) the default add/edit modal windows when adding or editing an entry. It’s not a coincidence that the FIRST sample I wrote for LightSwitch and posted in the Samples of msdn.com/lightswitch was a set of extension methods and contracts to easily replace standard modal windows with custom ones.
Most of the times when I have an editable grid screen, selecting Add or Edit I DON’T want the modal window to pop-up, I just want to edit in the grid. Or in list and details screen I want to edit the new or existing entry in the detail part of the screen.
This is the main reason I most of the times override the default Add/Edit command behavior. And for this reason I created and use the next two extension methods:
public static void AddFocus<T>(this VisualCollection<T> collection, string focusControl, bool inCollection)
      where T : class, IEntityObject {
      collection.AddNew();
      collection.EditFocus(focusControl, inCollection);
    }

    public static void EditFocus<T>(this VisualCollection<T> collection, string focusControl, bool inCollection)
      where T : class, IEntityObject {
      if (focusControl != null) {
        try {
          IContentItemProxy control = null;
          if (inCollection)
            control = collection.Screen.FindControlInCollection(focusControl, collection.SelectedItem);
          else
            control = collection.Screen.FindControl(focusControl);
          Microsoft.LightSwitch.Threading.Dispatchers.Main.BeginInvoke(() => {
            try {
              control.Focus();
            }
            catch {
              collection.EditSelected();
            }
          });
        }
        catch {
          if (collection.HasSelection())
            collection.EditSelected();
        }
      }
    }





So what you have to do in your MyCollectionAddAndEditNew_Execute partial method is write something like:
MyCollection.AddFocus("TheNameOfTheControlYouWantToFocus", true);



or for MyCollectionEdit_Execute partial method:
MyCollection.EditFocus("TheNameOfTheControlYouWantToFocus", true);

if you have a list detail screen the same code applies but with inCollection = false.

One could implement one method for collections and one for details. Also notice where constraints applied to T generic parameter. The class constraint is required for declaring VisualCollection<T>. The IEntityObject constraint is required so that we avoid the arbitrary casting of collection.SelectedItem to IEntityObject so as to be passed to FindControlInCollection as second parameter.

Notice that in the extension methods I don’t check if the control name exists. I just use try catch. On error (if the control name is not valid) I choose to fall-back to the default behavior. You can change this without any intellectual rights being violated Smile.

Tuesday, June 26, 2012

Simple Extension Methods (part 1)

As soon as I started writing LightSwitch applications I noticed that many times I was repeating the same code over and over for trivial tasks. So after all this time I have collected a number of extension methods that I widely use in my apps.
For me reusing code is a must and although the implementation of LS (IMHO) does not provide for this out of the box the underlying framework is ideal for writing extension classes and methods that are a major step towards code reusability. If you have downloaded any of my samples from msdn or have seen my Application Logo post, you already suspect I am an “extension method fanatic”.
So I will present a small series (I don’t know how small) of posts with extension methods from my Base.LightSwitch.Client library.
The first method is one of the first (if not the first one) extension methods I wrote. As soon as you want to override the code for default commands like Edit and Delete for a collection (let’s name it MyCollection) you have to write something like that:
partial  void MyCollectionDelete_CanExecute(ref bool result){
  if (this.MyCollection is null || this.MyCollection.SelectedItem == null)
    result = false;
  else
    result = true;
}

This is the minimum code (it can be written more elegantly I know but this is the concept) you need to write. I don’t take into account the permissions issue.

A similar chunk of code has to be written for Edit.

Isn’t the code listed below easier to read:
partial  void MyCollectionDelete_CanExecute(ref bool result){
  result = this.HasSelection("MyCollection");
}
It’s not only easier to read but is less error prone than the original. Plus you can inject any security logic in this HasSelection method.
And this is the code:
public static bool HasSelection(this IScreenObject screen, string collectionName) {
  if (!screen.Details.Properties.Contains(collectionName))
    return false;
  IVisualCollection collection =
    screen.Details.Properties[collectionName].Value as IVisualCollection;
  return collection != null &&
         collection.SelectedItem != null &&

         (collection.SelectedItem as IEntityObject).Details.EntityState != EntityState.Deleted;
}
For the ones that dislike using string property names I can suggest this version :
public static bool HasSelection<T>(this VisualCollection<T> collection) where T : class, IEntityObject {
  return collection != null &&
         collection.SelectedItem != nul &&
         collection.SelectedItem.Details.EntityState != EntityState.Deleted;
}


This version is more concrete is generic and also does not have to do the (out of thin air) conversion of SelectedItem to IEntityObject. If you use this version though you have to change your partial method as you cannot extend null:
partial void MyCollectionDelete_CanExecute(ref bool result){
  result = MyCollection!= null && MyCollection.HasSelection();
}
The choice is yours…Smile

Monday, June 11, 2012

You Are I.

Finding my application’s URI address either in deployment or development/debugging environment has been an issue I had to face in my early LightSwitch days. Although not a LightSwitch issue, but rather a Siverlight/ASP.NET one, I thought that sharing the 2 lines of code I have ended up using to discover the URI (along with the port, most important for debugging and/or standalone deployments) might be useful.
First the client. The first and easy approach was extracting the URI and port from System.Windows.Browser.HtmlPage.Document.DocumentUri. Nice! Not really. This only works for web and NOT OOB (Out of Browser) or Desktop according to LS naming, applications. System.Windows.Browser namespace is in general not available in ΟΟΒ applications.
The way I ended up adopting is accessing System.Windows.Application.Current.Host.Source.AbsoluteUri. Ok, I know it’s WPF/Silverlight way (System.Windows… I mean) but my experience says that a similar approach will be available even if HTML 5 is the next (not too soon I expect) output of LS.
Then the server. Here things are simpler and for as long as the server part of a LS application remains a Web Service the path will always be the same: HttpContext.Current.Request.Headers["HOST"].
That’s it! Short but juicy (I hope Smile).

Wednesday, February 22, 2012

Application Logo Sample

Application Logo post had the dazzling number of 4 questions regarding how to use the code. So as I promised to the last guy that asked how to use the extension class I created a small sample and uploaded to msdn's Lightswitch samples. You can find it here

Friday, January 20, 2012

Application Logo

Reading this article in MSDN forums and taking the best (I hope) of what LR_ and Michael Washington offered as solutions I ended up with an extension class that allows you to put your application’s logo on the Ribbon Bar of any shell having one. You also have the choice to put it either on the left or the right. LR_’s original code was only modified in 2 points.
  1. You can decide if you want you Application Logo on the left or on the right of your commands.
  2. You don’t need to pass a URL to the image. The application’s logo (defined in project properties) is bound inspired by the XAML provided by Michael Washington.
I even left the LeftButtonDown part as it’s a good idea.

Although implied from the detailed reference to them I want to thank both guys for making it so easy for me to put my logo on screen.


    private static class LogoPlacement

    {

         public static void AddLogo(this Microsoft.LightSwitch.Client.IClientApplication application, System.Windows.HorizontalAlignment alignment) {

           Microsoft.LightSwitch.Threading.Dispatchers.Main.BeginInvoke(() => { LogoPlacement.AddLogo(System.Windows.Application.Current.RootVisual, alignment); });

         }

      internal static void AddLogo(UIElement element, System.Windows.HorizontalAlignment alignment) {

        if (rcb != null) return;

 

        for (int i = 0; i < System.Windows.Media.VisualTreeHelper.GetChildrenCount(element); i++) {

          if (rcb != null)

            return;

          UIElement child = (UIElement)System.Windows.Media.VisualTreeHelper.GetChild(element, i);

          AddLogo(child, alignment);

        }

        if (element is Microsoft.LightSwitch.Runtime.Shell.Implementation.Standard.RibbonCommandBar) {

          rcb = element as Microsoft.LightSwitch.Runtime.Shell.Implementation.Standard.RibbonCommandBar;

          Image myImage = new Image() {

            Stretch = System.Windows.Media.Stretch.Uniform,

            Margin = new Thickness(2,8,14,8),

            HorizontalAlignment = alignment,

            Cursor = System.Windows.Input.Cursors.Hand

          };

          myImage.SetValue(ComponentViewModelService.ViewModelNameProperty, "Default.LogoViewModel");

          myImage.SetBinding(Image.SourceProperty, new System.Windows.Data.Binding { Path = new PropertyPath("Logo") });

          myImage.MouseLeftButtonUp += (s, e) => MessageBox.Show("Here may be some About...");

          myImage.SizeChanged += (s, e) => {

            double left = (s as Image).HorizontalAlignment == HorizontalAlignment.Left ? e.NewSize.Width + 10.0 : 0.0;

            double right = (s as Image).HorizontalAlignment == HorizontalAlignment.Right ? e.NewSize.Width + 10.0 : 0.0;

            rcb.Padding = new Thickness(left, 0, right, 0);

          };

          ((Grid)rcb.Parent).Children.Add(myImage);

        }

      }

 

      private static RibbonCommandBar rcb = null;

    }



Tuesday, November 8, 2011

CLASS Extensions. Making of (the end)

If you have installed CLASS Extensions you know that there are two different controls that are used to handle Color business type. The ColorPicker and the ColorViewer. If you have read the previous post of the series you know that behind the scenes, each one of these controls is a wrapper for 2 other controls. The ColorPicker is for editing whereas ColorViewer is used for viewing only. The reason of exposing 2 different controls was that I didn’t manage to correctly retrieve the information about the content being read-only and changing dynamically the behavior of the controls.
If you browse the code you will see in <Control>_Load and other places (be kind, I was desperate) the attempt to bind to IsReadOnly property of the DataContext.
What I wanted to achieve was to support the “Use read-only controls” functionality. As I wrote in the previous post I was very close. I found out after publishing that the IsReadOnly property was set ONLY when the field or the whole dataset was read-only, like a calculated field or a view-dataset (containing data from more than one source or directly mapped to a DB view). What I was looking for was a property I found out digging exhaustively down the Quick-Watch window, called …(drum-roll) IsBrowseOnly. After discovering it and search back against the documentation, still didn’t manage to locate any reference to this property.
Anyway all well that ends well. Took me some time but as I mentioned in a previous post of the series, I was prepared, given that documentation is far from being complete and definitely there is nothing like a complete Reference Guide, or maybe I am not good at reading it :-).
So, IsBrowseOnly binding, along with designer initial settings issues solved, and 3 new business types (Rating, AudioUri and VideoUri) will be included to the next version of CLASS extensions. Code will, most probably, not be published this time. So keep an eye open and stay tuned…Winking smile

Monday, October 3, 2011

The Lights Witch Project(s)

LWDid you know that when you create a new LightSwitch Application project beneath the Data Sources and Screens “Folders” live 5 projects in total?

Changing your view to File View Capture only “reveals” 3 of them: Client, Common, Server. If you choose to Show All Files ShowAllFiles 2 more appear: Client Generated, Server Generated. Now you have 5 projects in all. Ok, hurry back to Logical View, pretend you didn’t see this.Smile

Seriously now, the reason I include the beginners tag in posts like this one and this one, is because I am trying to make a point. LightSwitch is NOT the new Access. It’s much, much more than Access in terms on both potential but also complexity. LightSwitch Team has done a great job making the Logical View a user-friendly powerful application design environment. But, I believe it’s good to have a broad (at least) idea of what actually happens under the surface. This way when, for some reason (not very probable if you play with the designer only but always possible), you get a build error and clicking on that error a code file you have never seen before appears, you will not be terrified by the fact that someone has turned your beautiful screens and tables into ugly, messy code files.

Client Project: Is the project where the client application and accompanying code is found. In this project, in the UserCode folder, partial classes are automatically created and then populated by code every time you choose (for example) to Edit or Override Execute Code for a screen command. You also put your own classes there (but this is not beginner stuff…). It, very broadly, represents the presentation tier of your application.

Common Project: Is the project where business logic lies. Well, “Business Logic” has always been a bit vague to me as deciding what is business logic and what is not, is a bit foggy. I mean, deciding if a command can be executed under specific conditions or not is business or client? Client is the politically correct answer. To me this has always been a convention of thought. Anyhow Common contains all code that should be available both at the client and the server. One example is writing code for <Entity Name>_Created. This code is executed either at the client or at the server, hence Common.

Server Project: Is the project where the web application and the data-tier lies. An example would be <Entity Name>_PreprocessQuery methods when overridden, the code is included in Server\UserCode\<DataSource Name>Service.cs.

Client/Server Generated are complementary to the respective projects containing auto-generated code that you should never edit in any way (any change will be lost during the next build or modification made by the designer) and this is the reason why the are not visible in File View by default.

It’s better to know the enemy…Winking smile

Saturday, October 1, 2011

DisplayName like family name is inherited.

Did you know that setting the Display Name property of your table (entity) and all fields will make screen customization even easier?
All display names you define in your data sources are automatically used in all your screens. This practically leaves you with the collection name translations in the Screens as the display name of your table is used only in singular.
Also, not only new screens are initialized by display names defined at the table level, but if you don’t change a display name manually in a screen, any change made in the table is propagated to the screen.
This makes screen customization much easier and also localization.
For less beginners or more curious than average ones: What actually happens is that LightSwitch uses the DisplayName attribute (which decorates almost all objects and their properties in LightSwitch) which is inheritable, to…display names (what else). When you first create a screen (either you have changed the display name of the table column or not) you will notice Display Name fields in the properties pane are dimmed and oblique (italic). This is to denote DisplayName attribute has not been defined but inherited instead. Once you change the display name of a field on a form, it automatically acquires it’s own DisplayName attribute and therefore the DisplayName attribute of the table column stops being inherited any more.
Even elements not related to tables (layout groups for example) have by default their display name dimmed and oblique.
The full story is that for every single element LightSwitch first looks for an “explicit”, so to say, DisplayName attribute. If not found then it looks for an inherited one and if not found either (the case of a new group layout) then derives the Display Name from the Name property of the element (inserting space before every upper-case letter found after a lower case one).
I hope this was actually a tip and not a trip…Winking smile

Friday, September 30, 2011

Large Small Medium Large?

Did you know that everything you design in a LightSwitch application (tables, screens, queries etc) is stored in a file called ApplicationDefinition.lsml?
LSML extension stands for the title of the post…no, I’m kidding of course. It stands for LightSwitch Markup Language. This file can be found in the folder <Project Path>\<Project Name>\Data.
Working in Logical View this is file is not visible (no files are visible in this view either way). Turning to File View you can see links to this file in all three projects Client, Common and Server. Notice they are links. The original file is not included directly in any project.
The reason there is only one file and all projects have link to it, is that it’s common to all of them and any change made to it should be available simultaneously to all projects.
The reason it’s not included to any project, in my humble opinion, is mainly because it’s not meant to be edited in any way. It could be included in the Common project for example as it IS common.This file is the product of all designer work done in LightSwitch. Even when you change the Theme, or Access Type properties of the application from the Properties of the project this change is stored in this file.
This is the reason also, that only ONE (or none) “<Project Name> Designer” window is available at any given time in your workspace. Because there IS only one such window displaying a different part of the ApplicationDefinition.lsml file every time you double-click a table or a form or whatever opens a designer window.
One tip for the end. When you plan a major change in your model (data design), especially if you are NOT using a native datasource, keep a backup of ApplicationDefinition.lsml. If you are using version control, check a version in, even temporarily. If not, keep a backup copy of the file. One day you might thank me for this advice…Winking smile

Thursday, September 29, 2011

Where are my tables (stored)?

Did you know that, when you create a native datasource in LightSwitch managing tables (entities) from the LightSwitch designer, the corresponding schema along with the data manipulated while debugging are stored in a SQL server database file set found in <ProjectName>\Bin\Data?
These database files are attached to the local instance of SQL 2008 Express. Visual Studio LightSwitch will install one if not found. The SQL server instance must be configured to allow user instances, as by default LightSwitch will attach the aforementioned files as a user instance. Along with your schema, default ASP.NET security schema is also found there.
The connection string compacting all the above can be found in the web.config file of the ServerGenerated project. If you don’t know what or where is this don’t worry we will talk about it in a next post. Just go ctrl+shift+F enter _IntrinsicData as the string to find and search in the entire solution. Please don’t touch. Generated, you know…Winking smile