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…
- Try to code the commands.
- Fail miserably, open my personal blog and find the post where I described how to do this.
- Curse because the post is so lengthy, too lengthy even to include a code sample.
- Download my own sample again.
- Curse at the amount of code needed to implement a simple command.
- Copy & paste the code.
- Finish the application.
- Start a new LightSwitch application.
- Go to step 1.
Prep work
Adding the commands
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); } } }
- 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…
- The first argument of the constructor, is the label (aka. name) of the command
- The second argument will be used for the tooltip.
- 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.
- Last of the constructor arguments, the image to display on the command.
- 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:
- Those constructor arguments. We are using the same group (“Quickies…”), same images (I’m lazy), but of course a different name and tooltip.
- This time, we’re only going to close one screen: the currently selected one (passed as an argument).
- 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.
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!
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. 🙂
Thanks Bala! 🙂 I’ll be right here if you need meh!
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
😮 Now I get it 😀
Lemme have a go at that later this week 🙂
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:
ok, can’t post xml.
look for element named
authentication mode=”Forms”
then the element below it, add a timeout=”60″ just after where it says name=”appname”
you should auto log out after 60 seconds of no activity.
Thanks for the hint, will try it this weekend!
Jan
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.
Hey Bala!
Agreed on the public book idea, hope to find more time soon (big deadline at work) for these community initiatives! Perhaps we can ask Michael Washington if he would be interested in joining and/or setting up the site in .NETNuke?
I’m also following you on the WCF RIA service answer.
Keep up the awesome work you are doing!
Jan
For those who are interested, this is the link to the Wiki Michale Washington created:
http://lightswitchhelpwebsite.com/Wiki.aspx
Thanks for sharing Richard. I hope a lot of people jump in and start contributing!
@powerbala
You could create a view with just the columns you want. If the view has enough information it will be updateable.
UPDATE commands are created dynamically when you update a row. Each UPDATE command will only update the columns that changed (there are some exceptions). See this post http://social.msdn.microsoft.com/Forums/en-US/lightswitch/thread/9314f8ab-1e31-4bd1-95cf-3ffc075ead93 for more info.
Dave
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.’
Interested++;
If you create a view that has the primary key and another column then you can use that view to update data. For example, I have this wide StudentMaster Table. I just want a view that has the StudentId (PK) and the LastName. I create that view. I can now go into SSMS and right-click on the view -> Edit Top 200 Rows. I can then type over the LastName and it will be updated.
Lame Assumption: I assumed that LightSwitch would work the same same for updating. But, when I select that view it says there is no primary key 😦 (it lies) and it will be treated as read-only. Is this a bug? I don’t know – I’ll post to the forum later today… Maybe we’ll find out.
LS Forum answer: http://social.msdn.microsoft.com/Forums/en-US/lightswitch/thread/14562d4b-0e8c-4c51-8d26-061b512ddb8b
Also, this is helpful:
How to fix then Entity Framework code: http://blogs.msdn.com/b/alexj/archive/2009/09/01/tip-34-how-to-work-with-updatable-views.aspx
After modifying my code I was still not able to make it work. In fact, I hosed my little throw-away test application.
If you find a way of making this work I’ll buy you a beer. 🙂
Sounds like a challenge!
Probably a weekend project though, rather busy at the moment 😦 (Iteration almost over…)
wow! it worked for me on my first attempt (in vb)! very good job!
Hi Jan, it seems that since version 1.4 the interface has changed, and I can’t get it to work 😦 any chance of an update?
Is everything working out for you with the latest version, Adam?
@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
Jan, FYI,
I downloaded the Source Code from Codeplex, but files belongs to” Command” are not included. Is that something what you intended for?
Hey Bala,
I think the version one CodePlex is a really old one, checking in the latest is at the top of my TODO list for next year!!
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))
I am having this same issue. I can’t get an image to show up.
Whoa, probably posted a bug in the newest version.
Will be my top priority this weekend, but thanks for reporting and sorry for the inconvenience!
Checked in a version yesterday where this should be fixed, so sorry for the inconvenience!
Whoa, probably posted a bug in the newest version.
Will be my top priority this weekend, thanks for reporting and sorry for the inconvenience!
Checked in the bugfix for this yesterday, so sorry for the inconvenience!
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
Pingback: Extensions Made Easy: dispatcher fixed & IsEnabled feature. « Jan Van der Haegen's blog
Thanks, this is great! Do you have a VBA translation of the above code?
Hey smacali!
Thanks for the feedback!
I’ll create a VB.NET sample this weekend and post it on the blog! Keep an eye out!
Regards
Jan
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.
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
Hey Raj,
I remember implementing an optional argument that you pass to the constructor of the class named “prefixUsedForSortingWithinGroup”. The commands get sorted by this argument.
I have no idea how one could have the command groups sorted =(
Keep rocking LS!
Hi Jan,
Thanks for your reply!!!
Do you have any sample code to use class named “prefixUsedForSortingWithinGroup”.
Cheers
Raj
Hey Raj! It’s not really a class, more like a named argument to the constructor.
Just take an example like http://code.msdn.microsoft.com/silverlight/NavigationDemo-having-some-f2629c9c/sourcecode?fileId=48359&pathId=685450823
Then after the new Uri( “….”, UriKind.relative) add the name of the argument followed by ‘:’ and the value, like
prefixUsedForSortingWithinGroup : “AAA” or
prefixUsedForSortingWithinGroup : “001” or …
Keep rocking LS!
I cant see the image on the button, i have this Uri http://puu.sh/2GWt1.png , i dont know how i can put the icon on the button http://puu.sh/2GWwg.png
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
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?
Hey Tony,
I should add better support for those URIs =/
It’s either a typo, the build action on the image, or the project name.
I use this to avoid the latter;
uri = new Uri(string.Format(@”/{0};component{1}/{2}{3}”,
this.GetType().Assembly.FullName.Split(‘,’)[0], //(1.)
“/Images”, //(2.)
displayName,//(3.)
“.png”), //(4.) ,
UriKind.Relative);
Keep rocking LS!
Jan
Hi Jan,
What should the build action be on the image file and what should the Copy To value be set at.
It feels like I have tried all combinations but nothing works.
At least I have the correct assembly name now.
Never mind, got it.
I missed a “/” right at the start of the URI!!!!
OUch, painful. But still awesome you found it! =)
Keep rocking LS!
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.
Nevermind… the URL I was looking for was /AppName.DesktopClient.Client;component/Resources/ImageName.png
=)
Keep rocking LS!!
Jan