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這里就不再介紹了,感興趣的可以去查找相關資料。

 

小 結


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM