Introduction
With Layouts you can create a web application using Xaml markup language to define its UI. Actually Layouts does much more than this: you can create a fully SPA (single page application) using best MVVM practices. Layouts is suited to build complex user interfaces like dashboards or LOB applications but is easy enough to even just build a page of your exiting application.
In short Layouts replaces HTML with XAML and leave the same Javascript and CSS that web developers are used to work with.
A basic knowledge of TypeScript (http://www.typescriptlang.org/) is preferable before reading this article or look at Layouts source.
Getting Started
First create an empty TypeScript project in Visual Studio (or in Sublime or whatsoever).
Put in following files:
index.xml is an empty HTML page with links to layouts.js, linq.min.js and app.js:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<title>Layouts Sample</title>
<link rel="stylesheet" href="app.css" type="text/css" />
<script src="linq.min.js" type="text/javascript"></script>
<script src="layouts.js" type="text/javascript"></script>
<script src="app.js" type="text/javascript"></script>
</head>
<body>
</body>
</html>
Also create a CSS file (app.css) that just remove any margins and set body to occupy the whole page:
* {
margin: 0px;
}
html {
height:100%;
}
body {
font-family: 'Segoe UI', sans-serif;
height:100%;
margin:0px;
}
#helloworld {
color: red;
}
Finally copy latest layouts.js file from github.com repository, excellent linq.min.js from https://linqjs.codeplex.com/ and setup TypeScript compiler to generate everything in one single file called app.js.
Hello World
Add a typescript file app.ts like the following:
window.onload = () => {
var app = new layouts.Application();
var lmlReader = new layouts.XamlReader();
var lmlTest = `<?xml version="1.0" encoding="utf-8" ?>
<Page>
<TextBlock id="helloworld" Text="Hello World" VerticalAlignment="Center" HorizontalAlignment="Center"/>
</Page>
`;
app.page = lmlReader.Parse(lmlTest);
};
If everything compile and run you should be able to see a white page with Hello World centered in red.
Looking at above code you will see that I've created an Application (variable app) and a xaml reader (lmlReader). Next I'll describe how my page should be composed in xaml-like markup that I than pass to XamlReader.Parse method to get a Page object. Application.Page is finally set with newly created page.In layouts there can be only one Application object. Application.Page returns current page: one could switch between pages just setting this property.
The interesting part is how Page is defined in Xaml. We define the Page itself using the Page header that contains a TextBlock element. Looking at TextBlock, it defines a paragraph object (p) vertically and horizontally center. TextBlock id attribute is directly passed to resulting HTML so we can just select it in CSS file (app.css) to give it a red color.
Let's experiment a bit with this xaml for example set HorizontalAlignment to Left or Right and VerticalAlignment to Top or Bottom.
XamlReader.Parse method is quite powerful because it doesn't only manage Page object. It can parse and create other layouts controls but also controls you define in your code.
Sample Login Page
Now let's complicate a bit our sample creating a login page:
window.onload = () => {
var app = new layouts.Application();
var lmlReader = new layouts.XamlReader();
var lmlTest = `<?xml version= "1.0" encoding= "utf-8" ?>
<Stack Orientation="Vertical" VerticalAlignment= "Center" HorizontalAlignment= "Center" >
<TextBlock Text="Welcome to Login Page" Margin= "8" />
<TextBox Placeholder= "User name" Margin= "8" />
<TextBox Type= "password" Placeholder= "Password" Margin= "8" />
<Button Text="Sign In" Margin= "8,16,8,8" />
</Stack>
`;
app.page = lmlReader.Parse(lmlTest);
};
Stack element is fundamental element in Layouts (as long with Grid) that allows arranging children elements in a stack vertically or horizontally oriented. TextBox is rendered as an HTML input control and Button as a html button. Margin is an attribute that determines how elements are positioned relative to their borders. Margin="8" means "reserve 8 pixel space on top, right, bottom and left".
Note that button is disabled: we have not yet specified a command for the button so Layouts disables it (see below).
Take a moment to experiment with Layouts Margin and Orientation properties. You can also embed a Stack inside another Stack.
Make another small modification:
var lmlTest = `<?xml version= "1.0" encoding= "utf-8" ?>
<Stack Orientation="Vertical" VerticalAlignment= "Center" HorizontalAlignment= "Center">
<TextBlock Text="Welcome to Login Page" Margin= "8" />
<TextBox Placeholder= "User name" Margin= "8" />
<TextBox Type= "password" Placeholder= "Password" Margin= "8" />
<Grid Columns="* Auto" Margin= "8,16,8,8" MaxWidth="300">
<Button Text="Sign In"/>
<TextBlock Text="Not yet registered?" Grid.Column="1" Margin="10,0,0,0"/>
</Grid>
</Stack>
`;
We've added a Grid panel inside the stack. Grid is the most powerful element present in Layouts. Grid arranges its children using grid layout composed of Rows and Columns. Above we've created a Grid with 2 columns and 1 row (by default). First column has star (*) width, second column is set to Auto. Grid also supports fixed size columns or rows where you'll be able to specify how large should be a column or row in pixels.
Again take a moment experimenting with Grid. Please note that if you change text "Not yet registered" with something else Grid will reserve enough space to completely show new text. Auto sizing is one important Layouts feature: it allows the creation of interfaces that follow content size.
MVVM
Layouts encourages use of MVVM pattern: it provides some important classes you are required to implement in order to plug UI elements with models. I can't dive here too much in MVVM description and I'm sure out there you can find guides and tutorials that describe it much better than I could do.
Let's create a view-model class that can handle login process:
class LoginViewModel extends layouts.DepObject {
static typeName: string = "app.LoginViewModel";
get typeName(): string {
return LoginViewModel.typeName;
}
constructor() {
super();
}
private _username: string;
get username(): string {
return this._username;
}
set username(value: string) {
if (this._username != value) {
var oldValue = this._username;
this._username = value;
this.onPropertyChanged("username", value, oldValue);
this._loginCommand.canExecuteChanged();
}
}
private _password: string;
get password(): string {
return this._password;
}
set password(value: string) {
if (this._password != value) {
var oldValue = this._password;
this._password = value;
this.onPropertyChanged("password", value, oldValue);
this._loginCommand.canExecuteChanged();
}
}
private _loginCommand: layouts.Command;
get loginCommand(): layouts.Command {
if (this._loginCommand == null)
this._loginCommand = new layouts.Command((cmd, p) => this.onLogin(), (cmd, p) => this.canLogin());
return this._loginCommand;
}
onLogin() {
if (this._username == "test" &&
this._password == "test") {
alert("Logged in!");
}
else
alert("Unable to login!");
}
canLogin(): boolean {
return this._username != null && this._username.trim().length > 0 &&
this._password != null && this._password.trim().length > 0;
}
}
Layouts defines a type called DepObject that provides some basic features useful to create view models. For example above we've defined a LoginViewModel class that derives from DepObject. Our view model defines a couple of properties (Username and Password) and a command (loginCommand). The first part declaring the type name is required to making Layouts binding work (as there is no way in javascript to discover type name at runtime).
Now let's link the view (login page) to above view-model:
window.onload = () => {
var app = new layouts.Application();
var lmlReader = new layouts.XamlReader();
var lmlTest = `<?xml version= "1.0" encoding= "utf-8" ?>
<Stack Orientation="Vertical" VerticalAlignment="Center" HorizontalAlignment="Center">
<TextBlock Text="Welcome to Login Page" Margin="8"/>
<TextBox Text="{username,mode:twoway}" Placeholder="User name (test)" Margin="8"/>
<TextBox Text="{password,mode:twoway}" Type="password" Placeholder="Password (test)" Margin="8"/>
<Button Text="Sign In" Command="{loginCommand}" Margin="8,16,8,8"/>
</Stack>
`;
app.page = lmlReader.Parse(lmlTest);
app.page.dataContext = new LoginViewModel();
};
First thing to note is that I've set property dataContext of current page to a new instance of LoginViewModel. Than looking at xaml definition you can certainly notice that I've informed Layouts to bind first text box to Username property of the view-model, second textbox to Password and finally button to loginCommand. Now if you run the sample application you should be able to edit username and password and then sign-in.
What's next?
Layouts is a large framework and would be impossible to describe all details in an article. This is a summary of what you could get with it:
- Controls like ItemsControl, ControlTemplate and ContentTemplate and many other
- Ability to create custom controls deriving from FrameworkElement or UserContol. In github repository you'll find controls like TreeView and TabView.
- Ability to embed external UI frameworks (I’ve successfully used Kendo UI, JQuery UI and datatables.net in projects)
- Complex binding scenarios with custom converters and targets (self,dataContext,element)
- Ability to directly embed HTML as native elements
- Embedded navigation system for SPA implementation
Conclusion
Layouts is my attempt to port Xaml and WPF paradigms to web application development. I've used Layouts to build a complex dashboard application that is now getting in production and that will be used by tens of people. In the last 3 months I've deeply worked to get it stable and fully featured.
History
10-19-2015 First Article Release