Windows 8 Metro 应用开发入门(二):Metro App的几个新控件

摘 要

基于Silverlight开发Metro App可以使用Silverlight原生的控件,为了更好的开发Metro App,控件库又专门增加了几个新的控件,如:GridView、Flipview、ProgressRing、SemanticZoom和VariableSizedWrapGrid等,这些控件为平板设备提供了良好的触控体验,这一章我们来介绍一下这几个控件的简单用法。

第1节 GridView

GridView是以网格样式展现一组数据,在Web开发中就已经存在,主要用于多行多列的数据显示中。Metro中以分组形式表达数据使用GridView更得心应手,在Silverlight 5及以前版本中,如果想实现分组数据展现要先写一堆数据模板,而在Metro中,这种展现方式得到了增强,GridView 是专门用于分组数据的,如果仅仅是列表数据,则建议使用ListView。

GridView控件有几个非常重要的常用成员:

(1) 数据源

如果是以分组形式展示数据,则数据源必须是可分组的,也即是有主从关系,通常是主列表项中每一项可能存在着某一子列表。GridView的数据源依托于CollectionViewSource,CollectionViewSource有一个属性IsSourceGrouped指明数据源是否是分组源数据集合,还有一个属性ItemsPath指明查找子数据项的路径,通常将可分组的源数据指定给CollectionViewSource对象的Source属性,如下定义了年级与学生的主从关系,年级包含了学生的数据列表:

    public class Grade
    {
        public Grade()
        {
            Students = new ObservableCollection<Student>();
        }
        public string Name { get; set; }
        public ObservableCollection<Student> Students { get; private set; }
    }

    public class Student
    {
        public string Name { get; set; }
        public DateTime Birthday { get; set; }
        public bool IsClassLeader { get; set; }
        public string Grade { get; set; }
    }

以下代码来模拟生成三个年级的学生:

        private List<Grade> CreateGrades()
        {
            List<Grade> Grades = new List<Grade>();
            for (int i = 1; i <= 3; i++)
            {
                Grade grade = new Grade();
                grade.Name = "年级 " + i;
                for (int j = 1; j <= 5; j++)
                {
                    grade.Students.Add(new Student()
                    {
                        Name = "王学生 " + j,
                        IsClassLeader = true,
                        Birthday = startDate.AddDays(j + 1),
                        Grade = grade.Name
                    });
                }
                Grades.Add(grade);
            }
            //MyGrades.Source = Grades;
            return Grades;
        }

那么,对于这个方法生成的数据源即是可分组的。对于CollectionViewSource,既可以使用静态资源的形式绑定到视图,也可以以后台代码的形式绑定。

(2)使用GridView展示数据源

a)、项样式

如果要展示个性化的数据视图,需要自定义DataTemplate来规划每组下的每个数据项的布局,如下是对每个学生信息的展示布局:

            <GridView.ItemTemplate>
                <DataTemplate>
                    <StackPanel Margin="20">
                        <TextBlock Text="{Binding Name}" FontWeight="Bold" Style="{StaticResource ItemTextStyle}"/>
                        <TextBlock Text="{Binding Birthday}" TextWrapping="NoWrap" Style="{StaticResource BodyTextStyle}" />
                        <CheckBox Content="班干部" IsChecked="{Binding IsClassLeader}" IsEnabled="False"/>
                    </StackPanel>
                </DataTemplate>
            </GridView.ItemTemplate>

b)、组样式

也可以对每个分组的展示布局及样式自定义,通过GroupStyle进行设定,如下是对每个年级分组的样式定义:

            <GridView.GroupStyle>
                <GroupStyle HidesIfEmpty="False">
                    <GroupStyle.HeaderTemplate>
                        <DataTemplate>
                            <Grid Background="Yellow" Margin="0">
                                <TextBlock Text="{Binding Name}" Foreground="Black" Margin="30" Style="{StaticResource HeaderTextStyle}"/>
                            </Grid>
                        </DataTemplate>
                    </GroupStyle.HeaderTemplate>
                    <GroupStyle.ContainerStyle>
                        <Style TargetType="GroupItem">
                            <Setter Property="MinWidth" Value="300"/>
                            <Setter Property="BorderBrush" Value="Blue"/>
                            <Setter Property="BorderThickness" Value="2"/>
                            <Setter Property="Margin" Value="3,0"/>
                            <Setter Property="Background" Value="Silver"/>
                        </Style>
                    </GroupStyle.ContainerStyle>
                </GroupStyle>
            </GridView.GroupStyle>

HidesIfEmpty属性指定当子项为空时是否隐藏组。

HeaderTemplate 定义组头样式。

效果图:

c)、选定项事件

当选定项改变时触发事件SelectionChanged,与Listview、DropDownlist等控件的效果差不多,在事件处理程序中可以做相应处理,如下:

        private void GridView_SelectionChanged_1(object sender, SelectionChangedEventArgs e)
        {
            Student student = ((GridView)sender).SelectedItem as Student;
            Debug.WriteLine(student.Name);
        }

以下是完整的示例代码:

XAML:

View Code
    <Page.Resources>
        <CollectionViewSource x:Name="MyStudents" IsSourceGrouped="True"/>
    </Page.Resources>
<GridView MaxHeight="500" Margin="0,120,0,0" Grid.Column="1"  ItemsSource="{Binding Source={StaticResource MyGrades}}" SelectionChanged="GridView_SelectionChanged_1" >
            <GridView.ItemTemplate>
                <DataTemplate>
                    <StackPanel Margin="20">
                        <TextBlock Text="{Binding Name}" FontWeight="Bold" Style="{StaticResource ItemTextStyle}" Foreground="Purple"/>
                        <TextBlock Text="{Binding Birthday}" TextWrapping="NoWrap" Style="{StaticResource BodyTextStyle}" />
                        <CheckBox Content="班干部" IsChecked="{Binding IsClassLeader}" IsEnabled="False"/>
                    </StackPanel>
                </DataTemplate>
            </GridView.ItemTemplate>
            <GridView.ItemsPanel>
                <ItemsPanelTemplate>
                    <StackPanel Orientation="Horizontal"/>
                </ItemsPanelTemplate>
            </GridView.ItemsPanel>

            <GridView.GroupStyle>
                <GroupStyle HidesIfEmpty="False">
                    <GroupStyle.HeaderTemplate>
                        <DataTemplate>
                            <Grid Background="Yellow" Margin="0">
                                <TextBlock Text="{Binding Name}" Foreground="Black" Margin="30" Style="{StaticResource HeaderTextStyle}"/>
                            </Grid>
                        </DataTemplate>
                    </GroupStyle.HeaderTemplate>
                    <GroupStyle.ContainerStyle>
                        <Style TargetType="GroupItem">
                            <Setter Property="MinWidth" Value="300"/>
                            <Setter Property="BorderBrush" Value="Blue"/>
                            <Setter Property="BorderThickness" Value="2"/>
                            <Setter Property="Margin" Value="3,0"/>
                            <Setter Property="Background" Value="Silver"/>
                        </Style>
                    </GroupStyle.ContainerStyle>

                    <GroupStyle.Panel>
                        <ItemsPanelTemplate>
                            <VariableSizedWrapGrid/>
                        </ItemsPanelTemplate>
                    </GroupStyle.Panel>
                </GroupStyle>
            </GridView.GroupStyle>
        </GridView>

C# 绑定数据:

        private List<Grade> CreateGrades()
        {
            List<Grade> Grades = new List<Grade>();
            for (int i = 1; i <= 3; i++)
            {
                Grade grade = new Grade();
                grade.Name = "年级 " + i;
                for (int j = 1; j <= 5; j++)
                {
                    grade.Students.Add(new Student()
                    {
                        Name = "王学生 " + j,
                        IsClassLeader = true,
                        Birthday = startDate.AddDays(j + 1),
                        Grade = grade.Name
                    });
                }
                Grades.Add(grade);
            }
            MyGrades.Source = Grades;
            return Grades;
        }

在基于横向排列分组时建议使用GridView,在基于纵向排列分组时建议使用ListView,关于ListView的使用请参考相关文档,这里不再介绍。下面给一个ListView的分组效果图:

 

第2节 Flipview

Flipview允许基于“上一页”“下一页”的方式浏览一系列视图,视图可以是图片,也可以是其他的自定义布局。现在很多网站上都有对图片的类似浏览方式,如网易的图片新闻,上一页的导航在整个视图的左侧,下一页的导航在右侧,这种导航非常适合在触摸设备中使用。如下:

Flipview控件来源于ItemsControl,它的使用比较简单,类似于一个容器,只需要往里塞视图即可,至于如何处理上下页的翻转,内部都已经完全实现好了,不仅可以往里面旋转图片,还可以放置其他的视图。如下:

                <FlipView HorizontalAlignment="Left" SelectionChanged="FlipView_SelectionChanged_1"  Margin="12"
 VerticalAlignment="Top" Grid.Row="1" Width="500">
            <Image Source="Assets/bg0.jpg" />
            <Image Source="Assets/bg1.jpg" />
            <Image Source="Assets/bg2.jpg" />
            <Image Source="Assets/bg3.jpg" />
            <Image Source="Assets/bg4.jpg" />
            <Grid>
                <TextBlock Text="12312313" FontSize="30"/>
            </Grid>
        </FlipView>

Image可以与Grid共存。Flipview同样有SelectionChanged事件,在事件处理程序中可以获取导航到当前的视图项。

 

第3节 ProgressRing

在Windows 8 中有一个新的进度条,就是几个点围绕一个圆旋转(当用户登录到Windows8系统时可以看到)。Metro应用程序也提供了这一效果的控件。如下:

它的使用非常的简单:

<ProgressRing x:Name="pr" Grid.Row="1" HorizontalAlignment="Left" Margin="500,231,0,0" 
VerticalAlignment="Top" Height="108" Width="108" IsActive="True"/>

它有一个很重要的属性IsActive来控制它的活动与休眠。

 

第4节 SemanticZoom

SemanticZoom控件提供了同一个数据源两种视图的展现形式,这是一个很有意思的控件,也可以说一个是近景视图ZoomedInView一个是远景视图ZoomedOutView,我们暂且称它人近景、远景吧。在近景视图中可以浏览每个项的详细信息,远景视图类似预览图,一般是浏览项的简略数据,如下分别是视图ZoomedInView和视图ZoomedOutView:

注意看视图ZoomedInView的右下角有个红圈围起来的一个“一”字按钮,点它可以切换到远景视图,当点击远景视图中的一个项时又切换回近景视图。这通常在快速预览数据列表时很有用,可以向用户隐藏一些不必要的数据。

SemanticZoom有两个非常重要的属性ZoomedInView和ZoomedOutView,当向这两个视图提供布局时该控件才可进行近远景视图的切换。如下:

<SemanticZoom HorizontalAlignment="Left" Margin="10" Grid.Row="1" VerticalAlignment="Top">
            <SemanticZoom.ZoomedInView>
                <GridView x:Name="gv1" ScrollViewer.IsHorizontalScrollChainingEnabled="False" ScrollViewer.IsVerticalScrollChainingEnabled="False">
                    <GridView.ItemTemplate>
                        <DataTemplate>
                            <StackPanel Margin="20">
                                <TextBlock Text="{Binding Name}" FontWeight="Bold" Style="{StaticResource ItemTextStyle}"/>
                                <TextBlock Text="{Binding DueDate}" TextWrapping="NoWrap" Style="{StaticResource BodyTextStyle}" />
                                <CheckBox Content="Complete" IsChecked="{Binding Complete}" IsEnabled="False"/>
                            </StackPanel>
                        </DataTemplate>
                    </GridView.ItemTemplate>
                </GridView>
            </SemanticZoom.ZoomedInView>
            <SemanticZoom.ZoomedOutView>
                <GridView x:Name="gv2" ScrollViewer.IsHorizontalScrollChainingEnabled="False" ScrollViewer.IsVerticalScrollChainingEnabled="False">
                    <GridView.ItemTemplate>
                        <DataTemplate>
                            <StackPanel Margin="20">
                                <TextBlock Text="{Binding Name}" Width="100" FontWeight="Bold" Foreground="Red" Style="{StaticResource ItemTextStyle}" FontSize="26"/>
                            </StackPanel>
                        </DataTemplate>
                    </GridView.ItemTemplate>
                </GridView>
            </SemanticZoom.ZoomedOutView>
        </SemanticZoom>

bool CanChangeViews 指明是否可以进行视图切换

bool IsZoomedInViewActive 指明近景视图ZoomedInView是否为活动视图

在视图切换时会触发两个事件:ViewChangeStarted和ViewChangeCompleted。

代码:

        public class SemanticItem
        {
            public string Name { get; set; }
            public DateTime DueDate { get; set; }
            public bool Complete { get; set; }
        }
        private void InitDataSource()
        {
            List<SemanticItem> ds = new List<SemanticItem>();
            for (int i = 0; i < 20; i++)
            {
                ds.Add(new SemanticItem() { Name = "S " + i, DueDate = DateTime.Now, Complete = true });
            }
            this.gv1.ItemsSource = this.gv2.ItemsSource = ds;
        }

如果你想向用户提供两个非常修改化的视图,当然可以发挥你的丰富想象力来实现布局。

 

第5节 VariableSizedWrapGrid

在Silverlight/WPF有一个扩展控件WrapPanel,它是根据自身的宽高和子元素的宽高对子元素进行“换行”排列,当指定子元素横排列优先时,如果横向布局不足容纳全部子元素,则会自动从下一行开始排列,纵向排列优先时类似。Metro应用提供了一个类似的控件VariableSizedWrapGrid,它比WrapPanel有更多的控制,比如,它可以指定子元素的宽和高,还可以指定当前用于布局子元素的行和列数,并且它还能指定某个子元素占用几行或几列,非常强大!如下图:

XAML:

<VariableSizedWrapGrid Grid.Row="1" MaximumRowsOrColumns="5" ItemHeight="44" ItemWidth="44" HorizontalAlignment="Left"
 Height="262" Margin="10,10,0,0" VerticalAlignment="Top" Width="396">
            <Rectangle Fill="Red"/>
            <Rectangle Fill="Blue"/>
            <Rectangle Fill="Red"/>
            <Rectangle Fill="Blue"/>
            <Rectangle Fill="Red"/>
            <Rectangle Fill="Blue"/>
            <Rectangle Fill="Red"/>
            <Rectangle Fill="Blue"/>
            <Rectangle Fill="Red"/>
            <Rectangle Fill="Blue"/>
            <Rectangle Fill="Red"/>
            <Rectangle Fill="Blue"/>
            <Rectangle Fill="Red"/>
            <Rectangle Fill="Blue"/>
            <Rectangle Fill="Red"/>
            <Rectangle Fill="Blue"/>
            <Rectangle Fill="Red"/>
            <Rectangle Fill="Blue"/>
            <Rectangle Fill="Red"/>
            <Rectangle Fill="Blue"/>
            <Rectangle Fill="Red"/>
            <Rectangle Fill="Blue"/>
            <Rectangle Fill="Red"/>
            <Rectangle Fill="Blue"/>
            <Rectangle Fill="Red"/>
            <Rectangle Fill="Blue"/>
            <Rectangle Fill="Red"/>
            <Rectangle Fill="Blue"/>
            <Rectangle Fill="White" Height="80" VariableSizedWrapGrid.RowSpan="2"/>
            <Rectangle Fill="Green" Width="80" VariableSizedWrapGrid.ColumnSpan="2"/>

        </VariableSizedWrapGrid>

VariableSizedWrapGrid有几个非常重要的属性:

double ItemHeight 设置每个子项的高

double ItemWidth 设置每个子项的宽

Orientation Orientation 子元素的排列方向

int MaximumRowsOrColumns获取或设置影响换行点同时说明 Orientation 的值。

DependencyProperty RowSpanProperty 子元素占据的行数,如上图中的白色块。

DependencyProperty ColumnSpanProperty 子元素占据的列数,如上图中的绿色块。

 

小 结

这一章简单介绍了5个控件的常用方法,其实有过Silverlight/WPF经验的都很容易理解,也参考MSDN的相关文档。

posted @ 2012-09-17 08:30  solan3000  阅读(7822)  评论(5编辑  收藏  举报