Multiple AppBar/CommandBar's

2019-01-18 17:20发布

问题:

Back in Windows Phone 8, I was able to to use multiple AppBar, swapping them on certain pivot pages but In Windows Phone 8.1, I'm not sure how to do this or is this even possible.

Basically for my scenario, I've got 3 Pivot Pages. Each page needs to have a different CommandBar because it needs to have different controls.

Is someone able to show me how I can do this?

Edit: Code Which I used for Windows Phone 8 to execute this:

XAML:

<phone:PhoneApplicationPage.Resources>
<shell:ApplicationBar x:Key="AppBar1" IsVisible="True" IsMenuEnabled="True">
    <shell:ApplicationBarIconButton IconUri="/Images/appbar_button1.png" Text="Button 1"/>
    <shell:ApplicationBar.MenuItems>
        <shell:ApplicationBarMenuItem Text="MenuItem 1"/>
    </shell:ApplicationBar.MenuItems>
</shell:ApplicationBar>

<shell:ApplicationBar x:Key="AppBar2" IsVisible="True" IsMenuEnabled="True">
    <shell:ApplicationBarIconButton IconUri="/Images/appbar_button1.png" Text="Button 1" />
    <shell:ApplicationBarIconButton IconUri="/Images/appbar_button2.png" Text="Button 2" />
    <shell:ApplicationBar.MenuItems>
        <shell:ApplicationBarMenuItem Text="MenuItem 1" />
        <shell:ApplicationBarMenuItem Text="MenuItem 2" />
    </shell:ApplicationBar.MenuItems>
</shell:ApplicationBar>

C#:

private void MainPivot_SelectionChanged(object sender, SelectionChangedEventArgs e)
    {
        switch (MainPivot.SelectedIndex)
        {
            case 0:
                ApplicationBar = this.Resources["AppBar1"] as ApplicationBar;
                break;
            case 1:
                ApplicationBar = this.Resources["AppBar2"] as ApplicationBar;
                break;
        }
    }

Basically switches the the AppBar when the PivotPage is changed.

回答1:

In WP8.1 RT, you have a property BottomAppBar of your Page. It works pretty much the same (apart it's extended) as old ApplicationBar - you can set it with CommandBar. I've created my command bars in code and it works, you can try like this:

// prepare your CommandBars - run method somewhere in the constructor of the page:
CommandBar firstBar;
CommandBar secondBar;

private void PrepareAppBars()
{
    firstBar = new CommandBar();
    firstBar.IsOpen = true;
    AppBarButton FirstBtn = new AppBarButton() { Icon = new BitmapIcon() { UriSource = new Uri("ms-appx:///Assets/first.png") } };
    FirstBtn.Label = "First";
    FirstBtn.Click += FirstBtn_Click;
    FirstBtn.IsEnabled = true;
    // Similar for second button
    AppBarButton SecondBtn = new AppBarButton() { Icon = new BitmapIcon() { UriSource = new Uri("ms-appx:///Assets/second.png") } };

    firstBar.PrimaryCommands.Add(FirstBtn);
    firstBar.PrimaryCommands.Add(SecondBtn);

    // define also SecondaryCommands

    // simlar secondBar
    secondBar = new CommandBar();
    secondBar.IsOpen = true;
    // ...
}

// then you can surely switch them like this:

private void MainPivot_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
    switch (MainPivot.SelectedIndex)
    {
        case 0:
            BottomAppBar = firstBar ;
            break;
        case 1:
            BottomAppBar = secondBar ;
            break;
    }
}


回答2:

A simple solution is to use XAML to define your buttons and ViewModel (MVVM pattern) to control the visibilities of these buttons, and this can avoid creating buttons in code and complicated logic to control which button to display.

First, define all the buttons might be used in CommandBar:

<Page.BottomAppBar>
    <CommandBar>
        <!--buttons of group1-->
        <AppBarButton Icon="Icon1" Label="button1"/>
        ...
        <!--buttons of group2-->
        <AppBarButton Icon="Icon2" Label="button2"/>
        ...
        <!--buttons of group3-->
        <AppBarButton Icon="Icon3" Label="button3"/>
    </CommandBar>
</Page.BottomAppBar>

Then define a property in the ViewModel, such as:

public class PageViewModel : INotifyPropertyChanged
{
    ...
    public int CommandGroup
    {
        get { return _commandGroup; }
        set { _commandGroup = value; NotifyPropertyChanged("CommandGroup"); }
    }
}

This CommandGroup property is used to control the show/hide of buttons, for example, setting CommandGroup = 1 to show buttons in group1 and hide buttons in other groups, and setting CommandGroup = 2 to show buttons in group2 and hide buttons in other groups, which group1 and group2 here are just logical groups.

Then define a converter to convert the value of CommandGroup property to Visibility:

public class CommandGroupToVisibilityConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, string language) 
    {
        return (System.Convert.ToInt32(value) == System.Convert.ToInt32(parameter)) ? Visibility.Visible : Visibility.Collapsed;
    }
}

And finally, bind this CommandGroup property to all the buttons in CommandBar (copy and paste things):

<Page.Resources>
    <c:CommandGroupToVisibilityConverter x:Key="MyConverter"/>
</Page.Resources>
<Page.BottomAppBar>
    <CommandBar>
        <!--buttons of group1-->
        <AppBarButton 
            Icon="Icon1" Label="button1" 
            Visibility="{Binding CommandGroup, Converter={StaticResource MyConverter}, ConverterParameter=1}"/>

        <!--buttons of group2-->
        <AppBarButton 
            Icon="Icon2" Label="button2" 
            Visibility="{Binding CommandGroup, Converter={StaticResource MyConverter}, ConverterParameter=2}"/>

        <!--buttons of group3-->
        <AppBarButton 
            Icon="Icon3" Label="button3" 
            Visibility="{Binding CommandGroup, Converter={StaticResource MyConverter}, ConverterParameter=3}"/>
    </CommandBar>
</Page.BottomAppBar>

Note that when CommandGroup == 2, then all buttons with ConverterParameter=2 will display and others are gone.

This might be very useful in where there are several views in one page (like a Pivot) and each of them has its different group of command buttons.



回答3:

I ended with creating my base page class (actually I already had I for passing navigation parameter to ViewModel) that extends original Page. In Base Page I added Dependency Property AppBarCollection so that I could use it in my real pages in Xaml. And there I define all necessary AppBars without necessity of creating them in code behind. The only thing I do there is choosing which one to show. Even this can be done from Xaml, but I didn't want to make things more complex. The option is basically the first suggestion, with the difference that you can define all AppBars in you page in xaml.

<views:BindablePage.AppBarCollection>
    <views:AppBarCollection>
        <CommandBar/>
        <CommandBar/>
        <CommandBar/>
    </views:AppBarCollection>
</views:BindablePage.AppBarCollection>