<<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.
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
- 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.
- 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”.
- Download this little quick&dirty sample* and unzip it. – The project is called MessagesExtension.client. Hint… Hint…
- Create a new LightSwitch project, or open an existing project.
- 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.)
- Select your LightSwitch project, flip it to Logical view, and select your client project.
- Add a reference > project > MessagesExtension.client
- 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…
- Hit F5, debug and change to your likings.
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
Hi, do you found there is a bug about IShellCommandGroupProvider? The command groups returned by IShellCommandGroupProvider show twice after the application is run!
Here is my forum post:
http://social.msdn.microsoft.com/Forums/en-US/lsextensibility/thread/6a80101e-6c96-47e0-812c-e078fc988fc4
Can you confirm wether you are encountering the same problem or not? send me a email or leave replay in forum thread?
Thank you.
Ryan
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
Hi Jan,
I have implemented the above but using your EasyCommandExtensions, which is awesome!
However, the one bit I am struggling with is where you say:
” I just set: command.MessageCount++;”
I cannot seem to figure out how to update the MessageCount? I cannot get a reference to the command object from the application, so I was wondering how you were doing this?
Thanks,
Richard
Hi Jan,
Not to worry, I realised that you cannot access the IShellCommand itself, so I figured out how to do it by raising and event and then updating the Button UI on the Main Dispatcher.
For others who are looking to do the same thing, the code is here:
http://www.electricscribes.com/blog/post/2011/12/05/MS-Lightswitch-Messages-RibbonButton.aspx
Cheers,
Richard
Hey Richard,
sorry I didn’t get back to you earlier, but I’m very happy to see you worked everything out. Very nice post by the way, keep them coming!!!
And thanks for the comment on the theme 😀 Quite like it myself so far…
^^
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.. 🙂
Oouch,
I only tested it from within the extension’s execute method, where I show the modal dialog, and thus was already on the UI thread…
Glad you solved it! 🙂
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.
found out what i was missing, using LightSwitchApplication.Application.Current.Details.Dispatcher.BeginInvoke solved my problem.
Hey Saf, glad you worked that out!
Pingback: Extensions made easy: Adding custom commands, global commands and new command groups to the LightSwitch button ribbon. « Jan Van der Haegen's blog
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?
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);
}
Hey secatrom!
Thanks for posting back! Should have made it more apparent that the build action should be set as resource. 🙂
Keep rocking LS!
This seems to break in VS2012 RC… Has anyone ported this over?
Hey J,
thanks for using EME, and posting back!!
Unfortunately, this feature doesn’t break in VS2012 but is due to the way the new Cosmopolitan Shell is implemented. 😥 Since the cosmo shell & theme will be the defaults for vs 2012, you’re kinda right that it breaks in vs2012, but it’s not really my fault. 😦
I submitted a but (upvotes appreciated https://connect.microsoft.com/VisualStudio/feedback/details/735154/lightswitch-cosmopolitan-shell-theme-not-handling-commands-well), and they already triaged it as “working on it – hope to fix in next release). Fingers crossed, else I promise I’ll find a workaround!!
Keep rocking LS!
Jan
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!
Hi Guys,
Is there any way to set the Order of Command Groups and the Commands within them?
Cheers
Raj
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
Hey Uri,
unfortunately there’s no way to do that. Your best bet would be to alter the default cosmo shell & theme, then have a look at the control template for the grid.
Keep rocking LS!
Jan
Hi Guys,
the download link of the sample not work:
http://www.paywithapost.de/pay?id=58b4d33ebf56e745ea072d4ac17288aa
Can anyone post this again?
Also the Link from Richard is not available:
http://www.electricscribes.com/blog/post/2011/12/05/MS-Lightswitch-Messages-RibbonButton.aspx
Many thanks!
Hannes
Hi Guys,
the download link of the sample not work:
http://www.paywithapost.de/pay?id=58b4d33ebf56e745ea072d4ac17288aa
Can anyone post this again?
Also the Link from Richard is not available:
http://www.electricscribes.com/blog/post/2011/12/05/MS-Lightswitch-Messages-RibbonButton.aspx
Many thanks!
Hannes