Extensions Made Easy v1.12: Fuzzy searching

Once you’ve grabbed EME v1.12.1 or higher, you can explore a new functionality that I ported from a client project to the extension called Fuzzy searching…  Although created months ago, published weeks ago, I never found the time to blog about this gimmick until minutes ago.  Time to post a small example, in VB.Net

What is Fuzzy searching?

Suppose there’s a patient tracker application, written in LightSwitch.  When I’m seeing the doctor, he has to enter my exact family name, or part of it, to find my record between the many Patient entities.  Chances are, I’m seeing the doctor because I have a sour throat or allergic reaction on my mouth, and I’m not in the mood for spelling out my last name, again (I ALWAYS have to spell out my last name… Well, about 99% of the cases anyways, my wife is called Kundry Annys, she has to spell it out in 100% of all cases)…  If the patient tracker application developer implemented fuzzy searching, he could make the doctor and his/her patients lives easier by implementing fuzzy searching…

Fuzzy searching is a technique where searching a list of items or entities is not done based on exact string match, but on “partial” or “metaphonic” match.  Basically, if the user enters a search term like “Hagen”, it’s convenient in some very particular scenarios to return all entities with a property that sounds like “Hagen”, including “Haeghen”, “Haegen” and “Hägen”.

Although this isn’t something you’d want to do for each search screen, some user scenarios definitely justify the use of a Fuzzy search.  Take my name for example: “Jan Van der Haegen”.  Although it might sound like an exotic name to the non-Dutch speaking audience, my name is literally translated as “John of the Hedge”.  You can imagine that this is quite a common family name in Belgium and the Netherlands, and thus, comes in a variety of notations across different families, from “Haägen” to “Verhaeghe” to “Van der Haegen” to…

Setting up a sample application

To test this fuzzy searching implementation, create a new LightSwitch application.

Note to self: Application798? Really? Get a life!

In the application, create a new entity called Patient.

As you might expect, the patient entity will contain some common fields like FamilyName, FirstName, and a computed field called FullName, for displaying purposes, which is computed as:

Namespace LightSwitchApplication

    Public Class Patient

        Private Sub FullName_Compute(ByRef result As String)
            ' Set result to the desired field value
            result = Me.FamilyName + ", " + Me.FirstName
        End Sub
    End Class

End Namespace

Making the entities fuzzy

To speed up the fuzzy searching, we won’t loop over the entities during the actual search.  Instead, we’ll add an extra property to our Patient entity, which will never be displayed except for this demo, where we store some “fuzzy” version of our entity.  When the end-user hits search, we’ll make the search term fuzzy as well, and look for exact matches between the fuzzy version of the search term, and the fuzzy version of our entity.

Add an extra property to the Patient entity called “FuzzyName”.  Make sure the maximum length is high enough to contain a fuzzy version (512 characters will do since our FirstName and FamilyName properties are 255 characters each).

This would make a valid candidate to be a computed field, but since computed fields aren’t stored (they are computed on the tier wherever they are called), we’ll “manually” keep this field in sync with the other properties on save (both insert and update), by writing some code (from the Write Code dropdown).

The code we’ll need to add is this:

Imports ExtensionsMadeEasy.Utilities.Extensions.StringExtensions

Namespace LightSwitchApplication
    Public Class ApplicationDataService

        Private Sub Patients_Updating(entity As Patient)
            entity.FuzzyName = entity.FullName.MakeFuzzy()
        End Sub

        Private Sub Patients_Inserting(entity As Patient)
            entity.FuzzyName = entity.FullName.MakeFuzzy()
        End Sub
    End Class

End Namespace

The imports statement at the top (using directive in c#) makes sure you can call an extension method called .MakeFuzzy() on any string.

I added a new screen (Lists and Details Screen template) to show the Patient entities, and in the list I’m showing both the FullName computed property and the FuzzyName property.

Again, this is done for demo purposes only, you’d normally never display this field to the end-user.

What we have done so far, will result in a behavior as in the screenshot below: for each entity, a value is stored that contains the FullName, but without vowels, diacritics, lower case letters, non-alphabetic characters, and with some special attention to how consonants are pronounced (for example: both “Haeghen” and “Haägen” will be stored as “HGN”).

In case you are wondering, the “MakeFuzzy” .Net implementation (source code) is based on this SQL implementation by Diederik Krols.  It’s supposedly Dutch specific, but I found it to work for English as well.  If you disagree, feel free to export a better algorithm (just export a class from your common project that implements  IMetaphonicStringValueConverter), or better yet: send it to me and I’ll gladly include your locale in EME.

However, this doesn’t solve anything yet.  If I misspell my name as “hagen”, the search result list is still empty…

Making the search term fuzzy

The last step, is to also grab the search term that is used in the list (or grid) on the screen, and replace it with it’s fuzzy version before it hits the server, so it can be compared to our fuzzy entities…

The bad news is that to do this, you must subscribe to the NotifyPropertyChanged event on the screens IVisualCollection, find the SearchTerms property on the IScreenCollectionPropertyLoader, and swap out the SearchTerms for their fuzzy counterparts, and be careful about threading in the process.  Thanks to Justin Anderson, for helping me get access to the “Search Pipeline”.

The good news, is that you can implement this from any screen, in the Screen_Created method (from the “Write Code” dropdown), as a one-liner:

Namespace LightSwitchApplication

    Public Class PatientsListDetail

        Private Sub PatientsListDetail_Created()
            ' Write your code here.
            ExtensionsMadeEasy.Presentation.Screens.ScreenCollectionFuzzySearch.MakeFuzzy(
                Me.Details.Properties.Patients)
        End Sub
    End Class

End Namespace

 

The result is that whenever the end-user (our dear doctor) hears my name, he/she can enter “Haagen”, “Haägen”, “Haaaaaaeeeeeeeeeghen”, …, our implementation will replace it with “HGN”, and match a whole set of records that sound like “Hagen” (and thus also have “HGN” in their FuzzyName property).

Succes! The name-spelling-era is finally over!

Extensions Made Easy: v1.12: Deep linking entities

I’ve been somewhat quiet on my blog lately (but have good excuses, as always), but tonight I’ll try to make it up with a little blogging spree…  Part one: what’s new in EME 1.12

Hooray, EME 1.12 is finally released…  It’s been a while since I worked on EME because I’ve been so busy.  Actually, EME 1.12 was released 2 weeks ago, but didn’t have the time at that point to show the goodies included!  A miniature “what’s included”…

Goody nr1: LS 11 (beta) support.

EME has been tested with VS LS 11 and works: commands, shell & theme exporting, the hole bunch.  I updated the manifest so you can actually install EME to target VS LS 11 beta!  Hooray!

Goody nr2: deep linking on entities.

For those of you that missed it, EME already allows deep linking on screens since a much earlier version.  I posted this in a sample (http://code.msdn.microsoft.com/silverlight/NavigationDemo-having-some-f2629c9c), but never gave it much attention.  

Deep linking is a technique in Silverlight where a user can interact with your application through the URL.  Basically, by passing parameters (the screen name) in the URL, the LightSwitch application opens up and navigates to the correct screen when fully loaded, for example:

LightSwitch doesn’t support deep linking out-of-the box, but if you install & activate Extensions Made Easy, it does!  That’s right, the only setup you need to do is to download Extensions Made Easy, and activate it.

Possible navigation commands:

* screens:  http://localhost:30325/default.htm?NavigateTo=StudentsListDetail

* entities: http://localhost:30325/default.htm?NavigateToEntity=Students&Id=1

* entities in the non-default datasource (or: you renamed the “ApplicationData”: http://localhost:30325/default.htm?NavigateToEntity=Students&Id=1&DataSourceName=ApplicationData

The funny thing about this “deep linking to entities” gimmick,  is that I was porting it to EME from a client specific project, at the very same time Chad77 was asking for this feature in the LightSwitch MSDN forums.  Funny, because that almost never happens.  Heck, that never happens!  Anyways, Chad77 was happy with the result, and I got a free beta tester! Double tap!

Goody nr3: Fuzzy searching

The third gimmick in EME is another functionality that I needed in a client specific LightSwitch project, and happily ported to EME for your convenience: fuzzy searching.  This one though, deserves a separate blog post, just because of it’s coolness… 🙂

 

 

How to make your required fields NOT have a bold label using Extensions Made Easy… (By Kirk Brownfield)

I’ll be honest, I haven’t spent much time on Extensions Made Easy lately.  I have good excuses though: I’m writing a “getting started with LightSwitch eBook” (link on the way), I founded a “LightSwitch exclusive startup“, I’m writing about LightSwitch for MSDN magazine, I’m working on my next extension called “LightSwitch Power Productivity Studio“, but mainly because I consider EME in its current form to be complete, in the way that it can do all that I intended it to do, all that I felt was missing/not easy enough in the LightSwitch RTM, and maybe even a bit more, like deep linking in a LightSwitch application

Ironically, just when I stopped writing about EME, other people started discovering the power behind the lightweight extension, and have been happily sharing…

Kirk recently mailed me with another question: “how can one make the required fields NOT have a bold label”?  For those of you that haven’t noticed, when you use the LightSwitch default shell & theme, required properties will have a bold label like in the screenshot below:

Adding Data

By the way, my apologies on the crappy graphics in my MSDN screenshots.  I turned ClearType off on my machine, as requested in the MSDN Magazine writer guidelines.  If you take a screenshot with ClearType on, it looks crappy on-screen but much better when printed.  Bad move for the MSDN Magazine web edition, obviously…

Before I could even answer his mail, a second mail came in from Kirk with the response to his own question.  Kirk later happily granted me the privilege of posting me the answer…

Firstly, set up your LightSwitch project so you can create a theme inside your LightSwitch application.  (Everyone hates the “Extension” debugging mess…)  A sample of how to do this can be found here.

Next up, since you want to do some control styling, not just mess with the colors and the fonts a bit, you need to export a new control style to the LightSwitch framework.  I wrote about this earlier, and Keith posted the VB.Net version!

The question of course is what control you need to style, and for this you need to understand a bit about LightSwitch’s Meta-data driven MVVM implementation (MV3).  Unfortunately, the joke is on you here, because there’s little to no documentation about the subject (hold your breath for exactly 7 days and there will be… 🙂) The very short preview: the LightSwitch default theme & shell provide Views that binds to ViewModelMetaData, which in turn binds to your ViewModels and Models.  The XAML (View) part that shows the label, is called an AttachedLabelPresenter.

Kirk actually found the control to style, by looking at the source code of the LightSwitch Metro Theme, reposted here for your convenience…

<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:framework="clr-namespace:Microsoft.LightSwitch.Presentation.Framework;assembly=Microsoft.LightSwitch.Client"
xmlns:internalstyles="clr-namespace:Microsoft.LightSwitch.Presentation.Framework.Styles.Internal;assembly=Microsoft.LightSwitch.Client"
xmlns:internalconverters="clr-namespace:Microsoft.LightSwitch.Presentation.Framework.Converters.Internal;assembly=Microsoft.LightSwitch.Client"
xmlns:converters="clr-namespace:MetroThemeExtension.Presentation.Converters"
xmlns:windows="clr-namespace:System.Windows;assembly=System.Windows">

<internalstyles:RequiredFontStyleFontWeightConverter x:Key="RequiredFontStyleFontWeightConverter"/>
<converters:TextToUpperConverter x:Key="ToUpperCaseConverter"/>

<!-- The attached label presenter is the control that places labels next to controls. It is restyled here to put the label-->
text in upper case. The style is applied using implicit styles, so do not give the style a key -->
<Style TargetType="framework:AttachedLabelPresenter">
<Setter Property="IsTabStop" Value="False" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="framework:AttachedLabelPresenter">
<TextBlock x:Name="TextBlock"
Text="{Binding DisplayNameWithPunctuation, RelativeSource={RelativeSource TemplatedParent}, Converter={StaticResource ToUpperCaseConverter}}"
VerticalAlignment="Top"
HorizontalAlignment="Left"
TextWrapping="Wrap"
FontWeight="{Binding Converter={StaticResource RequiredFontStyleFontWeightConverter}}">
<windows:VisualStateManager.VisualStateGroups>
<windows:VisualStateGroup x:Name="AttachedLabelPositionStates">
<windows:VisualState x:Name="None"/>
<windows:VisualState x:Name="LeftAligned">
<Storyboard>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="TextBlock" Storyboard.TargetProperty="Margin" Duration="0">
<DiscreteObjectKeyFrame KeyTime="0">
<DiscreteObjectKeyFrame.Value>
<windows:Thickness>0,3,5,0</windows:Thickness>
<!--DiscreteObjectKeyFrame.Value>
<!--DiscreteObjectKeyFrame>
<!--ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="TextBlock" Storyboard.TargetProperty="HorizontalAlignment" Duration="0">
<DiscreteObjectKeyFrame KeyTime="0" Value="Left"/>
<!--ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="TextBlock" Storyboard.TargetProperty="VerticalAlignment" Duration="0">
<DiscreteObjectKeyFrame KeyTime="0" Value="Top"/>
<!--ObjectAnimationUsingKeyFrames>
</Storyboard>
VisualState>
<windows:VisualState x:Name="RightAligned" >
<Storyboard>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="TextBlock" Storyboard.TargetProperty="Margin" Duration="0">
<DiscreteObjectKeyFrame KeyTime="0">
<DiscreteObjectKeyFrame.Value>
<windows:Thickness>0,3,5,0</windows:Thickness>
<!--DiscreteObjectKeyFrame.Value>
<!--DiscreteObjectKeyFrame>
<!--ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="TextBlock" Storyboard.TargetProperty="HorizontalAlignment" Duration="0">
<DiscreteObjectKeyFrame KeyTime="0" Value="Right"/>
<!--ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="TextBlock" Storyboard.TargetProperty="VerticalAlignment" Duration="0">
<DiscreteObjectKeyFrame KeyTime="0" Value="Top"/>
<!--ObjectAnimationUsingKeyFrames>
</Storyboard>
VisualState>
<windows:VisualState x:Name="Top" >
<Storyboard>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="TextBlock" Storyboard.TargetProperty="Margin" Duration="0">
<DiscreteObjectKeyFrame KeyTime="0">
<DiscreteObjectKeyFrame.Value>
<windows:Thickness>0,0,0,5</windows:Thickness>
</DiscreteObjectKeyFrame.Value>
</DiscreteObjectKeyFrame>
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="TextBlock" Storyboard.TargetProperty="HorizontalAlignment" Duration="0">
<DiscreteObjectKeyFrame KeyTime="0" Value="Left"/>
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="TextBlock" Storyboard.TargetProperty="VerticalAlignment" Duration="0">
<DiscreteObjectKeyFrame KeyTime="0" Value="Bottom"/>
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</windows:VisualState>
<windows:VisualState x:Name="Bottom">
<Storyboard>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="TextBlock" Storyboard.TargetProperty="Margin" Duration="0">
<DiscreteObjectKeyFrame KeyTime="0">
<DiscreteObjectKeyFrame.Value>
<windows:Thickness>0,5,0,0</windows:Thickness>
</DiscreteObjectKeyFrame.Value>
</DiscreteObjectKeyFrame>
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="TextBlock" Storyboard.TargetProperty="HorizontalAlignment" Duration="0">
<DiscreteObjectKeyFrame KeyTime="0" Value="Left"/>
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="TextBlock" Storyboard.TargetProperty="VerticalAlignment" Duration="0">
<DiscreteObjectKeyFrame KeyTime="0" Value="Top"/>
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</windows:VisualState>
</windows:VisualStateGroup>
</windows:VisualStateManager.VisualStateGroups>

</TextBlock>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ResourceDictionary>

The interesting bits are all near the top.  First thing to notice, is the comment:

<!-- The attached label presenter is the control that places labels next to controls. It is restyled here to put the label
text in upper case. The style is applied using implicit styles, so do not give the style a key -->

Ok, so that confirms what we thought: the AttachedLabelPresenter is the control that places labels next to control.  Good naming (Nomen est Omen)!

Also, the Metro theme puts all Labels in upper case, and for this exercise, we only wanted to get rid of the bold.  Let’s revert that by getting rid of the ToUpperCaseConverter a couple of lines below that comment line:

  Text="{Binding DisplayNameWithPunctuation, RelativeSource={RelativeSource TemplatedParent}, Converter={StaticResource ToUpperCaseConverter}}"

Nice, following all the links posted in this blog post, and removing that converter, we have a LightSwitch application where the way labels are represented, looks exactly like the standard theme, but is completely under our control.  One thing we can now accomplish, is to get rid of the functionality where required fields have a bold label.  This is accomplished by getting rid of the following line completely (not just the converter!):

 FontWeight="{Binding Converter={StaticResource RequiredFontStyleFontWeightConverter}}">

Awesomesauce, another mission accomplished!

As you might have suspected, I wrote this blog post myself, but titled it “By Kirk Brownfield” since he basically came up with the solution before I could even read his first mail. 🙂  He promised me to send information about some other LightSwitch hacking adventures he’s been undertaking, I’m personally looking forward to reading about it, and sharing it with you!

Keep rocking LS!

Creating a very dynamic LightSwitch shell extension in 15 minutes or less…

I just finished cooking, and eating a hearty prawns wok…  The recipe, which I’m proud to say I have discovered myself through trial-and-error (mostly error), is a one-liner:  heat up a stir-fry pan, throw in a packet of marinated prawns (remove the wrapping first), add some deep-frozen vegetables and half a box of rice vermicelli, and pour over a quarter of a can of bisque du homard (“lobster soup”).  Hey, I enjoy cooking and never buy TV meals, but also refuse to believe that a good meal should take hours to prepare.  Sometimes, by just throwing the content of a couple of boxes together, you can get the simplest but most delicious dinners…

The same is true for software development… (You didn’t really think I was going to do a blog post on cooking, did you?) Less then a week ago, I created a small LightSwitch application for someone.  He was a dream customer, from a LightSwitch point-of-view.  The application only included a dozen screens on even less entities, and if it wasn’t for some specific reports, could have been built without writing a single line of code… Needless to say I considered the project to be “easy money“, and the customer considered me “way too cheap compared to others“, a win-win situation.  The day after our first meeting, where we agreed on a price and what functionality the application should include,  I headed back to his office to go over the implementation together.   Again, all of his requests were surprisingly easy to implement.  Make a field read-only, or hide the label, using the runtime editor.  Check.  Make it a bit more colorful, using the metro theme.  Check.  Move the save & refresh buttons to the left, under the navigation tree… Check.

No wait… What?  It’s amazing how a “minor change” in the eye of the customer, can have a huge influence on the technical implementation.  To move the commands view (the ribbon that holds the save & refresh buttons), I would have to write a custom shell extension.  Out of all the extensions one can write for LightSwitch, the shell extension surely isn’t the easiest one, the result you get with the walkthrough doesn’t look very professional, and since we already agreed on a price I had no intend to spend a lot of time on this hidden requirement.

I asked the customer if I could take a small break, and much to my own surprise, came back fifteen minutes later with a LightSwitch shell extension where not only the commands view was located under the navigation view, but the end-user could drag and dock both views to whatever location he/she could want…  Sometimes, by just throwing the content of a couple of boxes together, you can get the simplest but most effective LOB applications…

Minute 1->4: “marinated prawns”: creating a shell extension

This is the obvious box, if we need a custom shell extension, we’re going to use the extensibility toolkit that came with our Visual Studio LightSwitch installation.

  • Create a new LightSwitch Extension Project, name it DockableShell.
  • Right click on the DockableShell.lspkg project, select Add>New Item…>Shell and name it DockableShell.  This will add all required classes and metadata for our extension.
  • Press F5 to debug the extension and see what we have so far, a new instance of visual studio will open in experimental mode… Really? Waiting for the visual studio instance to load, then open a new LightSwitch project just to test our extension, will kill productivity, so let’s fix that first.
  • Close the LightSwitch Extension Project

Minute 5 ->8: “deep-frozen vegetables”: using Extensions Made Easy to increase our shelling productivity

In case you’re new to my blog, and I have a feeling this post might attract a lot of first-timers,  let me show you how to use Extensions Made Easy, a light-weight extension that I have been working on, to boost your LightSwitch hacking experience…

  • Create a new LightSwitch project, name it DockableShellTestArea, and create a couple of dummy entities and screens.  Or open an existing LightSwitch project, obviously.
  • Download and activate Extensions Made Easy.
  • While you’re messing with your LightSwitch project’s properties, select the “EasyShell” as the selected shell.
  • Right click on the solution in the solution explorer, and select Add>Existing Item…  Add the client project from the extension that we made in the previous step.
  • Select your LightSwitch project, and change the view from “Logical View” to “File View”. (Highlighted with the red “circle” in the image below).
  • Add a project reference from your LightSwitch client project (DockableShellTestArea.Client) to your Extension’s client project (DockableShell.Client).
  • Change the visibility of the DockableShell class (found in the DockableShell.Client project under Presentation.Shells.Components), from internal to public.
  • And, lastly, export the shell to ExtensionsMadeEasy, by adding a new class to the DockableShellTestArea.Client project with the code below… (This is the only code we’ll write by the way… )
namespace LightSwitchApplication
{
    public class DockableShellExporter :
        ExtensionsMadeEasy.ClientAPI.Shell.EasyShellExporter
        <DockableShell.Presentation.Shells.Components.DockableShell>
    { }
}

All done, your solution should look a similar to this…

If you made it through these 4 minutes, you can now press F5 to see your LightSwitch application with your custom shell applied.

It doesn’t look so fancy yet, and that’s an overstatement, because we haven’t actually implemented our shell extension yet.  Move along fast, you’re over halfway there…

Minute 9 ->12: “rice vermicelli”: pandora’s box

Right, time to open pandora’s box.

Before you read on, realize that the kind of LightSwitch hacking we’re about to do, might damage your application, software, or cause your hardware to spontaniously self-ignite, and I won’t take responsability for it.  Seriously though, once you leave the path of published APIs, you should realize that no one can/will provide support for the errors you might encounter, and that your extensions can / will break in later versions of LightSwitch…

In the solution explorer, select your LightSwitch application’s client project and select “Open folder in windows explorer”.  Go up one level, and then down the ClientGenerated>Bin>Debug path.  You’ll find an assembly called Microsoft.LightSwitch.Client.Internal.DLL.   This assembly contains the files that are used by, for example, the LightSwitch default shell.  Instead of rolling our own shell, we’re going to tear apart and reuse the built-in LightSwitch default shell.  This scenario is in no way officially supported, and quite frankly I don’t believe you’re even allowed to do that, so do it at your own risk and don’t tell anyone about it, in a blog post or whatever… Crap…

  • Add a reference to Microsoft.LightSwitch.Client.Internal from your DockableShell.Client project.
  • Also add a reference to System.Windows.Controls
  • And a reference to System.Windows.Controls.Navigation
  • Open the file DockableShell.xaml and replace the contents with the following
<UserControl x:Class="DockableShell.Presentation.Shells.DockableShell"
     xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
     xmlns:nav="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Navigation"
     xmlns:stringsLocal="clr-namespace:Microsoft.LightSwitch.Runtime.Shell.Implementation.Resources;assembly=Microsoft.LightSwitch.Client.Internal"
     xmlns:DefaultShell="clr-namespace:Microsoft.LightSwitch.Runtime.Shell.Implementation.Standard;assembly=Microsoft.LightSwitch.Client.Internal"
     xmlns:ShellHelpers="clr-namespace:Microsoft.LightSwitch.Runtime.Shell.Helpers;assembly=Microsoft.LightSwitch.Client"
     xmlns:controls="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls"
     xmlns:internalControls="clr-namespace:Microsoft.LightSwitch.SilverlightUtilities.Controls.Internal;assembly=Microsoft.LightSwitch.Client"
     xmlns:internalToolkit="clr-namespace:Microsoft.LightSwitch.Presentation.Framework.Toolkit.Internal;assembly=Microsoft.LightSwitch.Client"
     xmlns:framework="clr-namespace:Microsoft.LightSwitch.Presentation.Framework;assembly=Microsoft.LightSwitch.Client">

     <Grid x:Name="shellGrid" Background="{StaticResource NavShellBackgroundBrush}">
          <Grid.RowDefinitions>
               <RowDefinition Height="Auto"/>
               <RowDefinition Height="*"/>
               <RowDefinition Height="Auto"/>
          </Grid.RowDefinitions>

          <DefaultShell:CommandsView x:Name="_commandsView" ShellHelpers:ComponentViewModelService.ViewModelName="Default.CommandsViewModel"
               VerticalAlignment="Stretch" VerticalContentAlignment="Stretch" HorizontalAlignment="Stretch" BorderThickness="0"
               HorizontalContentAlignment="Stretch" Margin="0"/>
          <Grid Grid.Row="1" >
               <Grid.ColumnDefinitions>
                    <ColumnDefinition Width="Auto"/>
                    <ColumnDefinition Width="Auto"/>
                    <ColumnDefinition Width="*"/>
               </Grid.ColumnDefinitions>
               <DefaultShell:NavigationView x:Name="NavigationView"
                    ShellHelpers:ComponentViewModelService.ViewModelName="Default.NavigationViewModel"
                    HorizontalAlignment="Stretch" HorizontalContentAlignment="Stretch"
                    Grid.Column="0"
                    VerticalAlignment="Stretch" VerticalContentAlignment="Top"/>

               <controls:GridSplitter Grid.Column="1" Width="6" Style="{StaticResource GridSplitterStyle}" IsTabStop="False"
                    Background="Transparent"
                    IsEnabled="{Binding ElementName=NavigationView,Path=IsExpanded, Mode=TwoWay}"
                    VerticalAlignment="Stretch" HorizontalAlignment="Left"/>

               <ContentControl Grid.Column="2" HorizontalAlignment="Stretch" HorizontalContentAlignment="Stretch" Margin="6,3,6,6"
               VerticalAlignment="Stretch" VerticalContentAlignment="Stretch" IsTabStop="False"
               ShellHelpers:ComponentViewService.ComponentContent="Default.ActiveScreensView"
               ShellHelpers:ComponentViewModelService.ViewModelName="Default.ActiveScreensViewModel"/>
          </Grid>
     </Grid>
</UserControl>

As you can see, we got three grids here, that hold together a bunch of controls in the DefaultShell namespace.  They are the actual views that are used in the default LightSwitch shell extension.  We’re also using ShellHelpers to do MVVM, the LightSwitch way.

You might get some red squigly lines in your xaml (errors), caused by the fact that the LightSwitch controls are internal and are not supposed to be used in your custom shell.  However, compiling and running, works just fine.  Press F5 to enjoy the result…

Basically, and given the implementation, it should come to no surprise, it looks exactly like we are using the Default shell.  We just spent 12 of our 15 minutes and from a functional point of view, end up with exactly the same result.  Not good.  However, from a technical point of view, we went from a simple LightSwitch application that uses the Default shell, to a simple LightSwitch application that uses a custom shell extension that looks, works and behaves exactly like the default LightSwitch shell.  Major difference!

Now, we have three minutes left on the clock before we should go back in the office and show the customer our results.  We could spend one minute to update the XAML posted above, swap the grids around until we reach the effect the customer wanted (save & close button under the navigation menu), and arrive two minutes early…

Or… Underpromise, overdeliver, and spend our 180 seconds opening box number four…

Minute 13 ->15: “bisque du homard”: Telerik RADControls for SilverLight

If you don’t have the Telerik RADControls for Silverlight installed, you can hop over to their website and download a trial, which should work just fine for this example.  I’m also giving away a licence at the end of this blog post!

  • Drag a RADDocking control on your DockableShell.xaml’s XAML.  I always forget the correct references to add, and this little trick does that for us.
  • Replace the contents of the DockableShell.xaml with the following…
<UserControl x:Class="DockableShell.Presentation.Shells.DockableShell"
     xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
     xmlns:nav="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Navigation"
     xmlns:controls="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls"
     xmlns:internalControls="clr-namespace:Microsoft.LightSwitch.SilverlightUtilities.Controls.Internal;assembly=Microsoft.LightSwitch.Client"
     xmlns:internalToolkit="clr-namespace:Microsoft.LightSwitch.Presentation.Framework.Toolkit.Internal;assembly=Microsoft.LightSwitch.Client"
     xmlns:framework="clr-namespace:Microsoft.LightSwitch.Presentation.Framework;assembly=Microsoft.LightSwitch.Client"
     xmlns:stringsLocal="clr-namespace:Microsoft.LightSwitch.Runtime.Shell.Implementation.Resources;assembly=Microsoft.LightSwitch.Client.Internal"
     xmlns:DefaultShell="clr-namespace:Microsoft.LightSwitch.Runtime.Shell.Implementation.Standard;assembly=Microsoft.LightSwitch.Client.Internal"
     xmlns:ShellHelpers="clr-namespace:Microsoft.LightSwitch.Runtime.Shell.Helpers;assembly=Microsoft.LightSwitch.Client"
     xmlns:telerik="http://schemas.telerik.com/2008/xaml/presentation"
     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">

     <Grid x:Name="shellGrid" Background="{StaticResource NavShellBackgroundBrush}">
          <telerik:RadDocking x:Name="Docking" BorderThickness="0" Padding="0" telerik:StyleManager.Theme="Metro" >
               <telerik:RadDocking.DocumentHost>
                    <ContentControl HorizontalAlignment="Stretch" HorizontalContentAlignment="Stretch" Margin="6,3,6,6"
                    VerticalAlignment="Stretch" VerticalContentAlignment="Stretch" IsTabStop="False"
                    ShellHelpers:ComponentViewService.ComponentContent="Default.ActiveScreensView"
                    ShellHelpers:ComponentViewModelService.ViewModelName="Default.ActiveScreensViewModel"/>
               </telerik:RadDocking.DocumentHost>

               <telerik:RadSplitContainer InitialPosition="DockedTop">
                    <telerik:RadPaneGroup>
                    <telerik:RadPane Header="Actions">
                              <DefaultShell:CommandsView x:Name="_commandsView"
                                   ShellHelpers:ComponentViewModelService.ViewModelName="Default.CommandsViewModel"
                                   VerticalAlignment="Stretch" VerticalContentAlignment="Stretch" HorizontalAlignment="Stretch" BorderThickness="0"
                                   HorizontalContentAlignment="Stretch" Margin="0"/>
                         </telerik:RadPane>
                    </telerik:RadPaneGroup>
               </telerik:RadSplitContainer>

               <telerik:RadSplitContainer InitialPosition="DockedLeft">
                    <telerik:RadPaneGroup>
                         <telerik:RadPane Header="Navigation" >
                              <DefaultShell:NavigationView x:Name="NavigationView"
                                   ShellHelpers:ComponentViewModelService.ViewModelName="Default.NavigationViewModel"
                                   HorizontalAlignment="Stretch" HorizontalContentAlignment="Stretch"
                                   VerticalAlignment="Stretch" VerticalContentAlignment="Top"/>
                         </telerik:RadPane>
                    </telerik:RadPaneGroup>
               </telerik:RadSplitContainer>
          </telerik:RadDocking>
     </Grid>
</UserControl>

(You might want to hit F5, and walk back inside the customer’s office, while you’re waiting for the build process to complete, or read the rest of the blog post…)  

Compared to the result we had 2 minutes and 45 seconds ago, we’re using the same views that we stole from the LightSwitch default shell implementation, but positioned them in Telerik RadPanes instead of in a simple grid.

Functionally, the end-user can now drag the “Actions” (business name for what we call the Commands View), and drag them anywhere he wants…

Or…

Mission accomplished, project delivered on time, customer is happy, and we made a nice amount of money… LightSwitch business as usual. 🙂

Win a Telerik RADControls for SilverLight license worth $799!

The Telerik licence has been won by @MatthieuFichefet, by being selected by Mojo, our judge (Video on the way, it’s hilarious), after tweeting:

Essential reading : Creating a very dynamic #LightSwitch shell extension in 15 minutes or less… http://wp.me/p1J0PO-9T@janvanderhaegen

In case you joined the contest but didn’t win, keep an eye out on the blog for new opportunities… Whenever I have anything to give away, I will!

Enjoy the hacking!

MyBizz Portal: The “smallest” LightSwitch application you have ever seen (Thank you!).

Time to get this blog on the road!  My life has known a fun but chaotic couple of weeks, with lots of crazy events and opportunities that you’ll hear about later… First things first, a little thank you is in order…

Only a couple of weeks ago, 10 days before the LightSwitch Star Contest on CodeProject was about to end, I posted a small article that stated:

“This weekend, I’m going to try to allocate most of my time into the submission of “MyBizz Portal”, […] My aim is to rank as high as possible in the category “Most Groundbreaking LightSwitch Application”, with an application that has one screen, a blank shell, the default LightSwitch theme and a single Silverlight Custom UserControl, so probably the smallest LightSwitch application you have ever seen.

I never assumed it to be even remotely possible that I would win any of the prizes, but then a first mail hit my inbox, stating that I won the first prize in the January edition of the contest, rewarding me an awesome bundle of LightSwitch specific software…  Most rewarding day of my life since I lost my virginity…

Until a second mail came… Apparently, not only did the judges like my entry, they liked it enough to award the “Grand Winner in the category Most Groundbreaking LightSwitch Application” label to MyBizz Portal!  I received the awesome Acer laptop this morning, and just finished installing LightSwitch on it…

I’m not ashamed to admit that the personal publicity and recognition, are very rewarding, and I had a smile on my face for the rest of the week after those two mails.  Also, the software licenses and the laptop will provide a nice financial boost to my startup (still on track to open virtual doors on April 2nd).  But perhaps the biggest reward of them all, came in the form of a third mail, from Jay Schmelzer (Principal Diractor Program Manager – VS LightSwitch), asking if I could do an interview with Beth Massi, our LightSwitch goddess

Since I knew she visiting The Netherlands the very next week, it made perfectly good sense to meet up for the interview and talk face-to-face.  Not only was it an honor to meet such a fantastic, charismatic, pragmatic and a little bit lunatic (aren’t we all?) person, she gave me some great real-life tips that will last a life time (such as “The Business doesn’t care”, or “How do you feel about that?” – but these tips deserve a separate blog post)!   Apparently, I left a good impression myself, as she blogged about our encounter in her latest trip report

Beth Massi's blog - my brother always says: "Screenshot or it didn't happen"...

Beth Massi's blog - my brother always says: "Screenshot or it didn't happen"...

 

So anyways, and I mean it from the bottom of my heart: thank you!

Thank you to Beth for meeting up, and for the amazing adventure, hope we can meet again!

Thank you to the LightSwitch team for the amazing product they have built!

Thank you to CodeProject for their contest, and the sponsors (First Floor Software, RSSBus, ComponentOne, and whoever sponsored the laptop) for the prizes!

And most of all… Thank you, to the LightSwitch community, and my blog readers, all 37 of you,  for being the virtual family that you are!  Without readers, there’s no satisfaction in writing a blog… Without this blog, I wouldn’t have met Beth, or have the crazy opportunities that I have now…  To thank all of you in particular, my next blog post will end with a $799 giveaway, so come back soon!

Centric RAD race: did the better technology win???

Maybe… Hard to say…  If LightSwitch won the competition, it would have been a well-ment: yes!  But we didn’t take the trophy home, the first two spots in the ranking were hijacked by teams using SharePoint+Nintex… 😦

Round 1: CRUD

Due to heavy snow storms, and the longest traffic jam Belgium has ever seen, we started the competition almost 45 minutes later than the other teams.  The assignment for round 1 was pretty straight-forward: create a system where one can manage software components (users, releases, usages, platforms, …).  There’s no framework like LightSwitch when it comes to CRUD operations, and under heavy time pressure we still managed to take second place in the overall rankings (points were awarded based on: teamwork, code maintainability and general functionality)!

Round 2: multi-tenancy

The goal for round 2 was to take the application built in round 1, and think about how different companies could use the application together.  Security was very important here, one company should be allowed to view components that have been marked as “public” from another company, and make a bid on it, which could be accepted, rejected, or responded to with a higher asking-price by the issuing company.

Because we relied on the Windows Authentication in round 1, we ended up having to create some extra entities (Company, User, …) and lost some time there.  After the small modifications, we created queries on all our entities so that the logged user could only see the private data from its own company.  A crucial thinking mistake really, we should just have created a second dataset (maybe even second LightSwitch app, and use a Portal Application) for the “market place” functionality.  That way, each company would have its own data, we could have stuck to the Windows Authentication, and simply publish to the public (common) dataset when a component is marked as “public”…

Round 3: social media integration

The last round was actually quite simple: add the ability to publish alerts about new components to any social media platform (Facebook, Twitter or Yammer), and the ability to follow components via RSS.

Although straight-forward, we lacked plug-and-play elements to implement this faster than the teams of professional SharePoint consultants…

In summary

It was a fun (and a bit stressful) night, and managed to defend LightSwitch’ honor well.   I must say that we ended up giving our chances for victory away because of the mess we made in round 2, and the lack of tools/experience in round 3.

I don’t feel like LightSwitch, as a technology, was the weaker link here: there were no brick walls hit, no “omg we simply can’t do this” moments.  I’m stronger than ever in my belief that, given some time to mature, LightSwitch has an extremely high market value in the future

And hey, there’s always next year!

— A special thanks to Centric for organizing this fun event (count me in for every future event that involves LightSwitch, free beer, free pizza, or a combination of!), and the LightSwitch community members that supported us through all kind of social media platforms!

LightSwitch twitter bot.

Hey guys ‘n guls,

I was reading some sites about LightSwitch yesterday and realized how hard it was to keep track of all the different blogs, articles, code samples, …

Because I’m on Twitter a lot (yes, a LOT), I created a Twitter-bot account (@LightSwitchNews) that monitors interesting LightSwitch-related sources, and tweets if a new blog post is written, a new extension or code sample is added to the gallery, …  The bot is created with “if this then that“, a website with the amazingly simple but effective concept that events on one platform (RSS, WordPress, GMail, GCalendar, whatever) can be configured to trigger an action on any other… (Really interesting, check it out!)

I created this user for my own purpose really (but feel free to follow if you find it interesting, ofcourse), whenever I have some spare time I can just see what the bot has been tweeting and stay updated about what’s going on in the community.  It’s my portal to the LightSwitch community… 😉

If you know any interesting LightSwitch community members, or if you are one and my bot’s not tweeting about you, let me know (@janvanderhaegen) and I’ll add you!