Passing Data and Uri Mapping
It's very simple to pass data from one Silverlight page to another:
Say you have an AutoCompleteBox
and a Button
on PageA.xaml, and when you press the button, you want to 1) reach PageB.xaml, and 2) print the content of the AutoCompleteBox
in to a TextBlock
in PageB.xaml.
Step 1: Insert the AutoCompleteBox
and a Button
. Notice the x:Name
, and Click
:
<input:AutoCompleteBox x:Name="Supplier" IsTextCompletionEnabled ="True" /> <Button Content="Search" Click="Button_Click"/>
Step 2: Go to PageA.xaml.cs and create the navigate method:
void Button_Click(object sender, RoutedEventArgs e) { if (Supplier.Text != "") //if user has inserted anything in the AutoCompleteBox { string supplier = Supplier.Text.ToString(); //Supplier = name of AutoCompleteBox NavigationService.Navigate(new Uri(string.Format("/PageB.xaml?Supplier={0}", supplier), UriKind.Relative)); } else { MessageBox.Show("Enter something"); } }
Step 3: Create PageB.xaml and make a new TextBlock that will contain the content retrived from the AutoCompleteBox
on PageA:
<TextBlock x:Name="Supplier_name"/>
Step 4: Go to PageB.xaml.cs and create the method that will accept the value from PageA and insert it into the TextBlock
.
protected override void OnNavigatedTo(NavigationEventArgs e) { string supplier = NavigationContext.QueryString["Supplier"].ToString(); Supplier_name.Text = supplier; }
This will work perfectly, but you will notice that your URL doesn't look very pretty when you reach PageB. So, we will use Uri Mapping to define what we want the URL to look like.
Uri Mapping
Step 1: Go to your App.xaml
and insert xml namespace for the navigation framework at the top:
xmlns:nav="clr-namespace:System.Windows.Navigation;assembly=System.Windows.Controls.Navigation">
Step 2: Go to PageA.xaml
and change your Button_Click
method. Inside the string.Format()
you add what you want the URL to be called, followed by the value you pass:
string supplier = Supplier.Text.ToString(); NavigationService.Navigate(new Uri(string.Format("PageB/{0}", supplier), UriKind.Relative));
Step 3: Add a new UriMapper to the Application Ressources. The Uri
represents what the URL will look like, and the MappedUri
where the button links to:
<Application.Resources> <nav:UriMapper x:Key="uriMapper"> <nav:UriMapping Uri="PageB/{c}" MappedUri="/PageB.xaml?Supplier={c}" /> </nav:UriMapper> </Application.Resources>
Notice that the string.Format("PageB/{0}"...
is identical to the Uri="PageB/{c}"
, except the value inside the brackets.
Step 4: Go to your Navigation Framework
(probably located at your MainPage.xaml
), and add the UriMapper property to the Navigation Frame (see line 5):
<navigation:Frame x:Name="ContentFrame" Grid.Row="1" Grid.Column="1" Source="/Customer_Home.xaml" UriMapper="{StaticResource uriMapper}" />
After running this code you'll see, that the URL is called "...PageB/ followed by the value entered in the AutoCompleteBox
.
DataGrid Control
The DataGrid property allows you to list large amounts of data within a table. With Silverlight, the grid contains rich user functionality, including automatic resizing, reordering and auto-generating of columns.
This post is only relevant, if you want to hardcode data and insert it into a grid. If you want to get the data from a database, you'll need to do it trough a Web Service. See my post called Accessing Data Through Web Services to learn more about this.
Step 1: You need to create a reference to System.Windows.Controls.Data
, by right-clicking on your project >> Add Reference >> .Net >> code>System.Windows.Controls.Data. Then, insert the following xmlns at the top of your .xaml page, where you will be inserting your Grid:
xmlns:Data="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Data"
Step 2: Insert the DataGrid into your .xaml page.
Notice the IsReadOnly
property:
<data:DataGrid x:Name="dg" IsReadOnly="True" Margin="10"/>
Step 3: Create a new class by right-clicking on the project >> Add >> Class. Populate the DataGrid by building a class that will be bound to the DataGrid. We define a few properties to bind to, and these represent the columns that the DataGrid
will contain.
I called the class Data.cs
and inserted the following code:
public class Data { public string Name { get; set; } public int Green { get; set; } public int Yellow { get; set; } public int Red { get; set; } public int NA { get; set; } public string Certification { get; set; } }
Step 4: In the code-behind for the .xaml page that contains your DataGrid
, insert the code shown below.
Notice that the List< Data> on line 5 refers to the class, and the dg
in the dg.ItemsSource
refers to the name you have given the DataGrid
.
public Page() { InitializeComponent(); List<Data> source = new List<Data>(); int itemsCount = 15; for (int i = 1; i < itemsCount; i++) { source.Add(new Data() { Name = "Company " + i, Green = i + 20 - 7, Yellow = 20 + 7 - i, Red = 0, NA = 0, Certification = "test" + i }); } dg.ItemsSource = source; }
That's it!
Of course, the loop is just practical for creating rows and content fast. In any other case, just delete the loop and repeat the Source.Add(newData)
method as many times as needed.
By following the steps above, you'll get a DataGrid like the one shown below:
AutoCompleteBox
When inserting a search function within your application, it is useful to use the AutoCompleteBox
if you already know what the user will search for. In the case of my application, the user can only search for a specific set of company names, and so the AutoCompleteBox
is useful.
Like in the previous post, I will hardcode the set data into the code, but this will later be changed so it collects the names from a MySQL database.
First, you need to create a reference to System.Windows.Controls
and to System.Windows.Controls.Input
. You do this by right-clicking on your project >> Add reference >> Choose System.Windows.Controls, and System.Windows.Controls.Input. If you do not add the reference, you'll be presented with an error message saying "Verify that you are not missing an assembly reference".
Next, you need to add a an xmlns
entry at the top of your .xaml page.
xmlns:input="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Input"
Then you just insert your new AutoCompleteBox
. Something like this (notice the IsTextCompletionEnabled
):
<input:AutoCompleteBox x:Name="Supplier" Width="220" Height="20" Margin="10" IsTextCompletionEnabled ="True"/>
The IsTextCompletionEnabled
enables the automatic completion of the text as you type. Its default is set to False
, so if you do not wish it to auto-complete, then do not add the property.
In the code-behind, you can bind a simple string array to the ItemsSource
property:
public Customer_Find_Supplier() { InitializeComponent(); this.Supplier.ItemsSource = new string[] { "Company One", "Company Two", "Company three" }; }
And that's it!
Linking from User Control to User Control
As I showed in a recent post, it is possible to link from a Silverlight User Control to a Silverlight page using the Silverlight Navigation Framework. But at times you will need to link from a User Control to another User Control, and there you won't be able to use it.
In my case, I encoutered this situation because my application has three different user types, who each have their own Navigation Framework. They have this because they each have vastly different tasks to perform, and thus there's need of different menus to access different pages. But there is one task that they must all carry out: Before any user can access the application, they must log in. Of course, the login page is a User Control, so I needed to link the Login page to one of the Navigation Framework user pages, which are also User Controls.
Basically, you will need too add a small code snippet outside the Grid
on the page you wish to link from (in my case, the login-page), and an event EventHandler
to a button. In the EventHandler
you specify which grids are visible, and which are not.
Below, I have posted a very simplified version of my MainPage.xaml
, that I use for the login. Notice that there are two grids at the top, and that we have named the inner grid Login
, and its Visibility
is set to Visible
:
<UserControl x:Class="QM_v1.MainPage" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" mc:Ignorable="d" xmlns:local="clr-namespace:QM_v1" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" d:DesignHeight="437" d:DesignWidth="947"> <!--Background layout--> <Grid x:Name="LayoutRoot"> <Grid x:Name="Login" Background="White" Width="Auto" Height="auto" ShowGridLines="False" Visibility="Visible"> <!-- Focus on the 'Enter' button --> <TextBox x:Name="Mail"> <PasswordBox x:Name="Pass" > <StackPanel Grid.Row="2" Grid.Column="2" Orientation="Horizontal"> <Button Content="Cancel" Click="Cancel_button"/> <Button Content="Enter" Click="Enter_button"/> </StackPanel> </Grid> <!-- Important bit: --> <local:Customer x:Name="Customer" Visibility="Collapsed"/> <local:Supplier x:Name="Supplier" Visibility="Collapsed"/> <local:ETS x:Name="ETS" Visibility="Collapsed"/> </Grid> </UserControl>
Notice that the local:Customer
bit is outside the second grid. In here you specify the name of the .xaml pages that you wish to link to, depending on which user logs in. Here I have just specified that I have three .xaml pages (Customer, supplier and ETS), and that each of these pages at this point in time are collapsed, ie. not visible/active.
When the user presses the Enter_Button
you want your EventHandler
to check who the user is, and depending on the outcome they should be directed to either customer.xaml, supplier.xaml or ETS.xaml. For simplicity, I have hardcoded the user name and password, but they should of course be gathered from a database.
private void Enter_button(object sender, RoutedEventArgs e) { if (Mail.Text != "" && Pass.Password != "")//there is some content. { if (Mail.Text == "customer@gmail.com" && Pass.Password == "customer") { Customer.Visibility = System.Windows.Visibility.Visible; //customer = Name of .xaml page Login.Visibility = System.Windows.Visibility.Collapsed; //Login = inner grid at MainPage.xaml } else if (Mail.Text == "supplier@gmail.com" && Pass.Password == "supplier") { Supplier.Visibility = System.Windows.Visibility.Visible; Login.Visibility = System.Windows.Visibility.Collapsed; } else if (Mail.Text == "ets@gmail.com" && Pass.Password == "ets") { ETS.Visibility = System.Windows.Visibility.Visible; Login.Visibility = System.Windows.Visibility.Collapsed; } else { MessageBox.Show("Please enter correct e-mail and password."); } } else { MessageBox.Show("Please type your e-mail and password.", "Error", MessageBoxButton.OK); } }
Notice that the two things happen: 1) The users' own page is set to visible (Customer.Visibility = System.Windows.Visibility.Visible;
) and the current page, the MainPages' Login
-grid, is closed/collapsed (Login.Visibility = System.Windows.Visibility.Collapsed;
).
VoilĂ !
Adding Images
Here is just a quick guide to inserting images into your Silverlight application, and how to make them link to other pages. In this example I will just hardcode the name of the image, but I will soon write another post about how to insert images dynamically.
First, you need to create an images
folder. If the name of your application is MyApp
, go to the ClientBin folder located at MyApp >> MyApp.Web >> ClientBin, and create the images
folder.
The following simple code snippet shows how you insert an image located in your images
folder.
<Image Source="../images/picture.jpg" Height="100" />
If you want your image to link to another Silverlight page, you simply create the image as an HypelinkButton
:
<HyperlinkButton HorizontalAlignment="Center" NavigateUri="/mypage.xaml"> <HyperlinkButton.Content> <Image Source="../images/picture.jpg" Height="100" /> </HyperlinkButton.Content> </HyperlinkButton>
Oh, and remember that Silverlight only supports .png and .jpg images.
Views – Navigation Framework
When developing your Silverlight application you will almost always need to create several pages and subpages. Thus, you will need a navigation framework. Silverlight allows you to browse between different pages while enabling forward and backward navigation through the history using the browser's back and forward buttons.
This post provides a complete walk trough on how to implement a menu in your Silverlight application, and how to create and access subpages. You will end up with something like the application showed below (but without the formatting):
The working application is temporarily hosted here.
Step 1: Add support for the Navigation Framework by adding a reference to System.Windows.Controls.Navigation.dll
by right clicking on the References
folder in your Silverlight Project, and choosing Add reference
.
Step 2: Go to the .Net tab, and select the System.Windows.Controls.Navigation
, as seen below:
Notice that it says "Filtered to: Silverlight 4" at the top. If you happen to have converted your Silverlight application from Silverlight 3, then it will say "Filtered to: Silverlight 3", and you will need to update this by right-clicking on you project >> Properties >> and set Target Silverlight Application
to "Silverlight 4".
Step 3: You will need to add System.Windows.Controls.Navigations
to your UserControl
definition. Insert the following code at the top of your MainPage.xaml:
xmlns:navigation ="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Navigation"
Step 4: Create a StackPanel
within your Grid
in your MainPage.xaml, and insert HyperlinkButtons
, that will be used as the menu. Something like this:
<!--Menu --> <StackPanel Orientation="Horizontal" HorizontalAlignment="Center" VerticalAlignment="Bottom" Grid.Column="1" Grid.Row="0"> <HyperlinkButton Content="Home" Click="LinkClick" Tag="/Home.xaml" Padding="5"/> <HyperlinkButton Content="Find supplier" Click="LinkClick" Tag="/Find_Supplier.xaml" Padding="5"/> <HyperlinkButton Content="Questionnaire data" Click="LinkClick" Tag="/Options.xaml" Padding="5"/> </StackPanel> <!--Navigation Frame --> <navigation:Frame x:Name="ContentFrame" Margin="10" Grid.Row="1" Grid.Column="1" BorderBrush="Black" BorderThickness="2" Source="/Home.xaml" />
Note:
Lines 2-6 define how the menu will look like, so you can of course change this as you please.
Line 10, 14, and 18: You will create each of these pages in the next step. Change the Tag
so it matches the name of your subpages.
Line 9, 13, 17: The LinkClick
-method is created later.
Line 28: The Source
states the opening page, ie. the frontpage of your application.
The above code basically just creates the menu that the user will click on to navigate through the application. The XAML page you insert this into will not ever be shown: It is merely used to define the framework and the links to subpages. What you need to do next is actually create all the subpages, that will hold all content.
Step 5: Add a new page by right-clicking on your Project >> Add >> New Item >> New Page. It looks like this:
Remember to name the pages like you stated in the HypperlinkButton
above.
Step 6: Open your new subpages and add simple Textblocks
, preferably including the name of the page, so you can identify it when you run the program.
Step 7: The last step is to add a LinkClick
method in your MainPage.xaml.cs
.
private void LinkClick(object sender, RoutedEventArgs e) { HyperlinkButton button = (HyperlinkButton)sender; string viewSource = button.Tag.ToString(); ContentFrame.Navigate(new Uri(viewSource, UriKind.Relative)); }
And that's it!
Inner Views
If you need to create a new page that is not one of the 'main' pages shown in the menu, you need to create an InnerView. You just do like shown above (you create a button with a Click
property, but without the Tag
property, and you create a new page). The main difference lies in step 7. You have to insert the code below instead:
private void LinkClick(object sender, RoutedEventArgs e) { NavigationService.Navigate(new Uri ("/InnerView.xaml", Uri.Kind.Relative)); }
Enjoy!