天天看点

UWP中用SplitView和Pivot实现两级目录

两级目录类似以下情况

  • 2015
    • 2015.1
      • 内容1
      • 内容2
      • 内容3
      • 内容x
  • 12
    • 12.1
      • 内容1
      • 内容2
      • 内容3
    • 12.2
      • 内容1
      • 内容2
  • 24
    • 24.1
      • 内容1
      • 内容2
    • 24.2
      • 内容1
      • 内容2
      • 内容3
    • 24.3
      • 内容1
      • 内容2

SplitView控件:

有关SplitView的MS文档:SplitView class

UWP中用SplitView和Pivot实现两级目录

由图可以看出SplitView控件由Pane和Content组成,所以Pane可以用来存放一级目录,而Content可以用来存放二级目录以及二级目录下的内容。

SplitView控件有几个重要的属性:

Property Access type Description
DisplayMode Read/write Gets of sets a value that specifies how the pane and content areas of a SplitView are shown.
OpenPaneLength Read/write Gets or sets the width of the SplitView pane when it’s fully expanded.
IsPaneOpen Read/write Gets or sets a value that specifies whether the SplitView pane is expanded to its full width.

使用SplitView可以实现UWP上新加入的汉堡包菜单

UWP中用SplitView和Pivot实现两级目录

初始界面

UWP中用SplitView和Pivot实现两级目录

点击右下角菜单按钮后

SplitView代码

<SplitView x:Name="spViewRoot"
                   DisplayMode="Overlay"
                   OpenPaneLength="256"
                   IsPaneOpen="{Binding MenuOpened, Mode=TwoWay}"
                   IsTabStop="False">
            <SplitView.Pane>
                <Grid>

                </Grid>
            </SplitView.Pane>
            <SplitView.Content>
                <Grid>

                </Grid> 
            </SplitView.Content>
        </SplitView>
           

Splitview的IsPaneOpen决定了Pane是否展开他绑定了一个后台数据:MenuOpened.

private bool menuOpened;
        public bool MenuOpened
        {
            get
            {
                return menuOpened;
            }
            set
            {
                menuOpened = value;
                OnPropertyChanged("MenuOpened");
            }
        }
           

配合的BottomAppBar代码

<Page.BottomAppBar>
        <CommandBar x:Name="btmCmdBar">
            <AppBarButton Label="Menu"
                          Click="AppBarButton_Click">
                <AppBarButton.Icon>
                    <FontIcon Glyph="&#xE71D;"/>
                </AppBarButton.Icon>
            </AppBarButton>
        </CommandBar>
    </Page.BottomAppBar>
           

AppBarButton的Click事件里要做的就是对SplitView的IsPaneOpen取反。这样,每次点击菜单按钮就可以让菜单打开或关闭。

接下来要做的就是向SplitView中添加一级目录

一级目录就相当于是一个列表,所以只需要在SplitView的Pane中添加一个ListView即可,当然为了美观我稍微的设计了一下布局
           
<Grid>
    <Grid.RowDefinitions>
        <RowDefinition Height="48"/>
        <RowDefinition Height="Auto"/>
    </Grid.RowDefinitions>
    <Grid.ColumnDefinitions>
        <ColumnDefinition Width="48"/>
        <ColumnDefinition/>
    </Grid.ColumnDefinitions>
    <ListView Grid.Row="1"
              Grid.ColumnSpan="2"
              ItemsSource="{Binding NavList}"
              ItemTemplate="{StaticResource MenuListItemTemplate}">
    </ListView>
</Grid>
           

ListView中的元素布局模板全用数据绑定实现:

<Page.Resources>
    <DataTemplate x:Key="MenuListItemTemplate"
                  x:DataType="model:NavItem">
        <Grid>
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="48"/>
                <ColumnDefinition/>
            </Grid.ColumnDefinitions>
            <FontIcon x:Name="Glyph" FontSize="16" Glyph="{x:Bind SymbolAsChar}" VerticalAlignment="Center" HorizontalAlignment="Center" ToolTipService.ToolTip="{x:Bind Label}"/>
            <TextBlock x:Name="Text" Grid.Column="1" Text="{x:Bind Label}" />
        </Grid>
    </DataTemplate>
    <vm:MainPageViewModel x:Key="viewModel"/>
</Page.Resources>
           

每个菜单元素的Model:

public class NavItem
{

    public enum NavType
    {
        one,
        two,
        three,
    }
    public string Label { get; set; }
    //用于表示目录名称前的小图标
    public Symbol Symbol { get; set; }
    public char SymbolAsChar
    {
        get
        {
            return (char)this.Symbol;
        }
    }
    //用来标识目录是哪一项
    public NavType DestPage { get; set; }
    public object Arguments { get; set; }
}
           

ViewModel代码如下:

//用于前台一级目录中的 ListView 数据绑定
private List<NavItem> navList;
public List<NavItem> NavList
{
    get
    {
        return navList;
    }
    set
    {
        navList = value;
    }
}

//在 ViewModel 的构造函数中初始化 ListView 的数据
public MainPageViewModel()
{
    this.NavList = new List<NavItem>(
    new[]
    {
        new NavItem()
        {
            Symbol = Symbol.Favorite,
            Label = "2015",
            DestPage = NavItem.NavType.one,
        },
        new NavItem()
        {
            Symbol = Symbol.Favorite,
            Label = "12",
            DestPage = NavItem.NavType.two,
        },
        new NavItem()
        {
            Symbol = Symbol.Favorite,
            Label = "24",
            DestPage = NavItem.NavType.three,
        },
    });
}
           

此时运行程序就得到了我想要的效果:

UWP中用SplitView和Pivot实现两级目录

更多关于SplitView和汉堡包菜单的实现请看MS官方提供的例子:

Windows-universal-samples / Samples / XamlNavigation /

GitHub

接下来就是二级目录和二级目录下内容的呈现了。

Pivot控件

有关Pivot的MS文档:Pivot class

Pivot控件有两个总要的部分:Title和Items。

Title就是显示在Pivot顶部的标题;Items就是Pivot中的内容,也相当于是一个列表。

而每个Pivot的Item又由两部分组成:Header和Item,所以我从内到外设计模板。

使用模板和数据绑定的好处就是可以动态加载Pivot,这样我们不用把Pivot的数量定死,而是根据一级目录下的元素个数来添加Pivot。

每个PivotItem的模板:

每个PivotItem中用一个ListView来展示内容

<DataTemplate x:Key="PivotListTemplate">
    <ListView ItemTemplate="{StaticResource PivotListItemTemplate}"
              ItemsSource="{Binding ContentList}"/>
</DataTemplate>
           
<DataTemplate x:Key="PivotListItemTemplate">
    <TextBlock Text="{Binding Content}"/>
</DataTemplate>
           

他只有一个TextBlock用来显示每一个元素中的内容。

每个PivotItem的header模板:

<DataTemplate x:Key="PivotHeaderTemplate">
    <TextBlock Text="{Binding Header}"/>
</DataTemplate>
           

他只有一个TextBlock用来显示每一个元素Header中的内容。

Pivot代码

<Pivot ItemTemplate="{StaticResource PivotListTemplate}"
       HeaderTemplate="{StaticResource PiovtHeaderTemplate}"
       ItemsSource="{Binding PivotList}"
       Title="{Binding PivotTitle}">
</Pivot>
           

整个数据绑定的结构:

UWP中用SplitView和Pivot实现两级目录

此时运行结果:

UWP中用SplitView和Pivot实现两级目录

接下来要实现的就是通过SplitView的Pane中菜单点击不同的一级目录,然后再SplitView的Content中显示拥有不同数量PivotItem的Pivot。

为了达到以上效果,我们需要改进一下SplitView的Pane中的ListView。

首先需要让ListView实现元素点击事件,由于ListView没有Command,所以我们需要用微软提供的一个方法来实现。

首先添加引用

xmlns:interactivity="using:Microsoft.Xaml.Interactivity"
xmlns:core="using:Microsoft.Xaml.Interactions.Core"
           

然后在ListView中添加如下代码:

<interactivity:Interaction.Behaviors>
    <core:EventTriggerBehavior EventName="SelectionChanged">
        <core:InvokeCommandAction Command="{Binding NavCommand}" CommandParameter="{Binding ElementName=lvNavMenu, Path=SelectedItem}"/>
    </core:EventTriggerBehavior>
</interactivity:Interaction.Behaviors>
           

这样我们就将后台的NavCommand绑定到了ListView的SelectionChanged事件。

后台ViewModel中NavCommand代码:

private DelegateCommand navCommand;
public DelegateCommand NavCommand
{
    get
    {
        return navCommand ?? (navCommand = new DelegateCommand(
            (Object obj) =>
            {
                NavItem selectedItem = obj as NavItem;
                if (selectedItem != null)
                {
                    this.PivotTitle = selectedItem.Label;
                    //判断当前选择的是哪一个一级目录,根据不同的选择加载不同的PivotList
                    switch (selectedItem.DestPage)
                    {
                        case NavItem.NavType.one:
                            this.PivotList = new List<MyPivotItem>() { new MyPivotItem() };
                            break;
                        case NavItem.NavType.two:
                            this.PivotList = new List<MyPivotItem>() { new MyPivotItem(), new MyPivotItem() };
                            break;
                        case NavItem.NavType.three:
                            this.PivotList = new List<MyPivotItem>() { new MyPivotItem(), new MyPivotItem(), new MyPivotItem() };
                            break;
                    }
                    this.MenuOpened = false;
                }
            },
            (Object obj) => true));
    }
}
           

结果如下所示

UWP中用SplitView和Pivot实现两级目录
UWP中用SplitView和Pivot实现两级目录
UWP中用SplitView和Pivot实现两级目录

更多有关Pivot 的内容请看微软官方的GitHub例子:

Windows-universal-samples / Samples / XamlPivot /

GitHub

本人所用Demo源码:GitHub