知识点
1.了解和使用Windows 8应用中的导航;
2.利用导航和一些常用控件实现一个文本阅读器;
一、Windows 8中的导航
Windows 8中的导航主要通过Frame中的Navigate方法进行,Navigate的原型如下:
- public bool Navigate(Type sourcePageType);
- public bool Navigate(Type sourcePageType, object parameter);
可以看出,在页面导航的过程中可以传递object类型的参数,这使得页面之间的数据传递非常灵活,但是Windows 8的挂起恢复机制要求传递的参数能够可序列化,因此在进行页面间的数据传递时,对参数的类型和组织方式也要有所考虑。
二、在Windows 8应用中使用导航
为了简单起见,我们设计了如下功能的TXT文本阅读器:
1.在首页中打开一个文件选择器,选择一个TXT文件;
2.在正文页显示TXT文章的标题和内容;
创建工程
打开Visual Studio,选择文件〉新建项目,在新建对话框中选择Visual C#模版,建立一个空的应用Blank App(XAML),名称可以自己选择,点击确定即可完成项目的创建。
视图设计
主页仅仅放置了一个按钮,布局比较简单,代码如下:
<Page x:Class="TextReader.MainPage" IsTabStop="false" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="using:TextReader" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d"> <Grid Background="{StaticResource ApplicationPageBackgroundThemeBrush}"> <Button Content="打开" Width="120" HorizontalAlignment="Center" VerticalAlignment="Center" Click="OnOpen"/> </Grid> </Page>
在正文页我们选择创建一个基本页,Visual Studio会为我们生成一个带后退按钮和标题的页面,我在这个基础上进行改造,加入一个装在ScrollViewer容器中的TextBlock来显示文章内容,同时采用数据绑定将标题和内容绑定至不同的数据,代码如下:
<common:LayoutAwarePage x:Name="pageRoot" x:Class="TextReader.ArticlePage" DataContext="{Binding DefaultViewModel, RelativeSource={RelativeSource Self}}" IsTabStop="false" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="using:TextReader" xmlns:common="using:TextReader.Common" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d"> <Page.Resources> <!-- TODO: Delete this line if the key AppName is declared in App.xaml --> <x:String x:Key="AppName">My Application</x:String> </Page.Resources> <!-- This grid acts as a root panel for the page that defines two rows: * Row 0 contains the back button and page title * Row 1 contains the rest of the page layout --> <Grid Style="{StaticResource LayoutRootStyle}"> <Grid.RowDefinitions> <RowDefinition Height="140"/> <RowDefinition Height="*"/> </Grid.RowDefinitions> <!-- Back button and page title --> <Grid> <Grid.ColumnDefinitions> <ColumnDefinition Width="Auto"/> <ColumnDefinition Width="*"/> </Grid.ColumnDefinitions> <Button x:Name="backButton" Click="GoBack" IsEnabled="{Binding Frame.CanGoBack, ElementName=pageRoot}" Style="{StaticResource BackButtonStyle}"/> <TextBlock x:Name="pageTitle" Grid.Column="1" Text="{Binding ArticleTitle}" Style="{StaticResource PageHeaderTextStyle}"/> </Grid> <ScrollViewer Grid.Row="1" > <TextBlock Text="{Binding ArticleContent}" FontSize="24" Margin="20" TextWrapping="Wrap"> </TextBlock> </ScrollViewer> <VisualStateManager.VisualStateGroups> <!-- Visual states reflect the application's view state --> <VisualStateGroup x:Name="ApplicationViewStates"> <VisualState x:Name="FullScreenLandscape"/> <VisualState x:Name="Filled"/> <!-- The entire page respects the narrower 100-pixel margin convention for portrait --> <VisualState x:Name="FullScreenPortrait"> <Storyboard> <ObjectAnimationUsingKeyFrames Storyboard.TargetName="backButton" Storyboard.TargetProperty="Style"> <DiscreteObjectKeyFrame KeyTime="0" Value="{StaticResource PortraitBackButtonStyle}"/> </ObjectAnimationUsingKeyFrames> </Storyboard> </VisualState> <!-- The back button and title have different styles when snapped --> <VisualState x:Name="Snapped"> <Storyboard> <ObjectAnimationUsingKeyFrames Storyboard.TargetName="backButton" Storyboard.TargetProperty="Style"> <DiscreteObjectKeyFrame KeyTime="0" Value="{StaticResource SnappedBackButtonStyle}"/> </ObjectAnimationUsingKeyFrames> <ObjectAnimationUsingKeyFrames Storyboard.TargetName="pageTitle" Storyboard.TargetProperty="Style"> <DiscreteObjectKeyFrame KeyTime="0" Value="{StaticResource SnappedPageHeaderTextStyle}"/> </ObjectAnimationUsingKeyFrames> </Storyboard> </VisualState> </VisualStateGroup> </VisualStateManager.VisualStateGroups> </Grid> </common:LayoutAwarePage>
选择一个TXT文件并导航至文章正文页
为首页中的按钮添加一个Click事件,在事件处理函数中调用FileOpenPicker来选择文件,其作用与文件选择对话框相当。选择完文件之后,我们将页面导航至文章正文页,并向其传递文章标题和内容等数据。代码如下,其中高亮显示的部分为导航操作。
private async void OnOpen(object sender, RoutedEventArgs e) { var picker = new FileOpenPicker(); picker.FileTypeFilter.Add(".txt"); picker.CommitButtonText = "打开"; picker.SettingsIdentifier = "选择一个文件"; picker.ViewMode = PickerViewMode.List; picker.SuggestedStartLocation = PickerLocationId.Desktop; var file = await picker.PickSingleFileAsync(); if (file != null) { Stream stream = await file.OpenStreamForReadAsync(); if (stream.CanSeek) stream.Seek(0, SeekOrigin.Begin); if (stream.CanRead) { long length = stream.Length; byte[] buffer = new byte[length]; stream.Read(buffer,0,(int)length); List<string> bundle = new List<string>(); bundle.Add(file.DisplayName); bundle.Add(Encoding.GetEncoding("gb2312").GetString(buffer,0,(int)length)); this.Frame.Navigate(typeof(ArticlePage), bundle); } } }
数据绑定
为了有效复习数据绑定的知识,我们在文章正文页的数据呈现上也采用了数据绑定,设计了一个ArticleViewModel用呈现文章的标题和内容,这个类的设计如下:
class ArticleViewModel : INotifyPropertyChanged { public event PropertyChangedEventHandler PropertyChanged; private string articleTitle; public string ArticleTitle { get { return articleTitle; } set { if (articleTitle != value) { articleTitle = value; NotifyPropertyChanged("ArticleTile"); } } } private string articleContent; public string ArticleContent { get { return articleContent; } set { if (articleContent != value) { articleContent = value; NotifyPropertyChanged("ArticleContent"); } } } public ArticleViewModel(string tile, string content) { this.articleTitle = tile; this.articleContent = content; } private void NotifyPropertyChanged(string propertyName) { if (null != PropertyChanged) PropertyChanged(this, new PropertyChangedEventArgs(propertyName)); } }
为了实现数据绑定,我们需要设置UI元素的DataContext属性来关联ViewModel,这里我们在文章正文页的OnNavigateTo方法中接收首页传递的文章标题和文章内容数据,构造一个ArticleViewModel实例,并将DataContext设置为该实例,代码如下:
protected override void OnNavigatedTo(NavigationEventArgs e) { var parameter = (List<string>)e.Parameter; articleViewModel = new ArticleViewModel(parameter[0], parameter[1]); this.DataContext = articleViewModel; }
另外需要注意的是,由于文章正文页采用了基本页(LayoutAwarePage)作为了模板,系统为我们自动生成了应用挂起时的一些状态保存工作,而这个例子我们并不涉及挂起时的状态保存操作,为了避免调用这些方法,我们将OnNavigateFrom方法重载为空的方法,如下:
protected override void OnNavigatedFrom(NavigationEventArgs e) { }
最终的运行效果如下图所示:
应用运行效果图
(原创文章,转载请注明作者schbook:seekerxu@163.com)