Designing and implementing a Silverlight Control Library..

Jaime Rodriguez
On Windows Phone, Windows Presentation Foundation, Silverlight and Windows 7

Designing and implementing a Silverlight Control Library..

  • Comments 1
 

I have been involved in a handful of Silverlight 1.0 projects lately, so I think is time to aggregate and share a few of the lessons learned and my current thoughts on building “controls” for SL 1.0.

Beyond a write-up, I hope tackle a small library over the next couple  weeks.  I want to end up with:

·         A basic set of easily reusable controls for Silverlight 1.0.

·         Some kind of ‘framework’ underneath the controls.  Fx should be extensible and simple enough that others find it useful or to learn from {even if it is to avoid my mistakes}.

·         All the controls have to be designer friendly; I don’t expect to end up with some thing that does not require any JS code, but I want to make sure I don’t end up with 100% code. 


Design principles:  

I am biased since I have been involved in WPF  over the last few years, and I am aware of the SL 1.1 direction (which aims for convergence with WPF), so a lot of my initial design goals aimed to mimic WPF.

·         Template based.    
I love the concept of “control templates” and  separation of behavior from visual elements. 

·         Extensible  
I don’t know yet how far I will take it.  I might write 10 controls with no Layout, or I might take 20 controls and go as far as styling them, so I needed to leave ways for this thing to grow. I am also hoping other people have suggestions/ideas on improvements and maybe want to contribute. I will be putting this in codeplex.

·         Composable

WPF does a good job at this concept; you begin with a basic set of primitives ( e.g. Track, RangeBase) , and then grow them into controls.  (e.g scrollbar), which are then composed into other more interesting controls like ScrollViewer.

I can’t create it all, so I wanted to focus on primitives that people can compose on.

·         Light on code and perf. 
I kind of look at the Javascript DOM as being ‘single threaded’.  They do let me call setTimeout and setInterval to create callbacks, but these still do not execute concurrently.   Also script is much slower than compiled code, so I know I have to write a lot less to keep the UI responsive.


Implementation details so far:

Object-based

From the beginning, I was sold on using Javascript classes  and prototype “inheritance”.
Despite it being a little harder to read and a little more overhead (e.g. properties) , by making it an object based library I can keep code cleaner, drive reuse, etc. so I had to do it.   My workaround will be to put a few “intros” to creating prototype-based inheritance and such.

Also, I am still looking for best naming convention..  I find get_ and set_ very awkward.. You might see in the lib a couple instances where I just did not stick with that.. ( e.g. SLx.Application.Current, as opposed to get_current ()) …    Of course I will pick one and stick with it when I clean ..

Uses MS AJAX Client library
I was not familiar with the library so I had to dig through it.   I think the library is incredibly useful and comprehensive for true AJAX ( client-side HTML components communicating w/ server-side); unfortunately, I am not creating AJAX Controls so my decision was not as clear.

Still, there is lots of goodness in the AJAX library: 

·         They have extended the basic Javascript types (e.g. array, string, etc.) and added useful utilities (like StringBuilder).

·         They have created a friendly “object oriented” framework for creating classes,  interfaces, reflection and type checking, etc. 

·         They have some useful debugging libraries ( trace, assert, etc.) that I find quite useful specially given my short/sporadic coding attention spans to write code. I never get more than an hour at a time. 

·         They provide  a network stack for me to call webservices. I assume a good percentage of the projects will need this regardless

So, with all that in mind, I am using the AJAX library.

 

Application Model

There is a concept of an application, though it is not strictly enforced [yet].
The application is a singleton that provides access to resources,   layout manager and the host.
Today,  it is a dummy object but could do more when this thing grows.

 

Resources
So far, I do have a resource dictionary where I can put arbitrary XAML as strings and use it to create objects. For example, my control templates, shared brushes, etc.
I did not implement any of the resource resolution or scoping like WPF does.  I do not think that will be implemented; it would be too much JS to run for little value. 
Right now, the resource dictionary is at the app level.  I might later add one per control but it won’t be inherited, etc.

I have not written the code to parse a XAML file into individual resources, so far I use the downloader to get a single Zip file with lots of XAML files in it.  There is a manifest with the Zipfile, and then all I do is iterate through the files listed in the manifest, read them as strings, and add them to the dictionary. For templates, this is my long term approach. . I would like to next do brushes, and definitions of visual elements , these will require the xml parsing stuff; should not be hard to add.

 

Layout:

[As I re-read the description of layout, I should introduce a couple of terms:
A visual is a UIElement ( shape, textblock, mediaElement, etc.). It is a native Silverlight object.
A control is a term for one of the wrappers ( e.g. Button, label, etc.) it is a JS class that interacts and wraps one or more visuals.
A visual tree is a WPF concept, it is an upside-down tree with the silverlight root canvas on top.  {don’t worry much about tree} ]

At first I wanted to avoid layout altogether because:

·         I can’t do a real layout framework like WPF does. Not enough brain power, too much Javascript to run and most important execute.

·         I also believe a lot of existing Silverlight apps are not needing true dynamic layout, so I created a hybrid that lets you use both.  Here is how I am hoping it works:

But as I got started I realize I could not get around having basic alignment and basic ‘containment’ relationships that I needed to be aware of, so I implemented a weird thing that I call layout. It kind of looks like this:

·         All controls implement Measure-> Arrange  but I don’t have a single Logical Tree (like WPF does)..

o    I keep multiple roots. I am hoping this allows me to create islands of layout, instead of worrying about doing layout in the whole app app.   For example, an app might just need a menu (StakPanel) and the rest be absolute positioned, I did not want to write layout for the rest.

o   Currently a single control can contain many visuals ( UIElements in Silverlight) so the latter visuals do not participate in “layout”.

·         Controls can be inserted to the visualTree any where they want to and if the controls are panels, then they participate actively in Layout, if these are basic controls, they might be measured once and fire layout events.

·         Controls and panels get their choice of autosizing (or not).  For autosizing, a control can

o   sizeToContent  -- the Panel or Control pretty lets its child(ren) dictate the size.

o   sizeToParent--  the Panel or Control has a size and lets its children fit within that size.

·         If the control chooses neither of the ones above, we do not do dynamic sizing we simply do positioning for layout. 

·         The rules to invalidateLayout are unfortunately hard coded in the controls …   I miss true Dependency Properties and their notification capabilities.

·         All layout calculations are done in an “async manner” because layout can be a little time consuming and computation intensive [if I had a lot of controls].
This feels risky since I have no control of priority, etc. but works OK so far and I am guessing will continue to work for my ‘islands’ of layout. As a developer you might need to control the experience by showing transition animations to show already built UI.

 

Object hierarchy.
I kept my inheritance hierarchy pretty flat.  I knew I would have several concepts: Visual , Control, Panel, ContentPresenter, Primitives, etc.

What I decided to do is: 

·         I did not wrap visuals; I do have the concept of a visual, but it is merely a pointer to a native Silverlight UIElement; the framework is fairly agnostic to the type of visual that I am using mostly because I have wrapped most visuals inside a Canvas, so for the most part, the framework expects Visuals to be Canvases and it does not worry about what the Canvas contains.

·         I created a Control  base class that all my controls derive from.   This class

o   Contains the basic properties for controls ( left, top, visual, etc. ) ;  surprisingly I did have reuse there.

o   Since every thing derives from control, I get consistent base APIs I can code to 

o   Sets the infrastructure, and process for creating controls; 

o   Does any control registering into framework-like functionality (e.g. layout).

·         I kept Panel as a separate base class.  I am not sure if this decision will stick, but I did it on purpose for now because:

o   Panels should be about “behavior” (layout) so they will not include templates.

o   Panels have concept of Children, and need to manage these closely ( insert, remove, etc.). Control has the concept of “content”. Arguably this is different.

Those are my current base classes, as an example

·         Radio, Checkbox, Button,  ToggleButton, RepeatButton, Scrollbar, etc. all inherit from Control.

·         StackPanel and BasicDockPanel inherit from Panel.

ContentPresenter

On the inheritance and control hieararchy, it is worth highlighting a class called ContentPresenter.

It is not the same WPF concept –  it was my goal, but I ended up derailed just like with every thing else--.

ContentPresenter is a ‘container’ with no behavior that simply understands a few layout (alignment) attributes.  I could not just add these attributes to all visuals because then I would have to walk the whole visual tree during changes.  ContentPresenter allows me to wrap visuals and add them into the logical tree.

Examples of Controls that need a ContentPresenter are

·         Button  ( can have any content,  and content might need to be aligned

·         ScrollViewer  also needs a Content object.

Templates

Being template based just sounded like the right thing (WPF does it with XAML, here I have XAML);  but as I went on  I ran into some ‘gaps’ between what WPF does and what I could get away with Silverlight, and the tools we have for Silverlight.   I am sticking with templates because it is most designable, but I have to say this has added complexity, confusion and I have not worked around it..  {maybe I have not thought hard enough since dropping templates has never been an option}.

Here is the skinny on how templates and code merge:

1.       I do define “behavior” in Javascript classes ( like Button.js )

SLx.Button.prototype =

{

      add_Click : function ()  { } // Button has a Click

      measure : function ( )  { }

      arrange : function () { } //

} // etc..

 

2.       I define “look” or “presentation in XAML files (e.g. Button.xaml )

<Canvas x:Name="PART_Container" Width="120" Height="40" Canvas.Left="0" Canvas.Top="0">

            <Rectangle Width="120" Height="40" RadiusX="15.5" RadiusY="15.5" x:Name="PART_Container_Background">            

            <Canvas x:Name="PART_Content" Width="120" Height="40" Canvas.Left="0" Canvas.Top="0" Tag="_horizontalAlignment:SLx.HorizontalAlignment.Center,_verticalAlignment:SLx.VerticalAlignment.Center,_sizeToContent:true">

                  <TextBlock Text="Button" Canvas.Left="0" Canvas.Top="0" />

            </Canvas>

      </Canvas>

3.       I  then “marry” behavior to presentation through a “template definition” ... what this is is a “map” that tells Javascript what the XAML elements correspond to in Javascript. I marry them using findName of course.    A template definition looks like:

SLx.Button.DefaultTemplate =  [

{ name : "Button" , ref : "_visualElement", type : "element" },

{ name : "PART_Content" , ref: "_content" , type : "ContentPresenter" }

];

 

The template definition is passed when constructing the control.  The Control base class has a method called “bindParts” that reads the templates and maps these elements to right properties in the Control. When a type is any thing other than ‘element’ bindParts creates the right instance of a control to create for this part. 

Why such a complicated model?  I have two models a code, and a XAML model.

I need the template so that designers can create the look & feel.   Each ‘template’ is its own XAML file. I need the XAML to be editable in Blend.  

The problem is there is no control in Blend for controls, so my having a Button template does not help me design a full scene.  When I am designing a scene,  I need to drag & drop visuals ( say I drop an ellipse with text in the center) into a scene and then marry these with my behavior; this is where the “template definition” helps, I can map an template definition to existing visuals  by just pointing to the right names.

When I compose controls  ( for example scrollviewer is composed of ContentPresenter + Scrolllbars) things get too complicated. I do not like that so far, but I have not cracked it to simplicity – all I can say is it used to be harder, I am still working on it- .

One last thing to highlight around templates is the namescopes usage. 

For templates that come from a from a file, I end up having to use unique namescopes  -because Silveright does not allow me to use same name twice in same namescope--.  

I actually like this model of using namescopes; to be honest I wish I could create new namescopes from XAML too so I could have every thing separate.

 

Downloading the templates
As mentioned, I have not written an XML parser  [if you know of a x-plat,x-brow xml parser please share a link]  so what I have done is:

At development Each template lives in its own file.
As part of my post-build step, winzip zips all the xaml files into a zip file (called theme.zip) that is downloaded by the app.
The zip file contains a “manifest” file that lists the containers of the zipfile – todo check if there is better way --.
When the file download is completed, JS reads the manifest and then reads each file, adding it to the Application resources so that these templates are available later.

Triggers:

The system recognizes event Triggers. 
Triggers are recognized by name …   I considered not hardcoding names, and using the template definition to build the map but this just felt like more code to run for little value; names have to be unique and you are creating storyboards that target named visuals, so it seemed OK to go by name since the trigger follows the name of the visual.  

Here is how it works: 
When a control is created ( on when handler for Loaded event so we do not run them too early )  we do a findName ( ) on  <elementname>_<event>  ( e.g. canvas1_Loaded  or canvas1_MouseEnter ) …   if the trigger exists, we wire the event..

You can prevent your control from running/wiring triggers by overriding the _wireTriggers function() on your control class. You can also ‘wire’ your own triggers in this same function.

I am still considering iterating through the element resources to extract the animations.  It might be a more flexible way to reverse engineer what triggers to enable per control ( as opposed to hard coding ).

 

Control Class Factory
Since we have this notion of Control (JS class ) +  XAML +  template definition  it is useful to have an ‘abstraction’ or class factory.  Sparsed through all the Javascript files you will find static methods ( usually control name, like SLx.ControlFactory.Button () ) that allow you to create an instance of that class using the default template for that class.

Databinding
Just kidding
L

 

Controls:
As of this write-up, it is still early but you should be able to see

·         Button

·         RepeatButton

·         Checkbox – which you can use to simulate

o   ToggleButton,

o   Radio

·         Slider

·         Scrollbar

·         ScrollViewer

·         Label  -- wraps  TextBlock

·         LabelEdit  -- wraps HTML’s input Tag

·         Expander   - I think the web world calls it accordion, not sure if they are truly the same-.

Why do you see these??  A lot of these are needed for video players and the like, a SL 1.0 strength Also, this was a logical progression to ScrollViewer which I thought was milestone 1.0 for composability.

My to-do-list (though not sure if I will get there):
ListBox, and the mecca: ComboBox..

Keyboard support:
There is none today. 

Mark-up extensibility:

Leveraged the Tag property to extend the behavior of the visuals. For example, button’s content presenter is aligned to Center vertically and horizontally..  so the tag looks like: Tag="_horizontalAlignment:SLx.HorizontalAlignment.Center,_verticalAlignment:SLx.VerticalAlignment.Center,_sizeToContent:true"

This code is called via parseTags () method and it does a simple  split of property pairs ( separated by commas, but this is likely going to change to ‘;’  as I need commas for Thickness).  

The left hand side of a the pair is a member of the class doing the parsing; the member should be accessible via the this[] accessor; the right hand side is any thing that can be evaluated via Javascript’s eval ().    So far it is mostly constants, so I might step away from eval.

A decision that is still out is whether the Tag has a “class=<class name>” property pair that instantiates the javascript class and assigns it.   The reasons I did not go there so far is:

·         I was not sure I could guarantee a generic instantiation approach; so far I have opted for instantiating objects in the bindParts () function.. this way it does not have to be generic.

·         Going via “class=”  would imply I need to walk through all visual’s Tags to check for the attribute; right now the user is forced to write a little javascript to create the mappings and the objects; though it is more dev work, the perf startup required does not suffer as much..

 

Clearly, a few decisions to wrap-up, but bottom line is Tag is the extensibility model.

 

Assert happy:  
If you walk through the source you will notice I went  Assert happy with this; the reasons for it is I hardly ever get more than 2 hours straight coding time w/ out interruptions so asserts allow me to code with out much think time and catch an error later.

Once I have done much more cleaning, I do hope other people might want to reuse parts of this.  Asserts can help any one find the obvious design gaps I left open.  I do have a class to manage my asserts ( so I can disable/enable them on demand).

Status: As of 11/19  ---   feedback phase.

The lib is far from ready. It still has bugs, I have not gotten smart about layout ( I think there is a lot of room for improvements there), etc. but I think it is a good time for any one building their own similar libraries to share feedback.. For any one willing to jump in w/ their experience or ideas, this is the best time to chime in to mold it w/ your input.  Please send your feedback. To jaimer at Microsoft.com (or via codeplex below).

 

Next steps:
Will be updating library on week of 11/26 when I am back from thxgiving break. 
For those that might want to contribute, the project is at http://www.codeplex.com/sl10controls 

 

Revision History

Date

Author

Description

11-19

Jaimer

Rambling on what this will look like.

 

 

 

 

  • I have been involved in a handful of Silverlight projects where we ended up reinventing buttons, sliders,

Page 1 of 1 (1 items)