1. Introduction
ASP.NET Mobile Framework (AMF) is a Web Framework for making web application for most-used tablet and smartphone web browsers.
The current version (0.9.0.0) is based on Jquery Mobile Beta 2 (http://jquerymobile.com) and is currently in beta version too. The first final version will be released with the number 1.0.0.0 and will be available when Jquery Mobile will be in stable version.
AMF is under Microsoft Public License (MS-PL)
A. Links
AMF CodePlex: http://amf.codeplex.com
AMF Website: http://www.ymartel.com/aspnet-mobile-framework/
Jquery Mobile: http://jquerymobile.com
B. Why use AMF?
- Generating HTML through WebControls
- Speed of development
- Databinding
- Code readability
- Maintainability
- HTML5 Markup-driven configuration
C. Difference between standard asp.net and mobile framework
In AMF, we can have many mobile page in one asp.net page.
We don’t use form control (<form runat="server"></form>) because we can have multiple form in the same asp.net page. So, standard webcontrol don’t work.
We don’t use Viewstate too.
2. Targeted Platforms
- iOS, Android, Windows Mobile
- Blackberry, Symbian, webOS
- Firefox Mobile (Fennec), Opera Mobile / Mini
- Meego, bada, Maemo...
- Phonegap!
- Pre-fetch page
- Custom loading message
3. Supported platforms
The version 0.9.0.X of ASP.NET Mobile framework has the same supported platforms as Jquery Mobile Beta 2.
A-grade - Full enhanced experience with Ajax-based animated page transitions.
- Apple iOS 3.2-5.0 beta: Tested on the original iPad (3.2 / 4.3), iPad 2 (4.3), original iPhone (3.1), iPhone 3 (3.2), 3GS (4.3), and 4 (4.3 / 5.0 beta)
- Android 2.1-2.3: Tested on the HTC Incredible (2.2), original Droid (2.2), Nook Color (2.2), HTC Aria (2.1), Google Nexus S (2.3). Functional on 1.5 & 1.6 but performance may be sluggish, tested on Google G1 (1.5)
- Android Honeycomb: Tested on the Samsung Galaxy Tab 10.1
- Windows Phone 7: Tested on the HTC 7 Surround
- Blackberry 6.0: Tested on the Torch 9800 and Style 9670
- Blackberry Playbook: Tested on PlayBook version 1.0.1 / 1.0.5
- Palm WebOS (1.4-2.0): Tested on the Palm Pixi (1.4), Pre (1.4), Pre 2 (2.0)
- Palm WebOS 3.0: Tested on HP TouchPad
- Firefox Mobile (Beta): Tested on Android 2.2
- Opera Mobile 11.0: Tested on the iPhone 3GS and 4 (5.0/6.0), Android 2.2 (5.0/6.0), Windows Mobile 6.5 (5.0)
- Kindle 3: Tested on the built-in WebKit browser included in the Kindle 3 device
- Chrome Desktop 11-13 - Tested on OS X 10.6.7 and Windows 7
- Firefox Desktop 3.6-4.0 - Tested on OS X 10.6.7 and Windows 7
- Internet Explorer 7-9 - Tested on Windows XP, Vista and 7 (minor CSS issues)
- Opera Desktop 10-11 - Tested on OS X 10.6.7 and Windows 7
B-grade - Enhanced experience except without Ajax navigation features.
- Blackberry 5.0: Tested on the Storm 2 9550, Bold 9770
- Opera Mini (5.0-6.0) - Tested on iOS 3.2/4.3
- Windows Phone 6.5 - Tested on the HTC
- Nokia Symbian^3: Tested on Nokia N8 (Symbian^3), C7 (Symbian^3), also works on N97 (Symbian^1)
C-grade - Basic, non-enhanced HTML experience that is still functional
- Blackberry4.x: Tested on the Curve 8330
- All older smartphone platforms and featurephones - Any device that doesn't support media queries will receive the basic, C grade experience
Not Currently Supported
- Meego - Originally a target platform, but Nokia decision to relegate this platform to "experimental", we are considering dropping support.
- Samsung Bada - The project doesn't currently have test devices or emulators, but current support is known to be fairly good. Support level undecided for 1.0.
4. AMF example
A. Introduction
We’re going to make a little web application to manage users using the AMF framework.
B. Business Layer
First step is to create User and Country object.
public class User
{
public string Name { get; set; }
public bool IsActive { get; set; }
public string Country { get; set; }
public char FirstLetter { get; set; }
public int Age { get; set; }
public User(string name, bool isActive, string country,int age)
{
Name = name;
IsActive = isActive;
Country = country;
Age = age;
FirstLetter = Convert.ToChar(name.Substring(0, 1).ToUpper());
}
}
public class Country
{
public string Name { get; set; }
public int Number { get; set; }
public Country()
{
Number = 0;
}
public Country(string name, int number)
{
Name = name;
Number = number;
}
}
Next step, we’ve to create the business layer. For user, we want two public methods:
GetUsers: Get users from session. At the first call, we create a mock users list.
GetUsersByCountry: Get users by country.
public static class UserManager
{
public const string UsersSessionKey = "Users";
public static List<User> GetUsers()
{
List<User> users = null;
if ((HttpContext.Current.Session[UsersSessionKey] != null) && (HttpContext.Current.Session[UsersSessionKey] is List<User>))
{
users = HttpContext.Current.Session[UsersSessionKey] as List<User>;
}
else
{
users = GetMockUserList();
HttpContext.Current.Session[UsersSessionKey] = users;
}
return users;
}
private static List<User> GetMockUserList()
{
List<User> users = new List<User>();
User userFr1 = new User("Yohann",true,"France",28);
User userFr2 = new User("Francis", false, "France",32);
User userFr3 = new User("Jean", true, "France",16);
User userFr4 = new User("Yann", true, "France",5);
User userG1 = new User("Markus", true, "Germany",39);
User userG2 = new User("Raimund", true, "Germany",20);
User userG3 = new User("Tankred", false, "Germany",16);
User userG4 = new User("Vinzens", true, "Germany",7);
User userUsa1 = new User("Mike", true, "USA",39);
User userUsa2 = new User("Joe", false, "USA",12);
User userUsa3 = new User("John", true, "USA",10);
User userUsa4 = new User("Billy", true, "USA",79);
User userUsa5 = new User("Bob", true, "USA",48);
users.Add(userFr1);
users.Add(userFr2);
users.Add(userFr3);
users.Add(userFr4);
users.Add(userG1);
users.Add(userG2);
users.Add(userG3);
users.Add(userG4);
users.Add(userUsa1);
users.Add(userUsa2);
users.Add(userUsa3);
users.Add(userUsa4);
users.Add(userUsa5);
return users;
}
public static void AddUser(string name, bool isActive, string country, int age)
{
if ((HttpContext.Current.Session[UsersSessionKey] != null) && (HttpContext.Current.Session[UsersSessionKey] is List<User>))
{
List<User> users = HttpContext.Current.Session[UsersSessionKey] as List<User>;
if (users != null)
{
if (!String.IsNullOrEmpty(name))
{
User user = new User(name, isActive, country, age);
users.Add(user);
HttpContext.Current.Session[UsersSessionKey] = users;
}
}
}
}
public static List<User> GetUsersByCountry(string country)
{
List<User> users = null;
List<User> sessionUsers = GetUsers();
if (sessionUsers != null)
{
var q = from p in sessionUsers
where p.Country == country
select p;
users = q.ToList();
}
return users;
}
}
For country, we’ve to get the users list for make the country list.
public static class CountryManager
{
public static List<Country> GetCountries()
{
List<Country> countries = new List<Country>();
List<User> users = UserManager.GetUsers();
if (users != null)
{
var q = from p in users
group p by p.Country
into g
select new Country(g.Key, g.Count());
countries = q.ToList();
}
return countries;
}
}
C. Master Page
In the master page, you’ve to add javasript and css link for Jquery and Jquery Mobile.
<%@ Master Language="C#" AutoEventWireup="true" CodeBehind="Site.master.cs" Inherits="Mobile.FirstWebApplication.SiteMaster" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<tml xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>AMF example</title>
<link rel="stylesheet" href="http://code.jquery.com/mobile/1.0b2/jquery.mobile-1.0b2.min.css" />
<script type="text/javascript" src="http://code.jquery.com/jquery-1.6.2.min.js"></script>
<script type="text/javascript" src="http://code.jquery.com/mobile/1.0b2/jquery.mobile-1.0b2.min.js"></script>
</head>
<body>
<asp:ContentPlaceHolder ID="MainContent" runat="server">
</asp:ContentPlaceHolder>
</body>
</html>
D. Homepage: Fixed toolbars, Button and transition
MobilePage is the most important WebControl in AMF.
Header and Footer are optionals.
You can add html or AMF's webcontrols inside header/content and footer.
For your information, you can have many MobilePage in the same asp.net page.
<%@ Page Title="Homepage" Language="C#" MasterPageFile="~/Site.master" AutoEventWireup="true"
CodeBehind="Default.aspx.cs" Inherits="Mobile.FirstWebApplication._Default" %>
<%@ Register TagPrefix="mob" Namespace="Mobile.WebControls" Assembly="AMF" %>
<asp:Content ID="BodyContent" runat="server" ContentPlaceHolderID="MainContent">
<mob:MobilePage ID="mbPage1" HeaderPosition="Fixed" FooterPosition="Fixed" Theme="B" runat="server">
<Header>
<h1>Homepage</h1>
</Header>
<Content>
<p>Welcome in Asp.net Mobile Framework (AMF): The Web Framework for make Web Application For Smartphones & Tablets.</p>
<mob:LinkButton runat="server" ForceToReload="true" Text="User's list" Url="CountryList.aspx" DataTransition="Fade" />
<mob:IconLinkButton runat="server" ForceToReload="true" Icon="Plus" Text="Add User" Url="AddUser.aspx" />
</Content>
<Footer>
<h1>AMF 0.9</h1>
</Footer>
</mob:MobilePage>
</asp:Content>
Code explantation:
ToolbarFixed:
To fix the toolbars, you’ve to set HeaderPosition and/or FooterPosition to "Fixed".
Note: Fixed toolbars will re-appear after you scroll.
Buttons:
All buttons inherit from link.
To set a url in a Link, just set the Url’s property.
When we use Ajax navigation, the framework loads the page just the first time.
If you want to always reload the page, you’ve to set ForceToReload property to true.
We've to use this property because CountryList.aspx and AddUser.aspx will be dynamic pages.
IconButtons:
AMF includes a selected set of icons most often needed for mobile apps.
List of icons:
- LeftArrow
- RightArrow
- UpArrow
- DownArrow
- Delete
- Plus
- Minus
- Check
- Gear
- Refresh
- Forward
- Back
- Grid
- Star
- Alert
- Info
- Home
- Search
You can set the icon at the right or left position with the property Position.
You can just add icon without text with the property Postion to NoText.
Transition:
AMF has 6 CSS-Based transition effects for pages transition. By default, the framework applies the right to left slide transition.
Transitions:
- Slide
- SlideUp
- SlideDown
- Pop
- Fade
- Flip*
*Not run correctly on Android
If you want to use animation from the left to the right, use DataDirection="Reverse" property on a Link.
Result:
E. CountryListPage: Button back, Listview (IconListViewItem and CountBubbleListViewItem)
CountryList is a dynamic page to show the country list. We’re using ListView (with IconListViewItem and CountBubbleListViewItem). We’ve to add a back button in the header.
<%@ Page Title="" Language="C#" MasterPageFile="~/Site.Master" AutoEventWireup="true" CodeBehind="CountryList.aspx.cs" Inherits="Mobile.FirstWebApplication.CountryList" %>
<%@ Import Namespace="Mobile.FirstWebApplication.Business" %>
<%@ Register TagPrefix="mob" Namespace="Mobile.WebControls" Assembly="AMF" %>
<asp:Content ID="Content1" ContentPlaceHolderID="MainContent" runat="server">
<mob:MobilePage ID="mbCountryList" HeaderPosition="Fixed" FooterPosition="Fixed" Theme="B" runat="server">
<Header>
<mob:IconLinkButton runat="server" ForceToReload="true" Text="Back" Url="Default.aspx" Icon="Back" DataDirection="Reverse" DataTransition="Slide"/>
<h1>Country</h1>
</Header>
<Content>
<mob:ListView ID="lv" runat="server">
<ItemTemplate>
<mob:LinkContainer ForceToReload="true" Url="<%# GetUrl(((Country)Container.DataItem).Name) %>" runat="server" DataItem="<%# (Container.DataItem)%>">
<Content>
<mob:IconListViewItem Source="<%# GetImageUrl(((Country)Container.DataItem).Name) %>" runat="server"/>
<%# ((Country)Container.DataItem).Name%>
<mob:CountBubbleListViewItem BubbleInfo="<%# ((Country)Container.DataItem).Number %>" runat="server"></mob:CountBubbleListViewItem>
</Content>
</mob:LinkContainer>
</ItemTemplate>
</mob:ListView>
</Content>
<Footer>
<h1>AMF 0.9</h1>
</Footer>
</mob:MobilePage>
</asp:Content>
Code explantation:
BackButton in Header:
You can add a back button in a header. In JQuery Mobile Framework, there’s a function to make it automatically. But, this function is not available for the moment in AMF. Use the DataDirection property to reverse to set animation from the left to the right.
ListView:
This control is the way to make dynamic list.
If you want to make a readonly listView, use a TextContainer instead of LinkContainer.
Use IconListViewItem to make a row with an icon. Set Source property to add an image url.
Use a CountBubbleListViewItem to add a bubble at the left of the row. Set the BubbleInfo property to fill the bubble.
Next step is to bind the listview:
To bind the listview, use the ControlHelper to find it.
public partial class CountryList : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
Control control = ControlHelper.GetControlById(mbCountryList, "lv");
if (control is WebControls.ListView)
{
WebControls.ListView lv = control as ListView;
lv.DataSource = CountryManager.GetCountries();
lv.DataBind();
}
}
protected string GetUrl(string countryName)
{
return "UsersList.aspx?c=" + countryName;
}
protected string GetImageUrl(string countryName)
{
return String.Format("/Images/flag_{0}.png", countryName.ToLower());
}
}
Result:
F. UsersListPage: ListView – GroupBy, ReadOnly, Theming
UserListPage is a dynamic page to show the users by country.
<%@ Page Title="" Language="C#" MasterPageFile="~/Site.Master" AutoEventWireup="true" CodeBehind="UsersList.aspx.cs" Inherits="Mobile.FirstWebApplication.UsersList" %>
<%@ Import Namespace="Mobile.FirstWebApplication.Business" %>
<%@ Register TagPrefix="mob" Namespace="Mobile.WebControls" Assembly="AMF" %>
<asp:Content ID="BodyContent" ContentPlaceHolderID="MainContent" runat="server">
<mob:MobilePage ID="mpUsersList" HeaderPosition="Fixed" FooterPosition="Fixed" Theme="B" runat="server">
<Header>
<mob:IconLinkButton runat="server" ForceToReload="true" Text="Back" Url="CountryList.aspx" Icon="Back" DataDirection="Reverse" DataTransition="Slide"/>
<h1>Users</h1>
</Header>
<Content>
<mob:ListView ID="lv" runat="server" SearchEngine="true" Theme="D" SeparatorTheme="D" WithDataSeparator="true" SeparatorProperty="FirstLetter">
<ItemTemplate>
<%# ((User)Container.DataItem).Name %>
</ItemTemplate>
</mob:ListView>
</Content>
<Footer>
<h1>AMF 0.9</h1>
</Footer>
</mob:MobilePage>
</asp:Content>
Code explantation:
You can customize the listview using theme property like other controls. But you can also customize the separator with the SeparatorTheme property.
Use SearchEngine’s property to add a search box for the listview.
Use WithDataSeparator’s property to add GroupBy function on the listview. Set SeparatorProperty to fill dynamically the groupbox.
public partial class UsersList : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
if (HttpContext.Current.Request.QueryString["c"] != null)
{
string country = HttpContext.Current.Request.QueryString["c"];
Control control = ControlHelper.GetControlById(mpUsersList, "lv");
if (control is WebControls.ListView)
{
WebControls.ListView lv = control as ListView;
List<User> users = UserManager.GetUsersByCountry(country);
var q = from p in users
orderby p.Name ascending
select p;
lv.DataSource = q.ToList();
lv.DataBind();
}
}
}
}
Result :
G. AddUserPage: InputField, RangedField, ComboBox, FlipToggleSwitchField, Form
AddUserPage is a form to add add user.
<%@ Page Title="" Language="C#" MasterPageFile="~/Site.Master" AutoEventWireup="true" CodeBehind="AddUser.aspx.cs" Inherits="Mobile.FirstWebApplication.AddUser" %>
<%@ Register TagPrefix="mob" Namespace="Mobile.WebControls" Assembly="AMF" %>
<%@ Register TagPrefix="mob" Namespace="Mobile.WebControls.Field" Assembly="AMF" %>
<asp:Content ID="BodyContent" ContentPlaceHolderID="MainContent" runat="server">
<mob:MobilePage ID="mpUsersList" HeaderPosition="Fixed" FooterPosition="Fixed" Theme="B" runat="server">
<Header>
<mob:IconLinkButton runat="server" ForceToReload="true" Text="Back" Url="CountryList.aspx" Icon="Back" DataDirection="Reverse" DataTransition="Slide"/>
<h1>Add User</h1>
</Header>
<Content>
<mob:Form Action="AddUserResult.aspx" Method="Get" runat="server">
<Content>
<mob:InputField FieldName="name" LabelText="Name" runat="server"/>
<mob:RangedField runat="server" FieldName="age" LabelText="Age" Min="0" Value="20" Max="120"/>
<mob:ComboBox ID="cbCountry" FieldName="country" DataTextField="Name" DataValueField="Name" Text="Country" runat="server"></mob:ComboBox>
<mob:FlipToggleSwitchField LabelText="Is Active" FieldName="isactive" OptionOneText="On" OptionOneValue="true" OptionTwoText="Off" OptionTwoValue="false" runat="server"/>
<mob:InputField InputType="Submit" Value="submit" runat="server"/>
</Content>
</mob:Form>
</Content>
<Footer>
<h1>AMF 0.9</h1>
</Footer>
</mob:MobilePage>
</asp:Content>
Code explantation:
Form:
Add a form control and set Method (Get or Post) and Action property.
Fields:
You can set the field’s label using the LabelText’s property.
InputField:
You can customize InputField with InputType property:
- Text
- Password
- Number
- Email
- Url
- Tel
- Submit
- Range
- Search
RangedField:
RangedField is a slider.
Use the property Min / Max / Value to customize the control.
FlipToggleSwitchField:
A binary "flip" switch is a common UI element on mobile devices that is used for any binary on/off or true/false type of data input. You can either drag the flip handle like a slider or tap on each half of the switch.
To customize Text, Use the property OptionOneText and OptionTwoText.
To customize Value, use the property OptionOneValue and OptionTwoValue.
ComboBox:
ComboBox is a databindable control. Bind it to fill the control, use DataTextField / DataValueField.
If you want a horizontal layout, set DataType property to Horizontal.
To use Group By, you've to set DataGroupByField.
To use the multiple select options, you've to set MultipleSelect to "true".
To bind the combobox, it’s like the listview.
public partial class AddUser : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
Control control = ControlHelper.GetControlById(mpUsersList, "cbCountry");
if (control is ComboBox)
{
ComboBox comboBox = control as ComboBox;
comboBox.DataSource = CountryManager.GetCountries();
comboBox.DataBind();
}
}
}
Result:
H. AddUserResultPage: QueryString, MobilePage theming
Nothing special here for the QueryString, it’s like the standard asp.net.
<%@ Page Title="" Language="C#" MasterPageFile="~/Site.Master" AutoEventWireup="true" CodeBehind="AddUserResult.aspx.cs" Inherits="Mobile.FirstWebApplication.AddUserResult" %>
<%@ Register TagPrefix="mob" Namespace="Mobile.WebControls" Assembly="AMF" %>
<asp:Content ID="BodyContent" ContentPlaceHolderID="MainContent" runat="server">
<mob:MobilePage ID="mpUsersList" HeaderPosition="Fixed" FooterPosition="Fixed" Theme="E" runat="server">
<Header>
<h1>User</h1>
</Header>
<Content>
<%=Result %>
<mob:IconLinkButton runat="server" Icon="Home" ForceToReload="true" Text="Homepage" Url="Default.aspx" />
</Content>
<Footer>
<h1>AMF 0.9</h1>
</Footer>
</mob:MobilePage>
</asp:Content>
Code explantation:
MobilePage theming:
Set Theme property of the MobilePage.
You’ve got 5 themes:
public partial class AddUserResult : System.Web.UI.Page
{
protected string Result;
protected void Page_Load(object sender, EventArgs e)
{
if ((HttpContext.Current.Request.QueryString["name"] != null) && (!String.IsNullOrEmpty(HttpContext.Current.Request.QueryString["name"]))
&& (HttpContext.Current.Request.QueryString["age"] != null) && (HttpContext.Current.Request.QueryString["country"] != null)
&& (HttpContext.Current.Request.QueryString["isactive"] != null))
{
string name = HttpContext.Current.Request.QueryString["name"];
string age = HttpContext.Current.Request.QueryString["age"];
string country = HttpContext.Current.Request.QueryString["country"];
string isactive = HttpContext.Current.Request.QueryString["isactive"];
UserManager.AddUser(name, Boolean.Parse(isactive), country, Int32.Parse(age));
Result = "User added";
}
else
{
Result = "Error: You have to fill all fields";
}
}
}
Result:
I. Conclusion
This is just a little example of an AMF application. You are far from seeing all the functionalities of the framework.
Some other possibilities:
- Persistent footer
- DialogBox
- …
For more informations, you can visit: