Implement master pages in Silverlight

No.of Views4467
Bookmarked0 times
Downloads 
Votes0
By  MIB426   On  16 Feb 2010 03:02:12
Tag : Silver Light and XAML , Applications
This article will demonstrate how to build a traditional master page style application in Silverlight.
emailbookmarkadd commentsprint

Images in this article missing? We recently lost them in a site migration. We're working to restore these as you read this. Should you need an image in an emergency, please contact us at info@codegain.com

 

Introduction

There are several posts about how to implement the master page feature in Silverlight. So the question is do we really need this master page feature in Silverlight. If there is an advantage to use the master page features in ASP.NET, then I can’t see a reason why Silverlight can’t take advantage of this as well. This article will demonstrate how to build a traditional master page style application in Silverlight.

System Requirement

* This application requires the user to have copy of Visual Studio 2008
* Microsoft® Silverlight™ 3 Tools for Visual Studio 2008 SP1

Design Requirement

Just like traditional web pages, the login page is in start up as Fig 1. The login control consists of 2 text boxes, 1 combo box and 2 button controls. The text boxes collect username and password information from the user, the combo box is used to determine which environment to login. The Cancel button will remove the data in the text boxes and the Login button is used to submit the information for authentication.

Image Loading
Fig 1. Login Page
 
After clicking the login button, the MainPage will display as Fig 2.
Image Loading

Fig 2. MainPage

 

The MainPage has two main sections: master page section and sub page section as Fig 3. The master page section has command buttons bar across the top and the tree view menu on the left side. The sub page section has a content area on the right.

Image Loading

Fig 3. Master Page section and Sub Page section

 

The master page consists of:

* Form Title Label: to display sub form ID
* User ID Label: to display the current user
* System Label: to display system environment name
* Date Label: to display current date
* Count Label: to display data record count
* Status Label: to display current state
* Tree view: to dynamically change the content in content area
* 11 command buttons: to do action in sub page

Image Loading

Fig 4. Details of master page and sub page

 

11 command buttons include the following:

* Search: to trigger Search state
* Execute: to extract server data back to client and also trigger Modify state
* Edit: to make editable field control enable
* Delete: to delete current record
* Save: to save update change
* First Record: go to first record
* Previous Record: go to previous record
* Next Record: go to next record
* Last Record: go to last record
* Excel: to export data to excel
* Exit: to exit and close browser

Image Loading

Fig 5. 11 command buttons description

 

 

There are 4 kinds of states:

INITIAL : Search button is enabled as Fig 6.

Image Loading

Fig 6. Initial State

SEARCH: Search and Execute is enabled as Fig 7.
Image Loading

 

Fig 7. Search State

 

MODIFY: All buttons are enabled except the execute button as Fig 8.

Image Loading

Fig 8. Modify State

 

CUSTOM: You can decide which buttons to enable/disable, for example, you can enable all buttons as Fig 9.

Image Loading

Fig 9. Custom State

Tree view can be expanded or reduced as Fig 10.
Image Loading

 

Fig 10. Expanding tree menu

Brief of Each Project

There are four projects:

* DataObjectCollection : Data structures that are used by the service to communicate with client
* CommandInMasterDaoWcf: The service to pass data from server side to the client.
* CommandInMasterDemo : The Silverlight Application project.

There are 3 main Silverlight control need to address:

* LeftTreeViewMain - Tree view menu
* Login Control – Login page
* TopToolBar - Control contains 11 command buttons

* CommandInMasterDemo.Web: This is created as you create thr Silverlight application. It will host the Silverlight controls to run in the browser.
Image Loading

Fig 11. Projects

Using the Code

Before we start to look into sample, there are few methods I need to address first.

App.xaml.cs

To classify 4 states, Initial, Search, Modify and Custom

 

public enum WorkState
{

INT,//Initial State
SEA,//Search State
MOD,//Modify State
CUS,//Custom State
}
 

 

 
Get or Set tree view menu control
public static System.Collections.ObjectModel.Collection<MenuDataContext> MenuList { get; set; }


Get or Set Current Form ID
public static string CurrentFormID
{
get;
set;
}


In the Application_Startup, add login control in the RootVisual. It will make login control in the start up.
private void Application_Startup(object sender, StartupEventArgs e)
{
this.RootVisual = rootVisual;
rootVisual.Children.Add(lg);
}


I am using reflection to create an instance of user control. For example, the MainPage has namespace CommandInMasterDemo and class name MainPage, therefore I can use reflection to create the CommandInMasterDemo.MainPage object then convert it into UserControl type. If the user control is not null, I set the CurrentFormID to strName then add user control into current Application.RootVisual.
 
public static void Navigate(string strName)
{
App CurrentApp = (App)Application.Current;
Type type = CurrentApp.GetType();
Assembly assembly = type.Assembly;
UserControl uc = (UserControl)assembly.CreateInstance(type.Namespace + "." + strName);
if (uc != null)
{
CurrentFormID = strName;
CurrentApp.rootVisual.Children.Clear();
CurrentApp.rootVisual.Children.Add((UserControl)assembly.CreateInstance(type.Namespace + "." + strName));

}
}

 

The GetUserControl also uses reflection to create a user control instance. The only difference between Navigate and GetUserControl is preparing the namespace. All sub pages have its own sub group folder , such as CHM, FCM. Therefore we need to add a sub group name in the namespace. For example FCM201 user control has the namespace CommandInMasterDemo.FCM and the class name FCM201, therefore we use type.Namespace + "." + strName.Substring(0, 3) + "." + strName to create its instance.
public static UserControl GetUserControl(string strName)
{
CurrentFormID = strName;
App CurrentApp = (App)Application.Current;
Type type = CurrentApp.GetType();
Assembly assembly = type.Assembly;
return (UserControl)assembly.CreateInstance(type.Namespace + "." + strName.Substring(0, 3) + "." + strName);
}

 

LoginControl.xaml.cs


The login button will trigger proxy_GetUserInfoCompleted, then proxy_GetUserInfoCompleted will trigger proxy_GetFunctionMenuCompleted
 
private void btnLogin_Click(object sender, RoutedEventArgs e)
{
if (string.IsNullOrEmpty(txtAccount.Text) || string.IsNullOrEmpty(txtPassword.Password))
{
txtErrorInformation.Text = "Account and Password must enter";
return;
}
else
{
this.Cursor = Cursors.Wait;
try
{
DaoWcfClient daoWcf = new DaoWcfClient();
daoWcf.GetUserInfoCompleted += new EventHandler<GetUserInfoCompletedEventArgs>(proxy_GetUserInfoCompleted);
daoWcf.GetUserInfoAsync(txtAccount.Text, txtPassword.Password);
}
catch (Exception ex)
{
MessageBox.Show("btnLogin_Click Exception: " + ex.Message);
}
finally
{
this.Cursor = Cursors.Arrow;
}
}
}

void proxy_GetUserInfoCompleted(object sender, GetUserInfoCompletedEventArgs e)
{
try
{
strCurrentUser = e.Result;
if (string.IsNullOrEmpty(strCurrentUser))
{
txtErrorInformation.Text = "Account or Password is incorrect";
}
else
{
Resources.Remove("CurrentUser");
Resources.Add("CurrentUser", txtAccount.Text);
Resources.Remove("CurrentDatabase");
Resources.Add("CurrentDatabase", cbDb.SelectionBoxItem.ToString());
DaoWcfClient daoWcf = new DaoWcfClient();
daoWcf.GetFunctionMenuCompleted += new EventHandler<GetFunctionMenuCompletedEventArgs>(proxy_GetFunctionMenuCompleted);
daoWcf.GetFunctionMenuAsync(txtAccount.Text);
}
}
catch (Exception ex)
{
MessageBox.Show("proxy_FindUserInfoCompleted Exception: " + ex.Message);
}
}
void proxy_GetFunctionMenuCompleted(object sender, GetFunctionMenuCompletedEventArgs e)
{
System.Collections.ObjectModel.Collection<MenuDataContext> list = e.Result;
if (list.Count > 0)
{
App CurrentApp = (App)Application.Current;
App.MenuList = list;
App.Navigate("MainPage");
}
}
 
Lets have look into proxy_GetUserInfoCompleted, I am storing User ID and System name by using Resources.
 
Resources.Remove("CurrentUser");
Resources.Add("CurrentUser", txtAccount.Text);
Resources.Remove("CurrentDatabase");
Resources.Add("CurrentDatabase", cbDb.SelectionBoxItem.ToString());
 

 

In the proxy_GetFunctionMenuCompleted, after I got menu list I store the menu list to the App MenuList property, then use App.Navigate to go MainPage
 
System.Collections.ObjectModel.Collection<MenuDataContext> list = e.Result;
if (list.Count > 0)
{
App CurrentApp = (App)Application.Current;
App.MenuList = list;
App.Navigate("MainPage");
}
 
I created a delegate MenuEventHandler(object sender, RouteEventArgs e) for all command buttons.
public delegate void MenuEventHandler(object sender, RoutedEventArgs e);
 

 

Each command button has its own event

public event MenuEventHandler SearchClick;
public event MenuEventHandler ExecuteClick;
public event MenuEventHandler EditClick;
public event MenuEventHandler DeleteClick;
public event MenuEventHandler SaveClick;
public event MenuEventHandler LastClick;
public event MenuEventHandler FirstClick;
public event MenuEventHandler PreviousClick;
public event MenuEventHandler NextClick;
public event MenuEventHandler ExcelClick;


 

 

There are 3 properties:

* CurrentState : Get/Set current states

public WorkState CurrentState
{
get
{
return curretState;
}
set
{
curretState = value;
SetButtonState();
}
}

 

 BindGrid : Get/Set DataGrid control, so it can interact with First, Previous, Next and Last buttons. 

public DataGrid BindGrid { get; set; }
 

TotalRowCount : Get/Set total data record count

public int TotalRowCount { get; set; }

 

The SetButtonState is to hide/show command buttons in different states.

 

private void SetButtonState()
{
switch (CurrentState)
{
case WorkState.INT:
txtStatus.Text = "Initial";
//Search
btnSearch.IsEnabled = true;
imgbtnSearchOn.Visibility = Visibility.Visible;
...............
break;
case WorkState.SEA:
txtStatus.Text = "Search";
//Search
btnSearch.IsEnabled = true;
imgbtnSearchOn.Visibility = Visibility.Visible;
...............
break;
case WorkState.MOD:
txtStatus.Text = "Modify";
//Search
btnSearch.IsEnabled = true;
imgbtnSearchOn.Visibility = Visibility.Visible;
...............
break;
case WorkState.CUS:
txtStatus.Text = "Custom";
break;
default:
txtStatus.Text = "Search";
//Search
btnSearch.IsEnabled = true;
imgbtnSearchOn.Visibility = Visibility.Visible;
...............
break;
}
}
 

There are two ways to trigger 4 record movement buttons (First, Previous, Next and Last). One is interact with the DataGrid control, the other is to trigger it in the sub page.

 

private void btnLast_Click(object sender, RoutedEventArgs e)
{
if (BindGrid != null)
{
BindGrid.SelectedIndex = TotalRowCount - 1;
}
else
{
LastClick(this, e);
}
}
private void btnNext_Click(object sender, RoutedEventArgs e)
{
if (BindGrid != null)
{
if (BindGrid.SelectedIndex != TotalRowCount - 1)
{
BindGrid.SelectedIndex = BindGrid.SelectedIndex + 1;
}
}
else
{
NextClick(this, e);
}
}
private void btnPrevious_Click(object sender, RoutedEventArgs e)
{
if (BindGrid != null)
{
if (BindGrid.SelectedIndex != 0)
{
BindGrid.SelectedIndex = BindGrid.SelectedIndex - 1;
}
}
else
{
PreviousClick(this, e);
}
}
private void btnFirst_Click(object sender, RoutedEventArgs e)
{
if (BindGrid != null)
{
BindGrid.SelectedIndex = 0;
}
else
{
FirstClick(this, e);
}
}
 

CommonUtility.cs

In order to get TopToolBar control, I need to find the MainPage control first. That’s because the MainPage contains TopToolBar.

public MainPage GetMainPage(UserControl currentPage, bool blSub)
{
MainPage mainPage = blSub ? (MainPage)((Grid)((Grid)((Grid)currentPage.Parent).Parent).Parent).Parent : (MainPage)((Grid)((Grid)currentPage.Parent).Parent).Parent;
return mainPage;
}

 

After I found the MainPage control, I can use FindName method to get TopToolBar control.

public GetTopToolBar(UserControl currentPage, bool blSub)
{
ttb = GetMainPage(currentPage, blSub).FindName("topToolBar") as ;
return ttb;
}

 

ExportExcel.ashx.cs


The Silverlight doesn’t offer ability to save a file on local disk, therefore I use handler to create CSV/Excel file.

public void ProcessRequest(HttpContext context)
{
string strContext = context.Request.QueryString["Context"] != null ?
HttpUtility.UrlDecode(context.Request.QueryString["Context"]) : DateTime.Now.ToString("yyyyMMdd_HHmmss");
string[] strSplit = strContext.Replace("[", "").Replace("]", "").Split(char.Parse(";"));
string strFileName = strSplit[0];
string strQueryCase = strSplit[1];
DataGrid dg = new DataGrid();
switch (strQueryCase)
{
case "FindMTAccntScopeByYear":
List<AccountDataContext> list = new ().GetAccountByYear(strSplit[2]);
dg.DataSource = list;
break;
case "FindAllyCompAccountByOwnerId":
List<AllyCompAcctDataContext> listAlly = new DaoWcf().GetAllyCompAccountByOwnerId(strSplit[2]);
dg.DataSource = listAlly;
break;
}
dg.DataBind();
context.Response.Buffer = true;
context.Response.ClearContent();
context.Response.ClearHeaders();
context.Response.ContentType = "application/vnd.ms-excel";
context.Response.AddHeader("content-disposition", "attachment;filename=" + strFileName + ".xls");
dg.HeaderStyle.ForeColor = Color.Blue;
dg.HeaderStyle.BackColor = Color.White;
dg.ItemStyle.BackColor = Color.White;
System.IO.StringWriter tw = new StringWriter();
System.Web.UI.HtmlTextWriter hw = new HtmlTextWriter(tw);
dg.RenderControl(hw);
context.Response.Write(tw.ToString());
context.Response.Flush();
context.Response.Close();
context.Response.End();
} 

 

Demonstration


There are 3 samples I am going to go through:

FCM201 HARDWARE

This sample is showing how to use button to trigger different state.

private void Button_Click(object sender, RoutedEventArgs e)
{
Button b = (Button)sender;
switch (b.Tag.ToString())
{
case "INT":
WorkState.INT;
break;
case "SEA":
WorkState.SEA;
break;
case "MOD":
WorkState.MOD;
break;
case "CUS":
WorkState.CUS;
true;
topToolBar.ExecuteEnable = true;
topToolBar.EditEnable = true;
topToolBar.DeleteEnable = true;
topToolBar.SaveEnable = true;
topToolBar.RecordMoveEnable = true;
topToolBar.ExcelEnable = true;
break;
}
}

 

Initial button: to trigger Initial state as Fig 12

Image Loading
Fig 12. Initial State


Search button: to trigger Search state

Image Loading
Fig 13. Search state


Modify button: to trigger Modify State

Image Loading
Fig 14. Modify State


Custom button to trigger Custom state

Image Loading

Fig 15. Custom State

 

FCM202 SOFTWARE
 

This is the default flow in a general case. The flow goes Initial State -->Search State --> Modify State.

In the initial state, there is only Search button is enable.

Image Loading
Fig 16. Initial State


After click Search button, the state will change to Search and Execute button will become visible.

 

Image Loading
Fig 17. Search State


After clicking the Execute button, all command buttons became visible except Execute button. Now you should see the data displayed in the content page.

Image Loading

Clicking the Delete Button, it will come up warning message. The deletion is not functional in this sample.

Image Loading

Fig 19. Deletion Button

Clicking Edit Button, it will change the editable fields to allow modifications. The date type file will display a calendar control. The multi-selection field will display a combo box.
Image Loading

Fig 20. Edit Button

After you modify data, you can click Save button to update server data.
Image Loading

Fig 21. Save Button

In FCM202, I trigger record navigation button in the sub page control.
 
voidobject sender, RoutedEventArgs e)
{
iCurrent = 0;
SetCountStatus(iCurrent);
}
voidobject sender, RoutedEventArgs e)
{
iCurrent = list.Count - 1;
SetCountStatus(iCurrent);
}
voidobject sender, RoutedEventArgs e)
{
if (iCurrent != list.Count - 1)
{
iCurrent = iCurrent + 1;
SetCountStatus(iCurrent);
}
}
voidobject sender, RoutedEventArgs e)
{
if (iCurrent != 0)
{
iCurrent = iCurrent - 1;
SetCountStatus(iCurrent);
}
}
Image Loading

Fig 22. Record Navigation Button

pass information to handler to generate excel file
 
voidobject sender, RoutedEventArgs e)
{
string strOwnerId = txtOwnerId.Text;
string strEncodeUrl = System.Windows.Browser.HttpUtility.UrlEncode("[AllyCompAcct;FindAllyCompAccountByOwnerId;" + strOwnerId + "]");
string strUri = "http://localhost/CommandInMasterDaoWcf/ExportExcel.ashx?Context=" + strEncodeUrl;
HtmlPage.Window.Navigate(new Uri(strUri, UriKind.Absolute));
}
Image Loading

Fig 23. Export data to excel

FCM203 LOCAL


This is a customize flow. The flow goes Initial State --> Search State --> Custom State.
In order to active custom state, you need to set topToolBar.CurrentState = WorkState.CUS

Image Loading

Fig 24. Custom State

In FCM203, I trigger record navigation button in the TopToolBar by set datagrid to TopToolBar’s BindGrid property.
topToolBar.BindGrid = this.dgAccountYear;
topToolBar.TotalRowCount = list.Count;
Image Loading

Fig 25. Record Navigation Button

 

Moving Forward


I am placing this code into the public domain without restriction. It doesn't have the best pattern design or coding style. Anyone can use it for any purpose, including in commercial products. If you can improve the code or even make it more clear, please let me know. I will update the code to make it more useful. Thank you all.

Sample Project Source

Download source files -2192 kb

 
Sign Up to vote for this article
 
About Author
 
MIB426
Occupation-Not Provided
Company-Not Provided
Member Type-Fresh
Location-Not Provided
Joined date-06 Aug 2009
Home Page-Not Provided
Blog Page-Not Provided
 
 
Other popularSectionarticles
    Performance is a vital part for any application possibly more important for web based app.With wizard based approach of RIA service generally we tends to comprise all entities, exposing to the client and also allowing the DomainService to go for everything from the database. Not only this approach takes a toll on security, but also it loads the middle tier unreasonably. So this post is about few tips using which we can improve the performance. Well mostly we will cover the Pagination, Limiting
    Published Date : 10/Apr/2011
    My earlier post was about the basic CRUD operations on entities with relation .We had discussed about creating simple association and querying as well as Invoking CRUD operation onto them.As i have mentioned there ,here in this article we will have an in-depth look into Presentation Model and its mode of handling related multiple entities.
    Published Date : 10/Mar/2011
    Silvelight is latest buzz in Microsoft.NET Technologies. Silverlight enable us to create rich user interface for the web based application
    Published Date : 16/May/2010
    In the previous lesson we talked about COM automation support introduced in Silverlight 4 and we said that COM automation is available only for Silverlight OOB (Out-of-Browser) applications that have Elevated Trust, and that’s one of the security restrictions imposed by Silverlight. Today, we’re going to talk about COM automation in more details and give few Silverlight examples that make use of this great feature.
    Published Date : 31/Mar/2011
    In April 2010 Silverlight 4 was released to the world with one of its great features ever, COM-automation support, that allows you to create cutting-edge applications that do everything you can imagine.
    Published Date : 27/Mar/2011
Comments
There is no comments for this articles.
Leave a Reply
Title:
Display Name:
Email:
(not display in page for the security purphase)
Website:
Message:
Please refresh your screen using Ctrl+F5
If you can't read this number refresh your screen
Please input the anti-spam code that you can read in the image.
^ Scroll to Top
</