Adding custom commands, global commands and new command groups to the LightSwitch button ribbon.

<<Warning.  Creating commands the LightSwitch way takes a lot of time.  Why don’t you read this post instead? >>

 

I don’t know about you guys, but when I saw my first LightSwitch demo, it was love at first sight.  The thing I found most impressive, except for the sheer amount of time required to actually create an application, was how thick the generated client was.  A collapsible menu bar, with screens nicely grouped into categories, runtime editing of your screen, flipping a combo box to display a string with the Bing Map Control instead, a button ribbon filled with different commands in all kinds of groups, …

Ay there’s the rub, as a certain dude once said.

The "actions" category.... Woot!

Adding new commands from the IDE is easy, but they always appear in the same “Actions” category.  (Note: Unless you add the <Generated /> tag to the command in the ApplicationDefinition.lsml file… Then they appear in the “Data” category.)  There’s really no obvious support to assign your command to a certain category.

Another issue, is that command are defined on a screen level.  But what if I want a command that has nothing to do with the current screen?  A “global command”, sort of speak.  For example, what if I have some kind of messaging module, and I want a command that’s always visible to alert the user if he/she has new or urgent messages?

And apparently, I’m not the only one dreaming of custom command groups, or “global commands”…

The bad news: I have flipped LightSwitch on and off, turned the IDE inside and out, tried all kind of weird attributing in the ApplicationDefinition.lsml file, and there’s no easy way to implement this from the IDE (or at least not one that I could find).

The good news, for the ” pre-LightSwitch generation, old-school developers, not afraid of writing a couple of lines of code”, is that the LightSwitch framework itself does support it, and here’s how you do it…

Prep work:

Create a new LightSwitch project, or open an existing one.   Select your project in the Solution Explorer, and flip it to File View.

Creating the command group:

The first thing we want to do, is create a new command group.  Add a new class to your client project, called MyCustomCommandGroup.  The magic interface to implment here is Microsoft.LightSwitch.Runtime.Shell.ViewModels.Commands.IShellCommandGroup.  Your client project already has the required reference to the Microsoft.LightSwitch.Client.dll.  When you implement it, the important properties and methods are:

  • Name : the unique id of your command group
  • DisplayName : the “title” that is shown on your command group, preferably localized
  • Commands : the commands in this command group
Creating the command:
Next step, creating a command to put in our command group.  Add a new class to your client project, called MyCustomCommand.  The interfaces we’ll implement are: Microsoft.LightSwitch.Runtime.Shell.ViewModels.Commands.IShellCommand (required), Microsoft.LightSwitch.IExecutable (optional, but advised) and System.ComponentModel.INotifyProperyChanged (optional, but advised).
Once again: an overview of the important properties and methods:
  • Description: used in the tooltip, preferably localized
  • DisplayName: the “name” of your command, think I18N again
  • ExecutableObject: the “executable object” that contains the properties and methods to actually do something.  LightSwitch makes a separation between the Command (the viewmodel for the command you see in the button ribbon, and the actual ExecutableObject: the model, that actually does your stuff).  Because we implemented both interfaces in our MyCustomCommand class, you can simply implement it as: “return this;
  • Group: the unique id of the command group where this command should be placed
  • Image: the URI to the image to display, wrapped inside a System.Windows.Media.Imaging.BitmapImage for convenience
  • ExecuteAsync: the method that the default shell implementation calls when your command is clicked.  Here, you can implement the actual code that gets executed, for example: show a modal dialog window.
Showing your command group in the shell:
Finally, just because you created the command and the command group, doesn’t mean that the shell will actually display it.  The shell doesn’t even look for your custom command groups, but instead tries to find CommandGroupProviders, that are responsible for… Take a guess…
Add another class to your solution: MyCommandGroupProvider.  Two things of interest here: firstly, your class has to implement Microsoft.LightSwitch.Runtime.Shell.ViewModels.Commands.IShellCommandGroupProvider, with only method to implement:
  • IEnumerable<IShellCommandGroup> GetShellCommandGroups(IScreenObject currentScreen) : this method is called whenever a new screenobject becomes the “active screen”. The point here of course, is that the provider provides the custom commands to the shell. Feel absolutely free to ignore the active screen argument and always return your MyCustomCommandGroup instance.  This way, we have achieved one of our two goals: creating a “global command”.
Second thing to note, is that we should expose this MyCommandGroupProvider to the shell implementation.  For this, we will use the Managed ExtensibilityFramework, by adding an export attribute:  [Export(typeof(IShellCommandGroupProvider))].
Finally, hit F5 and with a but of luck, you will see the new command group with your new command in the shell…
New command & command group

New command & command group

And when you click it:
Modal popups

Modal popup pops up

Phew, not really all that hard now was it?  (Although I can assure you, without any documentation on the subject at hand, it was quite a challenge finding the right interfaces…)  This post, even without code, is already quite lengthy, that’s why this time I opted to simply describe the required process/interfaces, and not post any code.  However, if you want to see my implementation:
  1. Download this little quick&dirty sample* and unzip it.  – The project is called MessagesExtension.client. Hint… Hint…
  2. Create a new LightSwitch project, or open an existing project.
  3. Right-click the solution > “add existing…” > select the MessagesExtension.client you downloaded to include it in your solution.  (It might complain about some references, in which case you need to remove and add the required references again… Sorry about that, but ran out of time to investigate this.)
  4. Select your LightSwitch project, flip it to Logical view, and select your client project.
  5. Add a reference > project > MessagesExtension.client
  6. Also, add a new class to your client project: public class CommandProvider : MessagesExtension.Client.MessageCommandGroupProvider{}  — Yes, this really is all of the required code, I used an InheritedExportAttribute instead of the normal ExportAttribute one…
  7. Hit F5, debug and change to your likings.
All done for tonight, and I hope you enjoyed reading my seventh post on as I did writing it.  If you want to get in touch with me for any (non-obscene) reason, feel free to drop me a tweet (@janvanderhaegen) or a comment in the comment section below.
* Disclaimer: the sample code is not free, but payed for with 1  little tweet.  That should help me get in touch with you, my dear readers, all 12 of you! 😉  I promise I won’t pull this advertising trick each time.
Advertisements

27 thoughts on “Adding custom commands, global commands and new command groups to the LightSwitch button ribbon.

  1. Yet another brilliant post. This blog has just become my best resource in terms of LS development. Reading through the posts, it opened my eyes and I just realized how deep and broad LS can be. Thank you very much and keep the good ones coming.

    • Wow, thank you for your feedback!
      I just have no idea how to respond to this much kindness, haha! No, seriously, thank you! Comments like this give me the energy and enthusiasm to dive into the next post!

      Kind regards
      Jan

  2. hi!
    i’ve noticed that commands are refreshed at every change of tab, or manual screen refresh, or when start new screen, is there way to access custom commands from active screen level? in my app i have info button, similar to yours, which check last date in database, and if date is too old, then accordingly set notification in command display name (similar to yours “messages (1)”), but change is visible AFTER i change or refresh screen, is there way to manually refresh commands (bar)? btw. nice new design!

    • Hey Kivito!

      Sure there is, the Commands I showed, implement the INotifyPropertyChanged interface. The actual RibbonButton that will bind to your Command, will recognise this standard interface and add a listener to the PropertyChanged event, so you just need to raise your event. 🙂

      As an example, I updated my ViewNewMessageCommand to keep a count of the messages, and raise the PropertyChanged event when I update it (for example, with a timer, when I click the button, or from the active screen, wherever you want … I just set: command.MessageCount++;)

      Code sample:

      public class ViewNewMessageCommand : IShellCommand, IExecutable, INotifyPropertyChanged
      {
      private int messageCount = 1;
      public int MessageCount
      {
      get { return messageCount; }
      set {
      messageCount = value;
      if (this.PropertyChanged != null)
      {
      this.PropertyChanged(this, new PropertyChangedEventArgs(“Description”));
      this.PropertyChanged(this, new PropertyChangedEventArgs(“DisplayName”));
      }
      }
      }

      public string Description
      {
      get
      {
      return string.Format( “You have {0} urgent message(s) waiting to be read.”, MessageCount);
      }
      }

      public string DisplayName
      {
      get
      {
      return string.Format( “Urgent messages ({0}).”, MessageCount);
      }
      }

      //Rest of the class below…

      Hope this helps you out!

      Kind regards

      Jan

  3. hi Jan!
    thanks for help! i was trying with raiseevent but getting invalid cross thread error, so i put raiseevent in call to main dispatcher, and it seems to work.. not sure if it is right approach, but.. 🙂

  4. Hi, I’m really grateful for this post it will save me creating the same command on every screen. But I have one issue when the button is clicked I want it to use the dataworkspace to update my data, when I try to use it in ExecuteAsync I get the error “It is not valid to call Execute() on a different Dispatcher than the ExecutableObject’s Logic Dispatcher”.Can you provide an example on how this can be done please.

  5. Pingback: Extensions made easy: Adding custom commands, global commands and new command groups to the LightSwitch button ribbon. « Jan Van der Haegen's blog

  6. I have implemented this everything works fine but one thing and I cannot figure out what I’m doing wrong. This is with putting image on the button. This is my code in Command:
    System.Reflection.Assembly assembly = System.Reflection.Assembly.GetExecutingAssembly();
    System.Reflection.AssemblyName assemblyName = new AssemblyName(assembly.FullName);
    Uri uri = new Uri(“/” + assemblyName.Name + “;component/Owner.png”, UriKind.Relative);
    return new System.Windows.Media.Imaging.BitmapImage(uri);

    And I’m getting an error with uri:

    new Uri(“/OwnerInvestorTest;component/Owner.png”, UriKind.Relative)
    This expression causes side effects and will not be evaluated ????

    The picture is in the Resources and in the Client
    Please any help?

  7. Hello, I solved the problem of image, placing it within the folder UserCode code and placing it in the image have to be Build Action = Resource
    public MessagesCommand()
    : base(“”, “”, “Control”, new Uri(@”/nameapplication.Client;component/UserCode/Messages.png”, UriKind.Relative))
    {
    base.Description = this.Description;
    base.DisplayName = this.DisplayName;
    Application.Current.MessageReceived += new Application.NewMessage(Current_MessageReceived);
    }

  8. Hey Jan, thanks for this post. I’m still getting an error calling my screen:

    I’m calling a screen on the ExecutaAsyn method like this:
    public void ExecuteAsync()
    {
    Microsoft.LightSwitch.Threading.Dispatchers.Main.BeginInvoke(() =>
    {
    Application.Current.ShowCustomersListDetail();

    });
    }

    But on execution when I click the button I get this error: ” Operation is not valid due to the current state of the object.”

    Any help is much appreciated!

  9. Hello Jan,
    Thanks for your great posts!

    I’ve been working on a project lately which involves many search screens, all based on a main grid (pretty standard), and all need to have a common command button within the grid’s commandbar section.

    So I was wondering whether there’s a way to add a global command such as the example posted by you, which would be placed within the DataGrid’s commandbar section instead of appearing on the top ribbon part of the screen…?

    Many thanks!
    Uri

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