Extensions made easy: Adding custom commands, global commands and new command groups to the LightSwitch button ribbon.

One might think he already saw the title of this blog post before somewhere…  Aye, it is true, I blogged about this subject before…  But this time, it’s made easy… 

Adding global commands to my applications, is something I do almost every time.  The process, for me personally, is simple…

  1. Try to code the commands.
  2. Fail miserably, open my personal blog and find the post where I described how to do this.
  3. Curse because the post is so lengthy, too lengthy even to include a code sample.
  4. Download my own sample again.
  5. Curse at the amount of code needed to implement a simple command.
  6. Copy & paste the code.
  7. Finish the application.
  8. Start a new LightSwitch application.
  9. Go to step 1.
Wait, did you read step 5?  For my personal development entertainment (and hopefully for yours too), I’m proud to announce: ExtensionsMadeEasy now includes EasyCommands!
Let’s whip up a quick sample together…

Prep work

Create a new LightSwitch application, or open an existing one.  I opened my TheminTestArea, the project I use to test development the ExtensionsMadeEasy extension.
Download the latest version of ExtensionsMadeEasy (you can use the ExtensionManager for this), which at the time of writing is 1.4, and activate it.
Select your LightSwitch application in the solution explorer and flip to File View.

Adding the commands

This is what we'll make...

 

Lets start by adding a global command (“Kill em all!”).

If you remember how the ExtensionsMadeEasy work, you can already guess we’re going to make a CommandExporter here.

Right-click your client project and add a new class.

    public class CloseAllWithoutSavingCommand : ExtensionsMadeEasy.ClientAPI.Commands.EasyCommandExporter // 1.
    {
        public CloseAllWithoutSavingCommand() :
        base(
            "Kill em all!", // 2.
            "Close all open screens without saving", //3. 
            "Quickies...",  //4.
            new Uri(@"/TheminTestArea.Client;component/Images/Save.png", UriKind.Relative) //5.
            ) { }

        public override void Execute(Microsoft.LightSwitch.Client.IScreenObject currentScreen)
        {
            // 6.
            foreach (IActiveScreen activeScreen in Application.Current.ActiveScreens)
            {
                activeScreen.Close(false);
            }
        }
    }
  1. The class inherits an abstract base class EasyCommandExporter, located in the ExtensionsMadeEasy extension.  It’s an abstract class, has a constructor that takes 4 arguments, has one abstract method, and one virtual one…
  2. The first argument of the constructor, is the label (aka. name) of the command
  3. The second argument will be used for the tooltip.
  4. The name of the group in which our command should be displayed.  Note that we use a string here, not some kind of complex CommandGroup object, because Easy is what we love.
  5. Last of the constructor arguments, the image to display on the command.
  6. The final step to complete this class, is to implement the ExecuteMethod method.  For this example, we’re not even going to use the currently active screen object, passed as an argument.  This time, we’re going to loop through all of the screens that are open (ActiveScreens) and kill them all.  Passing false as an argument to the close method, suppresses the question to save pending changes by the way, we want to KILL the open screens, not gracefully close them if her majesty pleases.

Before we forget, add a new folder to your LightSwitch.client project called images, and add an image to it called save.png.

We’re now ready to launch our example, and if all goes well, you’ll find your first EasyCommand in the ribbon bar.

While we’re on a roll, let’s immediately proceed to our second EasyCommand.  Add another class to your LightSwitch.client project.

    public class CloseWithoutSavingCommand : ExtensionsMadeEasy.ClientAPI.Commands.EasyCommandExporter
    {
        public CloseWithoutSavingCommand() :
        base("Close Now!", "Close without saving", "Quickies...", // 1.
        new Uri(@"/TheminTestArea.Client;component/Images/Save.png", UriKind.Relative))
        {
        }

        public override void Execute(Microsoft.LightSwitch.Client.IScreenObject currentScreen)
        {
            if (currentScreen != null) // 2.
            {
                currentScreen.Close(false);
            }
        }

        public override bool IsCommandActiveForScreen(Microsoft.LightSwitch.Client.IScreenObject currentScreen)
        {
            return currentScreen != null; //3.
        }
    }

Noteworthy things in this code:

  1. Those constructor arguments.  We are using the same group (“Quickies…”), same images (I’m lazy), but of course a different name and tooltip.
  2.  This time, we’re only going to close one screen: the currently selected one (passed as an argument).
  3. Note that this second command also overrides the virtual method IsCommandActiveForScreen.   If you don’t, your command will be “global”, meaning displayed for all screens.  However, sometimes you’ll want to finetune that a bit, as an example here I’ll only show my command if there actually is an active screen.
All done.  Hit F5 to mess around a bit, and let me know if you find anything bad, or need anything good!

So what does the ExtensionsMadeEasy do?

  • It collects all commands you wrote, such as the two above.
  • It creates the IShellCommandGroups, as per the LightSwitch requirement.
  • It creates IShellCommands, full-blown objects, and their IExcecutableObjects.
  • It takes care of Threading.  As you may have noticed yourself, threading is a pain.  Therefore, your “Execute” methods will already be marshalled to the UI thread where possible.
  • It’s Easy!

 

 

 

 

 

 

Advertisements

57 thoughts on “Extensions made easy: Adding custom commands, global commands and new command groups to the LightSwitch button ribbon.

  1. I remember of those frustrating Beta2 days, how hard it was to add a global command. (I thought and even replied in forum like ‘copy-paste this part from lsml’ 🙂 🙂 )

    Another great work from you as usual Jan!! Haven’t tested it. But if I run into any issues, I know where to find u.

    Keep it up man. 🙂

  2. Hi Jan,

    Being away a couple of days, you just posted some much good stuff. I guess it is going to be a sleepless night for me just to consume all this.

    I was reading through your posts and thought of something. Might be a good idea that you could maintain a ‘living’ book on advanced topics of LS. Let’s call it ‘LS Deep Dive’ or something. You (or a group of developers) could maintain the online version so that people could download it by chapters of interested topics and it will also be easier for other developers to contribute to the ‘book’. I am not sure if there are any web solutions for this kind of thing.

    I also have two questions during which came up when I was writing some LS solutions.
    1. Currently, there seems to be a auto logout built in LS. I wonder if developer could get access to this so we can call this deliberitly when there is no action for a while.
    2. the currently LS data collection does not seem to let developer to control what columns to bring to the client side. I wanted this because sometimes I want to collect some aggregates of some collections without bring all the fields. For example, if I have a Customer collection,some times I only want to get customer zipcodes and IDs, without all the other fields of a customer. Then I want to do some aggregates of zipcodes and show some statistics on it, eg show map with customer distribution. So basically I want to limit the communication message size to improve performance.

    Anyway, this is a long comment and by the way, should I address you as Jan or Jan Vander Haegen.

    • There is one more Extension left out, to complete (or as addition to) this ExtensionMadeEasy.

      ‘Page Template’. Haven’t created one so far, but do you think, this will work without any other modification?

      • Hey Bala,

        the Page Template might be a hard one, because the ExtensionsMadeEasy only operate “at runtime”, whereas a Page Template is one of those typical “design time” extensions. I will look into this if I have more time though, but fear it might not work… 😦

        Kindest regards

        Jan

    • Hey mate,

      my first name is Jan, and you can address me by it. Locals & friends call me Jantje (because just like “Little John” in the Robin Hood stories, I’m a pretty large guy).
      I actually hate my last name. When people call me “Mr Van der Haegen”, I normally respond: “No, that’s my dad” 😉

      The EBook is a really good idea, I guess we can start a wiki for this? IMHO, it would be the most appropriate, to keep unwanted advertising out and give everyone a chance to participate. I’m not sure how one could easily download chapters then though…

      About your questions:
      1. I had no idea about the AutoLogout function! I’ll deep dive into that tonight 😀
      2. Ah the good ol’ CQRS suggestion. LightSwitch is in no way designed around perfomance, and if it’s an issue, then I have to agree with Bala: creating your own WCF services is the best (only / most elegant / suggested / …) workaround. Michael Washington has some nice posts on this ( http://lightswitchhelpwebsite.com/Blog/tabid/61/EntryId/52/Using-WCF-RIA-Services-In-LightSwitch-To-Simplify-Your-Application-UI.aspx and http://lightswitchhelpwebsite.com/Blog/tabid/61/EntryId/47/WCF-RIA-Service-Combining-Two-Tables.aspx)

      Stay tuned!!

      Jan

    • Hey surfel!
      I can’t find the auto logout thing :-s I deployed an application, logged in (form authentication), went for dinner, came back, and I was still logged in :-s
      Can you describe what your application looks like where you observed this? 🙂

      Kind regards

      Jan

      • Hi Jan,

        I had the similar experiance. What I mean by auto-logout is probably not precise. What really happens after some time of inaction is that when you try to load data, for example, to a grid, you should see red cross in grid, which means the session has been terminated.

        I actually do not like this, this is especially bad in terms of user experience. What I want is a way to detect inaction and do Log out my self (so to show to login screen after logout).

        Surfel17

    • I’ve not tested this, but LS just uses forms authentication as standard, that’s why you can integrate it into a normal ASP.net app and not need to re-login. Anyway, look for your Authentication element in the Web.config, and add a timeout attribute, something like this:

  3. I also agree with surfel17 on (E)book part. Good suggestion though. 🙂 🙂
    =====

    ///////// the currently LS data collection does not seem to let developer to control what columns to bring to the client side. I wanted this because sometimes I want to collect some aggregates of some collections without bring all the fields. For example, if I have a Customer collection,some times I only want to get customer zipcodes and IDs, without all the other fields of a customer. Then I want to do some aggregates of zipcodes and show some statistics on it, eg show map with customer distribution. So basically I want to limit the communication message size to improve performance.///////////////

    If I recall it then correct then, LS creates one SELECT, CREATE, UPDATE, DELETE commands based on each entity. So, it will get all your columns whether u like it or not.

    To my limited knowledge the only workaround for this is using WCF RIA service.

  4. Thx for the info Dave. That surprising and I think I need few more read to completely understand what u guys are talking about! 🙂

    But I’m still surprised to hear about ‘updateable views’. Could u plz explain what u mean by ‘If the view has enough information it will be updateable.’

  5. @Adam, it works fine on 1.5.

    @Jan, Hope you doing good. 🙂

    I was playing with this again and got stuck on how to ‘disable a global button’, something like “CanExecute” validation.

    Is it possible to implement?

    • Public Overrides Function IsCommandActiveForScreen(currentScreen As Microsoft.LightSwitch.Client.IScreenObject) As Boolean
      Return currentScreen IsNot Nothing AndAlso currentScreen.Name “Menu_Principal”
      End Function

  6. Jan, FYI,

    I downloaded the Source Code from Codeplex, but files belongs to” Command” are not included. Is that something what you intended for?

  7. I followed the instructions, the button appears with the title but without the image, what can be happening?

    PD. My LS Studio is in Spanish

    new Uri (@ “/ Application1.Client; component / Images / Save.png” UriKind.Relative))

    ———————————————————————————————
    Segui las instrucciones, aparece el boton, con el titulo pero sin la imagen, ¿Que puede estar ocurriendo?

    PD. Mi LS Studio está en español

    new Uri(@”/Application1.Client;component/Images/Save.png”, UriKind.Relative))

  8. Jan,
    Thanks for all your hard work. I have been reading about the extension and decided to give it a try in my first LS app. I got everything installed and activated but hit a roadblock on the C# as the way to code a class in VB is different enough that reading C# for me and translating it into what I do in VB is too much right now.
    Do you have a listing of VB code for your samples?
    Thank-you,
    DG

    • Hey Dan!
      Welcome to the world of LightSwitch wonders!
      I don’t have a VB.NET version of my code, however there’s an online tool (see my latest post) that can help you translate the C# version of any sample (or the other way around).
      I promise to do my best to post some VB.NET code in the future!
      Thanks for your feedback though, I do appreciate it!

      Kind regards

      Jan

  9. Pingback: Extensions Made Easy: dispatcher fixed & IsEnabled feature. « Jan Van der Haegen's blog

  10. Hi, Jan! Thanks a lot to share this nice tool with us. It’ll help me a lot.

    HERE IS THE VB.NET VERSION:

    Public Class CloseAllWithoutSavingCommand
    Inherits ExtensionsMadeEasy.ClientAPI.Commands.EasyCommandExporter

    Public Sub New()

    MyBase.New(“Close Now!”,
    “Close without saving”,
    “Quickies…”,
    New Uri(“/YOUR_PROJECT_NAME.Client;component/Images/Save.png”, UriKind.Relative)
    End Sub

    Public Overrides Sub Execute(currentScreen As Microsoft.LightSwitch.Client.IScreenObject)
    If currentScreen IsNot Nothing Then
    currentScreen.Close(False)
    End If
    End Sub

    Public Overrides Function IsCommandActiveForScreen(currentScreen As Microsoft.LightSwitch.Client.IScreenObject) As Boolean
    Return currentScreen IsNot Nothing
    End Function

    End Class

    ‘——————————–
    Imports Microsoft.LightSwitch.Client

    Public Class CloseWithoutSavingCommand
    Inherits ExtensionsMadeEasy.ClientAPI.Commands.EasyCommandExporter

    Public Sub New()
    MyBase.New(“Kill em all!”,
    “Close all open screens without saving”,
    “Quickies…”,
    New Uri(“/YOUR_PROJECT_NAME.Client;component/Images/Save.png”, UriKind.Relative))
    End Sub

    Public Overrides Sub Execute(currentScreen As Microsoft.LightSwitch.Client.IScreenObject)
    For Each activeScreen As IActiveScreen In LightSwitchApplication.Application.Current.ActiveScreens
    activeScreen.Close(False)
    Next
    End Sub

    End Class

    Best regards,

    Ciro.

  11. Hi Jan,

    Thanks for this post. Is there any way to set the order of the Command Groups and the Commands inside them?

    Cheers
    Raj

  12. Hey, I just tried modifying the code so that it DOES offer to save the screen before closing, but I think this is causing an infinite loop because the screen never closes, but sits there waiting for the users’ input. The screen just turns white, and it’s locked up when this happens.

    Anybody got any ideas how to fix it? To reproduce the behavior, you just change “activeScreen.Close(False)” to “activeScreen.Close(True)” and then open multiple screens and intentionally leave some unsaved changes on them, and then click the Close All button.

    I tried just putting a simple count detector in the loop to see if I could break out the easy way, but that doesn’t seem to do the job.

    Any ideas anyone?

    • Ouch, the pending changes thing.

      I remember that I had a lot of issues with those as well when creating a custom shell for the company.

      I’m not sure what the exact problem is in this case, if you’re still stuck on it then maybe the answer is in the ScreenModel class of the Cosmo shell and theme source code. It appears even the LS theme ran into issues with pending changes, lol.

      Keep rocking LS!

      Jan

  13. This is great, I have been able to do exactly what I needed without too many headaches.
    However, I still cannot get the button image to display. What am I doing wrong?

    I am using VS 2010 with v 1.12.1 of Extensions Made Easy

    MyBase.New(“Help”, “Display help about this screen”, “Help”, New Uri(“/AMPManager.Client;component/Resources/help_balloon.png”, UriKind.Relative))

    Do I have to change build action of the image file or is there some other step that I have missed?

  14. Hi, thanks… this is an amazing tool… it works great.

    I got one question, i’m using VS2012 and I can’t get the image to show property on the button. Can you help me? I’m using the Resources Folder but if I create an Images folder the image still not show. Do you have any ideas?

    Gracias.

    Johann Quintero.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s