您的位置:首页 > 其它

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

2012-10-25 13:05 591 查看



摘 要

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

小 结

本博客文章版权归博客园和solan3000共同所有。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: