Windows 8 Metro 应用开发入门(三):工具栏和对话框

摘 要

Metro UI与Windows Phone一样在提供了布局在屏幕下文的应用程序工具栏BottomAppBar,由于平板设备特有的应用,Metro UI还提供了布局在屏幕上方的导航栏TopAppBar。另外,Metro UI还提供了独特的对话框。这一章我们来介绍一下工具栏与导航栏的应用,最后再介绍一下弹出对话框。

第1节 BottomAppBar

应用程序工具栏BottomAppBar默认是隐藏在屏幕的下方,当用手上屏幕上向上滑动或是在屏幕上点击鼠标右键,BottomAppBar会从下方滑出。BottomAppBar和TopAppBar依托于Page类,如下:

    public class Page
    {
        public AppBar BottomAppBar { get; set; }
        public AppBar TopAppBar { get; set; }
        //...
    }

无论是上方的导航栏还是下方的工具栏,都是AppBar类型,我们来看一下AppBar的定义:

    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添加拥有五个按钮的下方工具栏:

    <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实例给当前页的上/下方工具栏,如下代码:

        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;
        }

 

第2节 TopAppBar

TopAppBar与BottomAppBar使用方法相同,无非就是名字不一样,而对于Page来说,TopAppBar在上方,更多的时候称为导航栏,BottomAppBar在下方,称为应用程序工具栏。在这里我们只展示如何用XAML创建一个导航栏TopAppBar:

    <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,又指定了其文字下标。

 

第3节 自定义AppBar

以上我们讨论的都是使用Metro应用程序项目默认的模板样式,如果你觉得上面的样式太单调,当然可以自定义按钮样式,比如使用图标,甚至你可以使用动画效果,接下来我们看看如何自定义样式。

在前面的讨论中我们知道AppBar是一个容器控件,并且只能包含一个子元素,所以我们在AppBar内放置一个Grid作为布局父控件,然后再将相应的按钮元素分配到Grid的各个单元格中。如下我们定义了一个背景具有渐变效果的Grid,且放置了三个按钮的下方应用程序工具栏,如图:

这一次我们并没有使用StandardStyles.xaml中的样式,只是简单为按钮放置了一个图标,XAML部分:

<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 风格的工具栏。

 

第4节 使用工具栏的事件

使用工具栏的目的就是要触发一定的动作,所以要想让工具栏里的按钮响应事件,则必须要为每个按钮注册事件处理程序。比如第1节示例中的btn2和btn3:

<Button x:Name="btn2" Style="{StaticResource EditAppBarButtonStyle}" Click="btn2_Click_1"/>
<Button x:Name="btn3" Style="{StaticResource SaveAppBarButtonStyle}" Click="btn3_Click_1"/>

当为按钮注册Click事件后,在后台的事件处理程序中就可以执行相应的操作,如下:

        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提供一致的用户体验。

 

第5节 弹出对话框

以往的开发中我们常常会弹出一个模态对话框来等待用户的响应,silverlight中是使用MessageBox,但在Metro UI中提供了一个全新的对话框MessageDialog,它是以异步方式弹出,你当然可以使用await来等待用户的响应,它还有一个更炫耀的功能就是可以在一个对话框让指定多个命令!你在以往往的开发中如果想实现类似的功能,是不是得自己实现?MessageDialog只提供了一个弹出方法:

public IAsyncOperation<IUICommand> ShowAsync();

(1)只有提示消息对话框

使用一个构造函数的MessageDialog可以实例化一个对话框,然后以异步方式打开它,此时它会默认显示一个“关闭”按钮:

          MessageDialog md = new MessageDialog("保存成功,请注意查收。");
            md.ShowAsync();

效果图:

(2)带有标题的对话框

            MessageDialog md = new MessageDialog("保存成功,请注意查收。", "提示");

效果图:

(3)指定自定义命令的对话框

MessageDialog类有一个重要的成员,可以在当前对话框中呈现多个命令按钮:

public IList<IUICommand> Commands { get; }   

可以看到,只要你愿意,你可以向Commands注入多个命令,有意思吧?有一个已经实现了接口IUICommand的类UICommand,这个类就是对命令的处理,它不仅接收一个标签文本,还可以接收一个处理程序的委托UICommandInvokedHandler,UICommand类的构造函数有四个:

        public UICommand();
        public UICommand(string label);
        public UICommand(string label, UICommandInvokedHandler action);
        public UICommand(string label, UICommandInvokedHandler action, object commandId);

来看一下如何注册命令的处理程序:

        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的最后一个构造函数:

public UICommand(string label, UICommandInvokedHandler action, object commandId);

最后一个参数可以指定命令的Id,也就是说,在下文中我们可以根据这个Id来进行不同的操作,这个object类型的Id允许你给它任意类型的数据。下面的代码我们取消了注册命令处理程序,而是为指定了命令Id:

                        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这里就不再介绍了,感兴趣的可以去查找相关资料。

 

小 结
posted @ 2012-09-18 08:29  solan3000  阅读(5276)  评论(10编辑  收藏  举报