The easiest way to create modern business applications for the Cloud and Office 365
Visual Studio LightSwitch provides a set of standard UI controls for displaying application data. These controls include common Windows UI elements like text boxes, as well as controls tailored for the data entry and editing tasks (like address viewer or data grid). Many applications can be built with only standard LightSwitch controls, but there are applications that require more advanced visualizations (e.g. charts or maps), or just have specific requirements that are not covered by standard control set. One possibility to address these requirements is to use a non-standard LightSwitch control. LightSwitch provides extensibility points for the 3rd party control vendors to extend the set of control available inside the IDE. From application development perspective they behave just like built-in controls—they show up in the same places in the IDE and their general working is no different from standard controls, so using them is very easy. The downside is that if a 3rd party control that satisfies the given scenario is not available, implementing one might be a task too difficult or labor-intensive for typical LightSwitch user. Fortunately there is an easier way: LightSwitch applications can use Silverlight “custom” controls directly, and re-using or authoring custom controls is much easier.
LightSwitch client application uses Silverlight framework as the foundation to build upon. LightSwitch controls are at the core just Silverlight controls, but they are enhanced with information and functionality that makes it possible for LightSwitch runtime to relieve the developer from many routine tasks associated with UI data binding, UI layout and command enablement. A custom control is a regular Silverlight control that is part of LightSwitch application UI (a screen). The main difference between LightSwitch controls and custom controls is that a custom control does not have LightSwitch-specific information associated with it. Therefore LightSwitch treats it as a “black box” and it is up to the developer to specify what data the control should display (data-bind the control to the screen) and to handle any events the control might raise. There are two possibilities here:
In practice both of these two approaches can be used, even for a single control, and it is up to the developer to decide which one is more advantageous, given unique application requirements. An example will make things clearer, but before we jump into it, we need to learn how exactly custom controls show up on a screen.
A screen in a LightSwitch application is built of three elements:
The screenshot below shows design view of a screen called ShipperListDetail.
Note how screen designer shows the associated control and the data binding for content items that have them. If you want to know more how content tree, controls and screen members work together please see The Anatomy of a LightSwitch Application Series Part 2 – The Presentation Tier.
So what does all this have to do with custom controls? Well, the way you add a custom control to a screen is by replacing the standard (default) control that LightSwitch assigns to a content item with a custom control. In the example above we have done it for the last control in the content tree and we will now show you how
Let’s say we have a database of shippers that are available to ship goods from our manufacturing facility to various parts of the country. For now we will focus only on three pieces of information: shipper’s name, phone number and rating. Open Visual Studio, create a new LightSwitch project (you can call it “CustomControls”) and add a Shippers entity:
Also, our business rules state that shipper rating, if known, must be a number between 1 and 5, so click the Rating column in table designer, go to Properties window, find the “Custom Validation” link at the bottom of the property sheet for the Rating column and add the following code (only the body of the Rating_Validate method needs to be modified)
Next create a List and Details screen for Shippers entity, including details for the entity on the screen. Your screen should look like this in the designer:
Now we are ready to create the custom control to display the rating. We will use the Rating control from Silverlight toolkit, so if you do not have the toolkit installed yet, you can get it from http://silverlight.codeplex.com/. After you have the toolkit installed, right-click the solution node in Solution Explorer and choose Add | New Project. Choose Silverlight Class Library project and name it RatingControlWrapper. Choose Silverlight 4 as the target Silverlight version and delete the Class1 class automatically created as part of the project.
Note: this you won’t be able to complete this portion of the example (creating custom control wrapper) if you have only Visual Studio LightSwitch Beta 1 installed on your machine. You need both Visual Studio Professional (or higher SKU) and Visual Studio LightSwitch. This is because Silverlight class library projects are not supported by Visual Studio LightSwitch alone. Later on I will show you how to use Rating control directly and set up control binding from LightSwitch code; that does not require anything other than Visual Studio LightSwitch. Also, this portion of the example assumes familiarity with Silverlight user controls and XAML; for more information about these topics see Getting Started with Controls in Silverlight documentation.
After the project is created, right-click the project node and choose Add | New Item. Choose a Silverlight User Control item type and name the new control RatingControlWrapper. After the project is created, add a reference to System.Windows.Controls.Input.Toolkit assembly. You will find it under the directory where Silverlight toolkit is installed; on my machine it was “C:\Program Files (x86)\Microsoft SDKs\Silverlight\v4.0\Toolkit\Apr10\Bin”. Next open the RatingControlWrapper.xaml file in XAML view, add a namespace declaration for the local and toolkit namespaces and finally add the Rating control itself to the content of our wrapper control. You should end up with XAML file that has the content shown below; we have highlighted the portions of the control XAML that we have changed. Note that we have replaced the default Grid layout for the content with a simpler StackPanel; this will come in handy later.
The most interesting portion of this code is the data binding specification: it binds the Rating control’s Value property (which controls how many rating “stars” the user sees, i.e. depicts the rating) to Screen.ShipperCollection.SelectedItem.Rating property. The TwoWay mode means that whenever one side of the binding changes, the other side will be updated. The default binding mode in Silverlight is OneWay, which means that UI reflects (screen) data; but changes in the UI do not affect the underlying data. We want the user to be able not only see the rating, but also to change the rating by clicking the control, giving a shipper desired number of “stars”, so we use TwoWay.
Now we can switch to our main project and replace the textbox that is used for the Shipper.Rating property with our custom control. Right-click the RatingControlWrapper project in the Solution Explorer and choose “Build”—it should build without errors. Open ShipperListDetail screen from our main application in the designer, select the Rating content item and open the control selection dropdown:
Choose “Custom Control” here. Switch to Properties window, scroll to Custom Control property and hit Change link—Add Custom Control window appears
Click “Add Reference” button, switch to Project tab and select RatingControlWrapper project. Hit OK to add project reference—you should now see the RatingControlWrapper assembly in the Add Custom Control dialog (see screenshot above). Expand the RatingControlWrapper namespace, select the RatingControlWrapper control and hit OK.
At this point you could try to run the application and start adding some shippers. Our rating UI shows up, but does not quite work as expected—it seems like the only rating that sticks is five stars. Also there is no obvious way to clear the rating either. How can we fix this?
The first problem stems from the fact that the Value property of the Rating control is a floating-point number and Shippers.Rating column is an integer. We could store floating point numbers instead of integers for our rating otherwise we need to convert the Shipper’s rating of 1, 2, 3, 4 or 5 into 0.0 to 1.0 range that the Rating control can work with. Fortunately Silverlight has a concept of a value converter that is designed for just that. So let’s create a value converter for our control wrapper. Add a new class to the RatingControlWrapper project and name it Int2DoubleConverter. Then change the class code to this:
To make the converter work for various number ranges we are going to use a parameter (scaling factor). In our case the maximum rating is 5 and we will use this value as the scaling factor. Now open the RatingWrapperControl in the designer and add the converter infromation to the binding:
Changing a shipper’s rating to unknown requires setting the underlying property to null, but the Rating control does not have this capability built-in. We can add it by using a link label and a bit of code. Add the following line to RatingControlWrapper.xaml:
Double-click the OnClearAction in XAML editor—this should result in opening the code-behind file for the control (RapidControlWrapper.xaml.cs or RapidControlWrapper.xaml.vb, depending on the language you use). Change OnClearAction method body to
That is it. Now you can launch the application and it works as expected
Our application works, but the data binding for our control is hard-coded into control definition. If the screen members change and the data binding path becomes invalid, the application will no longer work. I will now show you how to specify data binding for custom controls in screen code. In this way each screen can data-bind to the control in its unique way, so you can reuse the control with multiple screens.
First, open control definition again and remove the whole Value property binding. You can also delete the Int2DoubleConverter declaration from control resources. On the other hand, we want to expose the inner Rating control from our wrapper so that we can set a binding on it, so we’ll add the FieldModifier attribute to the control declaration. The XAML should now look like this
Next open ShipperListDetails screen and override (screen)_Loaded method (in VB you will also have to add a reference to System.Windows.Controls.Input.Toolkit assembly to the client project). The Loaded method should look like this (including necessary namespace imports which you should put at the top of the file)
For now let’s ignore the portion of the code that deals with finding controls and control proxies and focus on the last 5 lines where the data binding is set up. The binding mode and value converter setting look very similar to the previous example but where is the binding path?
Well, this is a different way to accomplish the same goal. Remember that the data context for a control is its content item. The binding specification here binds the Rating control’s Value property to the content item’s Value property. The content item does not just expose screen data; it has several properties that aid the UI layer (controls) in providing the best possible data editing experience. For example
The full list of properties exposed by content items is beyond the scope of this post; but for our purposes Value property is the most important and sufficient: this is the property that returns the underlying piece of screen data that the content item represents. In our case the content item represents Shipper.Rating property, so the Rating.Value will be bound to Shipper.Rating, which is exactly what we want. The only thing remaining is to make sure that the control has the Name property set to “RatingControl” in LightSwitch screen designer, otherwise our code won’t work.
Run the application and verify that it still behaves properly.
In this post we have seen how you can use custom Silverlight controls to enhance UI of LightSwitch applications. We have learned how custom controls plug into screen content tree and how to bind them to screen data, both from XAML as well as from code. We also used a value converter to overcome the problem of type mismatch between control property types and entity member types and added a control gesture to set the underlying data to null. In the second part of this post we will talk about how LightSwitch run time uses threads and what implications this has on controls and screen code. We will also cover some ways of making the screen and the control work together (interact) and we will show you how to make custom controls work with hierarchical data.
In LightSwitch Beta 2 Microsoft made some changes to make coding for custom controls more understandable and reliable. First, we changed the name of the screen’s “Loaded” method to “Created”. This is to avoid confusion with any data load operations—the method is called immediately after the screen UI is created and at that time the initial data load (if any) might still be in progress. This has no impact on custom control data binding because custom controls will be notified automatically when data becomes available.
More important changes are related to FindControl method and the IContentItemProxy interface it uses:
The following snippet illustrates the usage of these APIs in a simple example:
Karol, you have an awesome communication skills. Just loved the write up. I also liked how you gave us that scare first, where the control was tied directly to the LS property in the XAML binding and then showed how it cab be used generically multiple places by binding the data via code.
Although it's too late, but I'd just not to use the same name for the "Custom Library project" and the "control", since it gets confusing.
BTW, where did this name come from? :-)
(RapidControlWrapper.xaml.cs or RapidControlWrapper.xaml.vb, depending on the language you use)
I really would love to see more blogs from you. I know when I finish your blog, I've learned something new. :-)
Karol, the steps to do to create a custom control, wasn't so bad. But as of Beta 2, is it still going to take the same number of steps or has it been more streamed line?
One other question Karol; Let's say when we are building app in Silverlight and using XAML, we drop a control either in the XAML editor or on the designer surface. From there, us as developers add the needed properties for the control and adjust the size and location on the surface to make it fit. Some large controls, like third party control, i.e. a Scheduler that may have many properties and settings can be done via initial XAML and some at runtime. But we, as developers tend to make those decisions (mostly at design time).
Now suppose using those bigger controls (and not a rating control that has fixed size and fixed attributes) and try to incorporate into LS. Are we going to face a much bigger challenge from LS runtime trying to fit this into screen on it's own?
My real question is, do you really see using custom controls more of using "Simple" controls or it really doesn't matter simple/small or complex/big controls can be used as well?
With custom controls (if truly there is no restriction in using), the sky is the limit, since there are some great controls out there.
Hope we can dig deeper into this.
p.s. sorry for too many posts, I guess I'm excited! :-)
Ben, thank you for kind words of encouragement, I will try to do my best to keep up my blogging. Let me try to answer your questions.
When we (at Microsoft) were discussing the custom control feature, we envisioned it as a kind of "escape hatch" for LightSwitch developers. We did not want to force them to use this feature routinely (e.g. on every screen)--we believe that standard controls and 3rd party LightSwitch controls provide best value in terms of productivity and simplicity and they should be sufficient to construct most of the UI in LightSwitch apps. If this is not the case, we will try hard to fix this, and our preferred way to do so would be to enhance the set of standard controls rather than make it very easy to consume custom controls . Having said that, we realize that custom controls are useful and important scenario and they will be fully supported; it is just that they require some setting up and some code to facilitate data binding and communication between the control and the screen.
In terms of the improvements related to custom controls, for the final LightSwitch version there will be some improvements to screen APIs and to layout algorithms used by screen run time. These changes will not substantially alter developer experience, but they will streamline and simplify many tasks and custom controls should benefit too. I plan to cover one particular new API: IContentItemProxy.SetBinding in part 2 of this post. Full overview of screen APIs and screen layout is beyond the scope of this post and will probably have to wait till next public LightSwitch release becomes available.
Regarding simple vs. complex controls, I do not see any particular problem with consuming complex controls in LighSwitch. LightSwitch introduces only a few additional concepts/limitations compared to vanilla Silverlight apps when it comes to incorporating SL controls in the UI, mostly around threading. This is something I wanted to cover in part 2 of this post--stay tuned. I believe that if you keep those LightSwitch-specific rules and caveats in mind, you should be able to use your favorite Silverlight controls in LightSwitch apps with very little extra work, no matter the size/complexity of the control in question. At least this is the design goal :-)
Hope that helps! Cheers!
Karol, thank you for a well-informed reply!
Can you tell us where the dependency property "rating" comes from? This is the one used when setting the binding. E.g. rc.SetBinding(Rating.ValueProperty, b) .
I can't seem to set the value property to the custom control on the ListDetail screen.
...never mind. Found it.
I had to add the System.Windows.Controls.Input.Toolkit reference to the LightSwitch Client project (via File View). Once the reference was made, the dependency property was resolved.
Great article Karol!
Very well explained. Looking forward to Part 2. Any idea when that will be?
Another change for Beta2.
I believe Beta2 changed the name of collections.
In the RatingControlWrapper.XAML file
Should become this
Yes, you are right, Dan. Thanks for pointing this out.
Good tutorial, I love that we have the ability to add custom controls to LS. It really opens doors to allow us to customize our applications.
One question I had that I can't seem to find an answer to, is how do you get a custom control to display data INSIDE a grid (or list)? Say I want to show a small chart or gauge in a column of my datagrid. How do I set my custom control bindings to show data for each row?
Hi guys I made a screen which is not based on any Entity and want to add a custom control e.g. a textArea on that screen. How can I bind that custom control with a property of some entity which is associated to some other screen in same project?
Thanks in advance
Great write up. Question: Can you embed custom controls if you are using the web application type too?
Excellent article, thank you Karol. We are now waiting for Part II !!!
Karol wrote an article for CoDe Magazine that incorporates both part 1 and what he was going to post for part 2.
Check it out: blogs.msdn.com/.../using-custom-controls-to-enhance-lightswitch-application-ui-karol-zadora-przylecki.aspx