Windows 8 Metro 应用开发入门(三):工具栏和对话框
站长推荐:NSetup一键部署软件
一键式完成美化安装包制作,自动增量升级,数据统计,数字签名。应对各种复杂场景,脚本模块化拆分,常规复杂的脚本代码,图形化设置。无需专业的研发经验,轻松完成项目部署。(www.nsetup.cn)
Metro UI与Windows Phone一样在提供了布局在屏幕下文的应用程序工具栏BottomAppBar,由于平板设备特有的应用,Metro UI还提供了布局在屏幕上方的导航栏TopAppBar。另外,Metro UI还提供了独特的对话框。这一章我们来介绍一下工具栏与导航栏的应用,最后再介绍一下弹出对话框。
应用程序工具栏BottomAppBar默认是隐藏在屏幕的下方,当用手上屏幕上向上滑动或是在屏幕上点击鼠标右键,BottomAppBar会从下方滑出。BottomAppBar和TopAppBar依托于Page类,如下:
| 
					 1 2 3 4 5 6  | 
						    public class Page     {         public AppBar BottomAppBar { get; set; }         public AppBar TopAppBar { get; set; }         //...     }  | 
					
无论是上方的导航栏还是下方的工具栏,都是AppBar类型,我们来看一下AppBar的定义:
| 
					 1 2 3 4 5 6 7 8 9  | 
						    public class AppBar : ContentControl     {         public bool IsOpen { get; set; }         public static DependencyProperty IsOpenProperty { get; }         public bool IsSticky { get; set; }         public static DependencyProperty IsStickyProperty { get; }         public event EventHandler<object> Closed;         public event EventHandler<object> Opened;     }  | 
					
IsOpen 指明工具栏是否可见
IsSticky 如果工具栏可见,指明工具栏是否一直可见,即使失去焦点时
Closed 工具栏完全退出后触发事件
Opened 工具栏完全打开后触发事件
AppBar的定义非常简单,如果要使用它,还得自定义其展示视图,不过添加其子元素很方便。使用BottomAppBar有两种方式:XAML和后台Code。
(1)XAML方式
如下XAML我们向Page添加拥有五个按钮的下方工具栏:
| 
					 1 2 3 4 5 6 7 8 9 10 11 12 13  | 
						    <Page.BottomAppBar>         <AppBar IsSticky="True">             <Grid>                 <StackPanel Orientation="Horizontal">                     <Button x:Name="btn1" Style="{StaticResource AppBarButtonStyle}" AutomationProperties.Name="添加"/>                     <Button x:Name="btn2" Style="{StaticResource EditAppBarButtonStyle}" Click="btn2_Click_1"/>                     <Button x:Name="btn3" Style="{StaticResource SaveAppBarButtonStyle}" Click="btn3_Click_1"/>                     <Button x:Name="btn4" Style="{StaticResource DeleteAppBarButtonStyle}"/>                     <Button x:Name="btn5" Style="{StaticResource DiscardAppBarButtonStyle}"/>                 </StackPanel>             </Grid>         </AppBar>     </Page.BottomAppBar>  | 
					
IsSticky=”True”表明当工具栏打开后让它一直显示在屏幕上,在工具栏内先是放置了一个Grid,其内置一个StackPanel,最后是在内部摆放了5个按钮,并且还为按钮btn1设置了名字AutomationProperties.Name=”添加”,如果要让按钮响应事件,可以为每个按钮注册事件处理程序,就像btn2和btn3一样注册了Click事件。在创建Metro应用程序项目的时候,在Common文件夹下有一个默认的样式StandardStyles.xaml,里面有一系列的工具栏按钮样式,但是被注释掉的,我们可以取消注释,然后在我们的程序中使用它们,大概在StandardStyles.xaml文档的249行开始,有一个基本样式AppBarButtonStyle,其他的如EditAppBarButtonStyle和SaveAppBarButtonStyle等都是基于AppBarButtonStyle进行实现的,上面XAML中用到了AppBarButtonStyle、EditAppBarButtonStyle、SaveAppBarButtonStyle、DeleteAppBarButtonStyle等,我们来看一下上面XAML最终展示的效果:

(2)Code方式使用AppBar
代码创建工具栏只需要实例化AppBar且向其添加子元素即可,最后将AppBar实例给当前页的上/下方工具栏,如下代码:
| 
					 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15  | 
						        private void CreateAppBar()         {             AppBar bottomBar = new AppBar();             StackPanel sp = new StackPanel() { Orientation = Orientation.Horizontal };             Button btn1 = new Button();             btn1.Style = (Style)App.Current.Resources["AddAppBarButtonStyle"];             sp.Children.Add(btn1);             Button btn2 = new Button();             btn2.Style = (Style)App.Current.Resources["EditAppBarButtonStyle"];             sp.Children.Add(btn2);             bottomBar.Content = sp;             this.BottomAppBar = bottomBar;         }  | 
					
TopAppBar与BottomAppBar使用方法相同,无非就是名字不一样,而对于Page来说,TopAppBar在上方,更多的时候称为导航栏,BottomAppBar在下方,称为应用程序工具栏。在这里我们只展示如何用XAML创建一个导航栏TopAppBar:
| 
					 1 2 3 4 5 6 7 8 9 10 11 12 13  | 
						    <Page.TopAppBar>         <AppBar>             <Grid>                 <StackPanel Orientation="Horizontal">                     <Button x:Name="btnTop1" Style="{StaticResource AddAppBarButtonStyle}" AutomationProperties.Name="添加s"/>                     <Button x:Name="btnTop2" Style="{StaticResource EditAppBarButtonStyle}"/>                     <Button x:Name="btnTop3" Style="{StaticResource SaveAppBarButtonStyle}"/>                     <Button x:Name="btnTop4" Style="{StaticResource DeleteAppBarButtonStyle}"/>                     <Button x:Name="btnTop5" Style="{StaticResource DiscardAppBarButtonStyle}"/>                 </StackPanel>             </Grid>         </AppBar>     </Page.TopAppBar>  | 
					
只要注意一点即可,那就是以Page.TopAppBar指明该AppBar为上方的导航栏,其他与BottomAppBar完全一样,效果如下:

这一次我们既指定了按钮的Content,又指定了其文字下标。
以上我们讨论的都是使用Metro应用程序项目默认的模板样式,如果你觉得上面的样式太单调,当然可以自定义按钮样式,比如使用图标,甚至你可以使用动画效果,接下来我们看看如何自定义样式。
在前面的讨论中我们知道AppBar是一个容器控件,并且只能包含一个子元素,所以我们在AppBar内放置一个Grid作为布局父控件,然后再将相应的按钮元素分配到Grid的各个单元格中。如下我们定义了一个背景具有渐变效果的Grid,且放置了三个按钮的下方应用程序工具栏,如图:

这一次我们并没有使用StandardStyles.xaml中的样式,只是简单为按钮放置了一个图标,XAML部分:
| 
					 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28  | 
						<Page.BottomAppBar>         <AppBar IsSticky="True">             <Grid Margin="0" >                 <Grid.Background>                     <LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0">                         <GradientStop Color="#B24FF53E"/>                         <GradientStop Color="#B2F9F6F3" Offset="0.6"/>                         <GradientStop Color="#B2FB8421" Offset="1"/>                     </LinearGradientBrush>                 </Grid.Background>                 <Grid.ColumnDefinitions>                     <ColumnDefinition Width="100"/>                     <ColumnDefinition Width="100"/>                     <ColumnDefinition Width="100"/>                     <ColumnDefinition Width="*"/>                 </Grid.ColumnDefinitions>                 <Button x:Name="btn1" Grid.Column="0">                     <Image Source="Assets/user.png"/>                 </Button>                 <Button x:Name="btn2" Grid.Column="1">                     <Image Source="Assets/Cycle Racer.png"/>                 </Button>                 <Button x:Name="btn3" Grid.Column="2">                     <Image Source="Assets/Add to Cart.png"/>                 </Button>             </Grid>         </AppBar>     </Page.BottomAppBar>  | 
					
这里当然也可以使用StackPanel等其他布局控件,你完全可以按照你自己的要求设计出各种各样的Metro UI 风格的工具栏。
使用工具栏的目的就是要触发一定的动作,所以要想让工具栏里的按钮响应事件,则必须要为每个按钮注册事件处理程序。比如第1节示例中的btn2和btn3:
| 
					 1 2  | 
						<Button x:Name="btn2" Style="{StaticResource EditAppBarButtonStyle}" Click="btn2_Click_1"/> <Button x:Name="btn3" Style="{StaticResource SaveAppBarButtonStyle}" Click="btn3_Click_1"/>  | 
					
当为按钮注册Click事件后,在后台的事件处理程序中就可以执行相应的操作,如下:
| 
					 1 2 3 4 5 6 7 8 9  | 
						        private void btn2_Click_1(object sender, RoutedEventArgs e)         {             //Do Something         }         private void btn3_Click_1(object sender, RoutedEventArgs e)         {             //Do Something         }  | 
					
我们知道Silverlight是基于异步编程模型,同样在Metro App中也是基于异步编程模型,所以对于有耗时计算的,建议在工具栏的按钮处理程序中使用异步编程,这样不影响UI的流畅度,也是微软一直鼓励的做法。当然,在必要的时候或者是你喜欢的时候,也可以使用async/await来实现异步编程的不一样体验,尽管在.NET Framework4.5中增强了对异步的实现,但还是建议使用异步处理。在大家都很忙且有点急促的今天,给用户一个“不用等待”的体现,有什么不好呢?
其实关于应用程序工具栏和导航栏的使用,微软还是建议不要在主视图中使用太多的按钮,尽量把命令按钮放在工具栏中,为MetroUI提供一致的用户体验。
以往的开发中我们常常会弹出一个模态对话框来等待用户的响应,silverlight中是使用MessageBox,但在Metro UI中提供了一个全新的对话框MessageDialog,它是以异步方式弹出,你当然可以使用await来等待用户的响应,它还有一个更炫耀的功能就是可以在一个对话框让指定多个命令!你在以往往的开发中如果想实现类似的功能,是不是得自己实现?MessageDialog只提供了一个弹出方法:
| 
					 1  | 
						public IAsyncOperation<IUICommand> ShowAsync();  | 
					
(1)只有提示消息对话框
使用一个构造函数的MessageDialog可以实例化一个对话框,然后以异步方式打开它,此时它会默认显示一个“关闭”按钮:
| 
					 1 2  | 
						          MessageDialog md = new MessageDialog("保存成功,请注意查收。");             md.ShowAsync();  | 
					
效果图:

(2)带有标题的对话框
| 
					 1  | 
						            MessageDialog md = new MessageDialog("保存成功,请注意查收。", "提示");  | 
					
效果图:

(3)指定自定义命令的对话框
MessageDialog类有一个重要的成员,可以在当前对话框中呈现多个命令按钮:
| 
					 1  | 
						public IList<IUICommand> Commands { get; }  | 
					
可以看到,只要你愿意,你可以向Commands注入多个命令,有意思吧?有一个已经实现了接口IUICommand的类UICommand,这个类就是对命令的处理,它不仅接收一个标签文本,还可以接收一个处理程序的委托UICommandInvokedHandler,UICommand类的构造函数有四个:
| 
					 1 2 3 4  | 
						        public UICommand();         public UICommand(string label);         public UICommand(string label, UICommandInvokedHandler action);         public UICommand(string label, UICommandInvokedHandler action, object commandId);  | 
					
来看一下如何注册命令的处理程序:
| 
					 1 2 3 4 5 6 7 8 9 10  | 
						        MessageDialog md = new MessageDialog("确定要提交当前数据吗?", "询问");             md.Commands.Add(new UICommand("确定", cmd =>             {                 Debug.WriteLine("确定");             }));             md.Commands.Add(new UICommand("放弃", cmd =>              {                 Debug.WriteLine("放弃");             }));             md.ShowAsync();  | 
					
效果图:

如果你觉得上面的处理还不过瘾,请看下面。
(4)使用具有命令Id的命令
细心的你一定能发现上面UICommand的最后一个构造函数:
| 
					 1  | 
						public UICommand(string label, UICommandInvokedHandler action, object commandId);  | 
					
最后一个参数可以指定命令的Id,也就是说,在下文中我们可以根据这个Id来进行不同的操作,这个object类型的Id允许你给它任意类型的数据。下面的代码我们取消了注册命令处理程序,而是为指定了命令Id:
| 
					 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22  | 
						                        MessageDialog md = new MessageDialog("确定要提交当前数据吗?", "询问");             md.Commands.Add(new UICommand("确定", null, 0));             md.Commands.Add(new UICommand("放弃", null, 1));             md.Commands.Add(new UICommand("帮组", null, 2));             md.DefaultCommandIndex = 0;             md.CancelCommandIndex = 1;             var flg = await md.ShowAsync();             //var flg = md.ShowAsync();             switch (flg.Id)             {                 case 0:                     //Do Something                     break;                 case 1:                     //Do Something                     break;                 case 2:                     //Go to Help                     break;                 default:                     break;             }  | 
					
效果图:

在前面我们看到MessageDialog是以异步方式打开,所以我们可以根据需要获取ShowAsync()的响应结果,根据命令Id执行进一步的操作。使用DefaultCommandIndex指定当我们按下Enter键时响应的按钮,CancelCommandIndex指定当按下Esc键时应的按钮。
很遗憾的是MessageDialoge不能定义对话框的样式, 如何想创建更个性的对话框,可以使用Popup 来模拟对话框,关于Popup这里就不再介绍了,感兴趣的可以去查找相关资料。
http://www.cnblogs.com/solan/archive/2012/09/18/Win8Metro03.html
学习日记,兼职软件设计,软件修改,毕业设计。
本文出自 学习日记,转载时请注明出处及相应链接。
本文永久链接: https://www.softwareace.cn/?p=763
