导航.简单的理解可以是从一个页面跳转到另外一个页面。在传统的ASP.NET网站中这种效果很容易实现。而在Silverlight中我们也同样可以,我们有两种方法来实现这个效果。
第一个选择是使用代码更改页面视图(修改容器Content属性),移除/添加User Control来实现导航,这个方法比较简单、直接代码量也很少。并且在这个过程中还可以加入动画、变形等效果。
第二个选择就是使用Silverlight的导航系统,导航系统包含两个主要的控件:Frame、Page。基本的效果是可以在一个Frame里面切换多个页面(UserControl、Page)。
此次先简单介绍下第一种方法。 简单的页面切换效果:
这个例子,将页面分成上下两部分,上面表示菜单,下面放置一个容器空间用于承载内容。容器控件你可以旋转Border、ScrollViewer、StackPanel、Grid。下面是主页面的代码:
<Grid x:Name="LayoutRoot">
<Grid.RowDefinitions>
<RowDefinition Height="*"></RowDefinition>
<RowDefinition Height="Auto"></RowDefinition>
<RowDefinition Height="6*"></RowDefinition>
</Grid.RowDefinitions>
<ListBox Grid.Row="0" SelectionChanged="lstPages_SelectionChanged">
<!--页面列表-->
</ListBox>
<basics:GridSplitter Grid.Row="1" Margin="0 3" HorizontalAlignment="Stretch"
Height="2"></basics:GridSplitter>
<Border Grid.Row="2" BorderBrush="SlateGray" BorderThickness="1"
x:Name="mainFrame" Background="AliceBlue"></Border>
</Grid>
这个例子中使用Border作为容器命名为mainFrame,在页面加载的时候先为容器加入一个页面(为项目添加2个以上UserControl):
Page1 page1 = new Page1();
mainFrame.Child = page1;
这里的容器控件是多选的,你也可使用单行单列的Grid。下面为ListBox加入事件代码:
private void lstPages_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
//获取当前的Item
string newPageName = ((ListBoxItem)e.AddedItems[0]).Content.ToString();
//根据名称使用反射创建一个UserControl实例
Type type = this.GetType();
Assembly assembly = type.Assembly;
UserControl newPage = (UserControl)assembly.CreateInstance(
type.Namespace + "." + newPageName);
//加入到容器中.
mainFrame.Child = newPage;
}
至此一个简单的页面切换效果就OK了。这个DEMO的效果图如下:
●使用根视图(Root Visual)
上面是一个简单的例子,这个方法很常见,但不是通用方式。它的小缺陷是整个页面布局已经固定死了,工具栏或页面始终都固定在那里,那如果你想要的是一个全新的页面就不行了。下面对这个小例子进行一下扩展就可以达到我们的目的了。
首先在App.xaml.cs中声明一个Grid:
private Grid rootGrid = new Grid();
然后修改Appliaction_Startup事件代码:
this.RootVisual = rootGrid;
rootGrid.Children.Add(new Page1());
这样只能保证在初始化的时候会有一个页面而不能导航,为此在App.xaml.cs代码中加入一个静态方法,代码如下:
public static void Navigation(UserControl newPage)
{
//获取当前的Appliaction实例
App currentApp = (App)Application.Current;
//修改当前显示页面内容.
currentApp.rootGrid.Children.Clear();
currentApp.rootGrid.Children.Add(newPage);
}
这里要注意 方法的参数是UserControl。这样在你的UserControl中即可添加如下的代码(可以加到button事件中)进行页面切换了:
App.Navigation(new Page2());
new的对象是你的目标页面,不要写错了。就这几句代码又完成了咱们的目的!
●保存页面状态(Cache)
如果你想让用户在返回到历史页面的时候可以页面的修改状态比如用户输入的数据。首先对项目进行一下小小的修改,添加一个名字叫Pages的enmu,用于保存页面名称以便使用字符串产生不必要的问题:
public enum Pages
{
Page1,
Page2
}
下一步是在App.cs代码中加入一个泛型集合用于保存页面:
private static Dictionary<Pages, UserControl> pageCache = new Dictionary<Pages, UserControl>();
其中Key是Pages枚举,Value是UserControl。然后从新定义Naviagte方法:
public static void Navigation(Pages newPage)
{
App currentApp = (App)Application.Current;
if (!pageCache.ContainsKey(newPage))
{
//根据名称使用反射创建目标页面实例,并加入缓存
Type type = currentApp.GetType();
Assembly assembly = type.Assembly;
pageCache[newPage] = (UserControl)assembly.CreateInstance(
type.Namespace + "." + newPage.ToString());
}
currentApp.rootGrid.Children.Clear();
currentApp.rootGrid.Children.Add(pageCache[newPage]);
}
这样在其他的UserControl中调用如下代码就可以进行切换了:
App.Navigation(Pages.Page2);
如果在页面中放置一个文本框然后输入值,跳转到其他页面再切换回来的话就会看到文本框的值依然存在,这是因为UserControl被保存在内存中了。效果图如下:
如果你进行切换就会发现TextBox的值依然存在.这样就实现了简单的缓存.
●页面传值:
关于页面传值我仅仅说一下我的方式,当让网上也有其他的关于页面之间传值的方法。主要是使用独立存储的IsolatedStorageSettings对象,首先在UserControl中创建对象:
private IsolatedStorageSettings appSettings = IsolatedStorageSettings.ApplicationSettings;然后在Button事件中加入如下代码,用于传值:
if (!appSettings.Contains("Page2"))
appSettings.Add("Page2", "UserName");
OK了,在目标页面获取值的方式就简单了.
if (appSettings.Contains("Page2"))
txbShowvalue.Text = "User Name: " + appSettings["Page2"].ToString();
需要注意的一点是这个获取值的代码不要写在页面的构造函数里面,有可能不会触发,原因是在上面对象已经保存在内存中了,但是会触发Loaded事件,因此可以把代码放到这个事件里面。最总效果如下:
你也可以使用同样的方式来给Page1传值。OK..关于第一种页面导航、传值、简单缓存,就介绍完毕..
此次主要说一下如何使用Navigation System进行导航。它包含了两个重要的控件:Frame、Page。其中Frame控件是主要控件因为它负责导航以及显示内容。而Page空间是一个可选的控件,它可以使用普通的UserControl来代替,但两者之间稍有差别,后面会简单说一下。
1.Frame控件
Frame控件也是一个容器控件,它通过Content属性进行修改内容,当然最好是使用Navigate()方法来代替Content属性,因为它既会修改Content属性也会触发事件和保存Frame的日志(历史记录,更改当前浏览器的地址)。下面看一个简单的例子,首先定义一个两行一列的Grid,上面是一个包含了Frame的Border,下面是用来触发导航的按钮:
<UserControl x:Class="FramNavigation.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"
xmlns:navigation="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Navigation"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d" d:DesignWidth="640" d:DesignHeight="480">
<Grid x:Name="LayoutRoot">
<Grid>
<Grid.RowDefinitions>
<RowDefinition></RowDefinition>
<RowDefinition Height="Auto"></RowDefinition>
</Grid.RowDefinitions>
<Border Margin="10" Padding="10" BorderBrush="DarkOrange"
BorderThickness="2" CornerRadius="4">
<navigation:Frame x:Name="mainFrame"></navigation:Frame>
</Border>
<Button Grid.Row="1" Margin="5" Padding="5" HorizontalAlignment="Center"
Content="Go to Page1" Click="btnNavigate_Click"></Button>
</Grid>
</Grid>
</UserControl>
使Frame记得添加System.Windows.Controls.Navigation.dll引用。
然后在项目中创建一个UserControl命名为Page1,并在Main.xaml.cs的按钮事件中加入如下代码:
mainFrame.Navigate(new Uri("/Page1.xaml", UriKind.Relative));
Uri前的“/”表示应用程序的根目录..不可以使用Navigate()方法访问应用程序以外的页面,比如其他网站的页面。同样也可以使用如下代码代替Navigate()方法:
mainFrame.Content = new Page1();
如果你观察就会发现前后的浏览器地址栏是不一样的,你也可以使用Frame.Source属性查看当前Uri..如图:
这是因为只修改Content属性不会触发Navigation事件..这个DEMO最终的效果图如下:
string fragment = System.Windows.Browser.HtmlPage.Document.DocumentUri.Fragment;
3.安全问题如何?
从另一个角度考虑,也给你的程序留了一个很多的后门。比如用户输入URI访问了一个你不想使用Navigate()方法访问的页面。Silverlight本身并没有提供措施来避免此类问题,因此在你使用导航系统的时候潜在的也给你带来了URI访问的问题。
不过幸好你可以使用以下方法来人为避免。第一个像前面一样设置Frame的JournalOwnership为OwnJournal,那样就可以避免使用URL访问你应用程序的任何页面,同时地址也不会集成到浏览器的历史记录列表中。另外一个更好的方法是使用Navigating事件,这个方法可以验证请求的URI从而有选择性的进行导航,验证代码如下:
void mainFrame_Navigating(object sender, System.Windows.Navigation.NavigatingCancelEventArgs e)
{
if (e.Uri.ToString().ToLower().Contains("Page1.xaml")) {
e.Cancel = true; }
}
这样就可以在程序执行Navigate()方法后来验证URI是否合法。从而避免用户访问到禁止请求的页面。
4.对历史记录的支持
Frame控件的导航也可以与浏览器进行集成。你每次调用Navigate()方法,Silverlight就会添加一个地址到浏览器的历史记录中(如下图)。使用浏览器的前进/后台按钮或者在历史记录列表中选择一个页面都可以正常的访问历史页面。你可以复制地址,然后重新打开浏览器进行访问,依然是可以的。这样Silverlgiht的Appliaction.Startup事件会重新触发,并加载相应的页面到Frame中。
样式模板下载 |