GuidMarket SP1 - Next step into SilverLight
It's the tendency so why not a first service pack for the GuidMarket ?
More seriously, my first post about Silverlight (aka WPF/e) is especially dedicated to the technology evaluation that means the javascript code used to handle UI objects stay very "old school".
It's now interesting to provide a more flexible and improved code to extend the app with more objects and more features and thus to try to understand how to use this technology as well.
Download
What I modified
The first modification concerns the batch loading of the xaml streams which misses stability. So I wrote my own downloader javascript object that encapsulates the Downloader wpf/e object. I added an application object to easily extract the host object and the main canvas. The object enables me to bufferize the streams and the data that improves the synchronization between requests. I flushed the buffers within the main canvas when the loaded event is fired.
The second important modification concerns the "vista like" window loading. I added a WindowObject javascript object that manage the xaml on-the-fly creation and expose properties and methods to interact with the window. As the first version does not allow to create several windows on a same template, this version enables to create generate more than one window with different contents.
function
WindowObject(name, title, width, height, parentObject, contentUri, callback)
{
var _width = width;
var _height = height;
var _title = title;
var _name = name;
var _parent = parentObject;
var _left = (window.screen.width - _width) / 2;
var _top = (window.screen.height - _height) / 2;
var _contentUri = contentUri;
var _callback = callback;
this.Show = function()
{
_application.MainWindow = this;
var __downloader = new Downloader();
var __loadEventArgs = new LoadEventArgs("windowtemplate.xaml", this._OnLoaded, null)
__downloader.LoopBack(__loadEventArgs);
}
this._OnLoaded = function(args)
{
if(_parent == null) return;
var __host = _application.GetHost();
var __child = __host.CreateFromXaml(args);
_parent.children.add(__child);
_parent.findName(Constants.WINDOW_TITLE).Text = _title;
_parent.findName(Constants.WINDOW_ELEMENT)["Canvas.Top"] = _top;
_parent.findName(Constants.WINDOW_ELEMENT)["Canvas.Left"] = _left;
_parent.findName(Constants.OUTER_BORDER).Width = _parent.findName(Constants.INTER_BORDER).Width = _parent.findName(Constants.INNER_BORDER).Width = _width;
_parent.findName(Constants.OUTER_BORDER).Height = _parent.findName(Constants.INTER_BORDER).Height = _parent.findName(Constants.INNER_BORDER).Height = _height;
_parent.findName(Constants.BUTTON_CLOSE)["Canvas.Left"] = _width - 51;
_parent.findName(Constants.WINDOW_CONTENT).Width = (_width - 24);
_parent.findName(Constants.WINDOW_CONTENT).Height = (_height - 37);
_parent.findName(Constants.INNER_CONTENT_BORDER).Width = (_width - 14);
_parent.findName(Constants.INNER_CONTENT_BORDER).Height = (_height - 37);
_parent.findName(Constants.OUTER_CONTENT_BORDER).Width = (_width - 12);
_parent.findName(Constants.OUTER_CONTENT_BORDER).Height = (_height - 35);
_parent.findName(Constants.BUTTON_OK)["Canvas.Top"] = (_height - 73);
_parent.findName(Constants.BUTTON_OK)["Canvas.Left"] = (_width - 115);
_LoadContent(_contentUri);
}
this.Close = function()
{
var __window = _parent.findName(Constants.WINDOW);
_parent.children.remove(__window);
_application.MainWindow = null;
}
var _LoadContent = function(uri)
{
var __downloader = new Downloader();
var __loadEventArgs = new LoadEventArgs(uri, _OnContentLoaded, null);
__downloader.LoopBack(__loadEventArgs);
}
var _OnContentLoaded = function(args)
{
var __content = _application.GetHost().CreateFromXaml(args);
_parent.findName(Constants.WINDOW_CONTENT).children.Add(__content);
if(_callback != null) _callback();
}
}
Controls and ControlTemplates
I already said that control and controltemplate as we know in WPF do not exist in Silverlight. However, it's possible to have a similar feature by defining javascript objects which provide common characteristics and behaviors for UI objects. The graphical rendering can be loaded dynamically and synchronized by using xaml fragments loaded by the Downloader object.
The code bellow illustrates the definition of a ButtonBase control (JSON) which is rendered as a "vista like" button (without visual effects):
ButtonBase =
function()
{
}
ButtonBase.prototype =
{
_content : null,
_height : null,
_width : null,
_left : null,
_top : null,
_visual : null,
initialize: function initialize()
{
var __fragment = '<Canvas Name="_buttonCanvas" Canvas.Top="'+ this.Top +'" Canvas.Left="' + this.Left + '" MouseLeftButtonUp="BLOCKED SCRIPTonclick">';
__fragment += '<Rectangle Width="' + this.Width + '" Height="' + this.Height + '" RadiusX="2" RadiusY="2" Stroke="#A6A6A6" StrokeThickness="1">';
__fragment += '<Rectangle.Fill>';
__fragment += '<LinearGradientBrush StartPoint="0,0" EndPoint="0,1">';
__fragment += '<GradientStop Offset="0.0" Color="#FFF9F9F9" />';
__fragment += '<GradientStop Offset="0.5" Color="#FFF3F3F3" />';
__fragment += '<GradientStop Offset="0.5" Color="#FFE8E8E8" />';
__fragment += '<GradientStop Offset="1.0" Color="#FFD5D5D5" />';
__fragment += '</LinearGradientBrush>';
__fragment += '</Rectangle.Fill>';
__fragment += '</Rectangle>';
__fragment += '<Rectangle Width="' + this.Width + '" Height="' + this.Height + '" RadiusX="2" RadiusY="2" Stroke="#4AB2D9" StrokeThickness="1" />';
__fragment += '<TextBlock Canvas.Top="7" Canvas.Left="37" FontFamily="Segoe UI, Verdana, Arial" FontSize="11" Foreground="Black" Text="' + this.Content + '" />';
__fragment += '</Canvas>';
var __host = document.getElementById('wpfeControl1');
_visual = __host.CreateFromXaml(__fragment);
},
attach : function attach()
{
this.initialize();
var __host = document.getElementById('wpfeControl1');
var __content = __host.findName("_content");
__content.children.add(_visual);
},
get_Top: function get_Top() { return _top; },
set_Top: function set_Top(value) { _top = value; },
get_Left : function get_Left() { return _left; },
set_Left : function set_Left(value) { _left = value; },
get_Width : function get_Width() { return _width; },
set_Width : function set_Width(value) { _width = value; },
get_Height : function get_Height() { return _height; },
set_Height : function set_Height(value) { _height = value; },
get_Content : function get_Content() { return _content; },
set_Content : function set_Content(value) { _content = value; }
}
The code of the object have to change the object itself but also the dependent Silverlight objects that are generated. However there is an important constraint because the Silverlight objects are not available by code until they are added to their parent. It's possible to either read xaml fragment such as above or load xaml from a file as the WindowObject of the GuidMarket application. Indeed, the .xaml files could be regarded as much templates.
The code below shows how to create a new instance of our ButtonBase and how to add it to a parent object as a Canvas. In this sample I decided to use an attach method to render the control but in a real application, you should have to create a custom object manager to add and retrieve the controls and to release resources when they are removed from their parents.
var
_buttonBase = new ButtonBase();
_buttonBase.Content = "OK";
_buttonBase.Width = 100;
_buttonBase.Height = 30;
_buttonBase.Top = 10;
_buttonBase.Left = 10;
_buttonBase.attach();
Doubtful programmability
For the time being, I think Silverlight is not very flexible. A dynamic application needs too many asynchronous loadings. I had many problems between the asynchronous loading with the Atlas ScriptManager and the WPF/e Downloader object (and I don't speak about the permanent 'unexpected error' and other 'unknown exception' that everytime explode directly on your face).
I think the first real application need to be designed with more common controls and by creating custom controls. Custom controls can be developed but with too many code, the application needs a mediator object to handle them efficiently, to maintain the correct synchronization between WPF/e objects and javascript objects and to be able to raise custom events.
We may hope an efficient coupling between a more extended object model and Script# with the next CTP. While waiting, read the Nikhil Kothari's blog for more information...
Download new version