MAUI新生6.4-集合内容类控件难点:CollectionView

、基本使用(数据源在ViewModel中硬编码)

//①在Models文件夹下,新建Employee.cs文件,创建Employee类型
public class Employee
{
    public int Id { get; set; }
    public string Name { get; set; }
    public string Department { get; set; }
    public string Portrait { get; set; }
    public string Info { get; set; }
}

//②在ViewModels文件夹下,创建MainPageViewModel.cs
//头像图片复制到Resources/Images文件夹下
public partial class MainPageViewModel:ObservableObject
{
    [ObservableProperty]
    private ObservableCollection<Employee> employees;
public MainPageViewModel() { employees = new ObservableCollection
<Employee> { new Employee{ Id=1,Name="zs",Department="行政部",Portrait="zs.png",Info="这是关于张三的介绍" }, new Employee{ Id=2,Name="ls",Department="行政部",Portrait="ls.png",Info="这是关于李四的介绍" }, new Employee{ Id=3,Name="ww",Department="营销部",Portrait="ww.png",Info="这是关于王五的介绍" }, new Employee{ Id=4,Name="zl",Department="营销部",Portrait="zl.png",Info="这是关于赵六的介绍" }, new Employee{ Id=5,Name="qq",Department="营销部",Portrait="qq.png",Info="这是关于钱七的介绍" } }; } } //③MainPage.xaml使用CollectionView <ContentPage ...... xmlns:vm="clr-namespace:MauiApp16.ViewModels"> <!--使用最简易的关联ViewModel方式--> <ContentPage.BindingContext> <vm:MainPageViewModel /> </ContentPage.BindingContext> <!--ItemsSource属性为数据源--> <CollectionView ItemsSource="{Binding Employees}"> <!--ItemTemplate属性定义外观,类型为DataTemplate--> <!--DataTemplate为迭代的每一条数据项定义样式外观,类似于Vue中的v-for或Blazor中的foreach--> <CollectionView.ItemTemplate> <DataTemplate> <Grid ColumnDefinitions="Auto,Auto" RowDefinitions="Auto,Auto,Auto"> <Image Grid.Row="0" Grid.RowSpan="3" Grid.Column="0" Aspect="AspectFill" HeightRequest="60" Source="{Binding Portrait}" WidthRequest="60" /> <Label Grid.Row="0" Grid.Column="1" FontAttributes="Bold" Text="{Binding Name}" /> <Label Grid.Row="1" Grid.Column="1" Text="{Binding Department}" /> <Label Grid.Row="2" Grid.Column="1" FontAttributes="Italic" Text="{Binding Info}" VerticalOptions="End" /> </Grid> </DataTemplate> </CollectionView.ItemTemplate> </CollectionView> </ContentPage>

 

 

二、外观设置

1、条件样式:迭代数据项时,根据不同条件,显示不同外观,类似于Vue的v-for中if、Blaozor的foreach中if

//①在Controls文件夹下,创建一个Selector类
public class EmployeeDataTemplateSelector : DataTemplateSelector
{
    //实例化EmployeeDataTemplateSelector时,从外部赋值XingZhengBu和YingXiaoBu属性
    public DataTemplate XingZhengBu { get; set; }
    public DataTemplate YingXiaoBu { get; set; }

    //参数item为CollectionView迭代数据项,此例为Employee类型对象
    protected override DataTemplate OnSelectTemplate(object item, BindableObject container)
    {
        return ((Employee)item).Department.Contains("行政部")? XingZhengBu: YingXiaoBu;
    }
}

//②在MainPage.xaml文件中使用
<ContentPage
    ......
    xmlns:c="clr-namespace:MauiApp16.Controls"
    xmlns:vm="clr-namespace:MauiApp16.ViewModels">

    <ContentPage.BindingContext>
        <vm:MainPageViewModel />
    </ContentPage.BindingContext>

    <ContentPage.Resources>
        <!--  定义两个DataTemplate  -->
        <!--  本例仅将部门Label设置为不同的颜色  -->
        <DataTemplate x:Key="YingXiaoBu">
            <Grid ColumnDefinitions="Auto,Auto" RowDefinitions="Auto,Auto,Auto">
                ......
                <Label
                    Grid.Row="1"
                    Grid.Column="1"
                    Text="{Binding Department}"
                    TextColor="DarkBlue" />
                ......
            </Grid>
        </DataTemplate>
        <DataTemplate x:Key="XingZhengBu">
            <Grid ColumnDefinitions="Auto,Auto" RowDefinitions="Auto,Auto,Auto">
                ......
                <Label
                    Grid.Row="1"
                    Grid.Column="1"
                    Text="{Binding Department}"
                    TextColor="DarkRed" />
                ......
            </Grid>
        </DataTemplate>
<!-- 将定义的两个DataTemplate传入DataTemplateSelector --> <!-- 在DataTemplateSelector中,根据迭代的Item数据项条件,使用不同的DataTemplate --> <c:EmployeeDataTemplateSelector x:Key="EmployeeSelector" XingZhengBu="{StaticResource XingZhengBu}" YingXiaoBu="{StaticResource YingXiaoBu}" /> </ContentPage.Resources> <!-- ItemTemplate使用DataTemplateSelector,将迭代数据项作为参数传入到Selector中 --> <CollectionView ItemTemplate="{StaticResource EmployeeSelector}" ItemsSource="{Binding Employees}" /> </ContentPage>

  

2、排列方式:通过ItemsLayout设置垂直列表、水平列表、垂直网格、水平网格

1)垂直列表(如果不设置ItemsLayout,默认为垂直列表)

<!--简单设置ItemsLayout属性-->
<CollectionView ItemsSource="{Binding Employees}" ItemsLayout="VerticalList">
    ...
</CollectionView>

<!--通过元素属性设置ItemsLayout-->
<!--垂直/水平列表的类型为LinearItemsLayout-->
<CollectionView ItemsSource="{Binding Employees}">
    <CollectionView.ItemsLayout>
        <LinearItemsLayout Orientation="Vertical" />
    </CollectionView.ItemsLayout>
    ...
</CollectionView>

 2)水平列表 

<!--简单设置ItemsLayout属性-->
<CollectionView ItemsSource="{Binding Employees}" ItemsLayout="HorizontalList">
    ...
</CollectionView>

<!--通过元素属性设置ItemsLayout-->
<!--垂直/水平列表的类型为LinearItemsLayout-->
<CollectionView ItemsSource="{Binding Employees}">
    <CollectionView.ItemsLayout>
        <LinearItemsLayout Orientation="Horizontal" />
    </CollectionView.ItemsLayout>
    ...
</CollectionView>

 

 3)垂直网格

<!--通过ItemsLayout属性直接设置两列垂直网格-->
<CollectionView ItemsSource="{Binding Employees}"
                ItemsLayout="VerticalGrid, 2">
    ......
</CollectionView>

<!--通过元素属性,设置两列垂直网格。网格的对象类型为GridItemsLayout-->
<CollectionView ItemsSource="{Binding Employees}">
    <CollectionView.ItemsLayout>
       <GridItemsLayout Orientation="Vertical"
                        Span="2" />
    </CollectionView.ItemsLayout>
    ...
</CollectionView>

 4)水平网格

<!--通过ItemsLayout属性直接设置两列水平网格-->
<CollectionView ItemsSource="{Binding Employees}"
                ItemsLayout="HorizontalGrid, 2">
    ......
</CollectionView>

<!--通过元素属性,设置两列水平网格。网格的对象类型为GridItemsLayout-->
<CollectionView ItemsSource="{Binding Employees}">
    <CollectionView.ItemsLayout>
       <GridItemsLayout Orientation="Horizontal"
                        Span="2" />
    </CollectionView.ItemsLayout>
    ...
</CollectionView>

  

3、页眉页脚Header/Footer设置

1)页眉页脚设置为字符串

<!--设置Header属性和Footer属性-->
<CollectionView ItemsSource="{Binding Employees}"
                Header="Employees"
                Footer="2023">
    ...
</CollectionView>

 2)页眉页脚中显示视图 

<CollectionView ItemsSource="{Binding Employees}">
    <CollectionView.Header>
        <StackLayout BackgroundColor="Gray">
            <Label
                Margin="5"
                FontAttributes="Bold"
                FontSize="12"
                Text="Employees" />
        </StackLayout>
    </CollectionView.Header>
    <CollectionView.Footer>
        <StackLayout BackgroundColor="AliceBlue">
            <Label
                Margin="5"
                FontAttributes="Italic"
                FontSize="10"
                Text="2023" />
        </StackLayout>
    </CollectionView.Footer>
    ......
</CollectionView>

 

  3)页眉页脚设置DataTemplate。使用了DataTemplate后,可以设置条件模板,可自行测试。

<CollectionView
    Footer="{Binding .}"
    Header="{Binding .}"
    ItemsSource="{Binding Employees}">
    <CollectionView.HeaderTemplate>
        <DataTemplate>
            <StackLayout BackgroundColor="Gray">
                <Label
                    Margin="5"
                    FontAttributes="Bold"
                    FontSize="12"
                    Text="Employees" />
            </StackLayout>
        </DataTemplate>
    </CollectionView.HeaderTemplate>
    <CollectionView.FooterTemplate>
        <DataTemplate>
            <StackLayout BackgroundColor="AliceBlue">
                <Label
                    Margin="5"
                    FontAttributes="Italic"
                    FontSize="10"
                    Text="2023" />
            </StackLayout>
        </DataTemplate>
    </CollectionView.FooterTemplate>
    ......
</CollectionView>

 

4、数据项间距。此时,ItemsLayout需要使用属性元素的方式进行设置(见第2项设置)

1)垂直/水平列表的数据项间距

<CollectionView ItemsSource="{Binding Employees}">
    <CollectionView.ItemsLayout>
        <LinearItemsLayout ItemSpacing="10" Orientation="Vertical" />
    </CollectionView.ItemsLayout>
    ......
</CollectionView>

 2)垂直/水平网格的Item数据项间距

<CollectionView ItemsSource="{Binding Employees}">
    <CollectionView.ItemsLayout>
       <GridItemsLayout Orientation="Vertical"
                        Span="2"
                        VerticalItemSpacing="20"
                        HorizontalItemSpacing="30" />
    </CollectionView.ItemsLayout>
    ...
</CollectionView>

 

5、数据项大小调整的策略

<!--默认值MeasureAllItems,每一项都进行大小调整-->
<CollectionView ...
                ItemSizingStrategy="MeasureAllItems">
    ...
</CollectionView>

<!--MeasureFirstItem值,后续数据项都根据第一项大小为准->
<CollectionView ...
                ItemSizingStrategy="MeasureFirstItem">
    ...
</CollectionView>

 

6、数据项对齐方式

<!--默认为左对齐,通过在ContentPage上设置FlowDirection属性,修改为右对齐-->
<ContentPage
    ......
    FlowDirection="RightToLeft">
    <StackLayout Margin="20">
        <CollectionView ItemsSource="{Binding Monkeys}">
            ...
        </CollectionView>
    </StackLayout>
</ContentPage>

 

7、动态调整数据项的大小

//XAML文件中,ImageButton定义Clicked事件
<ContentPage
    ......><Grid RowDefinitions="*,50"><CollectionView
            ......
            <CollectionView.ItemTemplate>
                <DataTemplate>
                    <Grid ...>
                        <ImageButton
                            Clicked="ImageButton_Clicked"
                            HeightRequest="60"
                            WidthRequest="60"
                            Source="{Binding Portrait}"/>
                        ......
                    </Grid>
                </DataTemplate>
            </CollectionView.ItemTemplate>
        </CollectionView>
    </Grid>
</ContentPage>

//后台代码Clicked事件中,更改ImageButton的尺寸
private void ImageButton_Clicked(object sender, EventArgs e)
{
    var imageButton = sender as ImageButton;
    imageButton.HeightRequest = imageButton.WidthRequest = imageButton.HeightRequest.Equals(60) ? 100 : 60;
}

 

8、轻扫上下文菜单,结合SwipeView控件 

<ContentPage
    ......>
    <ContentPage.BindingContext>
        <vm:MainPageViewModel />
    </ContentPage.BindingContext>
    <CollectionView
        ......>
        </CollectionView.ItemsLayout>
        <CollectionView.ItemTemplate>
            <DataTemplate>
                <!--使用SwipeView控件,包装DataTemplate对象-->
                <SwipeView>
                    <!--设置左滑菜单,还可以设置右滑、上滑、下滑-->
                    <SwipeView.LeftItems>
                        <!--设置两个菜单,收藏和删除-->
                        <!--点击菜单,响应相应的命令,参数为当前行。设置为{Binding}或{Binding .}均可-->
                        <SwipeItems>
                            <SwipeItem
                                BackgroundColor="AliceBlue"
                                Command="{Binding Source={x:Reference collectionView}, Path=BindingContext.FavoriteCommand}"
                                CommandParameter="{Binding}"
                                Text="收藏" />
                            <SwipeItem
                                BackgroundColor="LightGray"
                                Command="{Binding Source={x:Reference collectionView}, Path=BindingContext.DeleteCommand}"
                                CommandParameter="{Binding .}"
                                Text="删除" />
                        </SwipeItems>
                    </SwipeView.LeftItems>
                    <Grid>
                        ......
                    </Grid>
                </SwipeView>
            </DataTemplate>
        </CollectionView.ItemTemplate>
    </CollectionView>
</ContentPage>

//ViewModel中的命令(DeleteCommand)
[RelayCommand]
private void Delete(Employee employee)
{
    employees.Remove(employee);
}

 

 

三、数据源设置

1、数据项选择控制

1)单选及设置预选 

<ContentPage
    ......>
    <ContentPage.BindingContext>
        <vm:MainPageViewModel />
    </ContentPage.BindingContext>
    <VerticalStackLayout>
        <!--
            SelectionMode设置选择模式,Single-单选、Multiple-多选,None-禁用选择
            SelectedItem设置初始预选项目,通过ViewModel设置。在后台代码中,将SelectedItem设置为null,则可以清除选择!
            单选时,SelectedItem为双向绑定,Label的Text显示选定项目
            SelectionChanged选择更改时触发的事件
        -->
        <Label Text="{Binding SelectedEmployee.Name}" />
        <CollectionView
            ItemsSource="{Binding Employees}"
            SelectedItem="{Binding SelectedEmployee}"
            SelectionChanged="CollectionView_SelectionChanged"
            SelectionMode="Single">
            <CollectionView.ItemTemplate>
                ......
            </CollectionView.ItemTemplate>
        </CollectionView>
    </VerticalStackLayout>
</ContentPage>

//ViewModel设置初始预先项目SelectedEmployee
public partial class MainPageViewModel:ObservableObject
{
    [ObservableProperty]
    private ObservableCollection<Employee> employees;

    [ObservableProperty]
    private Employee selectedEmployee;
    public MainPageViewModel()
    {
        employees = new ObservableCollection<Employee> 
        { 
            new Employee{ Id=1,Name="zs",Department="行政部",Portrait="zs.png",Info="这是关于张三的介绍" },
            new Employee{ Id=2,Name="ls",Department="行政部",Portrait="ls.png",Info="这是关于李四的介绍" },
            new Employee{ Id=3,Name="ww",Department="营销部",Portrait="ww.png",Info="这是关于王五的介绍" },
            new Employee{ Id=4,Name="zl",Department="营销部",Portrait="zl.png",Info="这是关于赵六的介绍" },
            new Employee{ Id=5,Name="qq",Department="营销部",Portrait="qq.png",Info="这是关于钱七的介绍" }
        };
        selectedEmployee = employees.Skip(2).FirstOrDefault();
    }
}

  

2)多选及设置预选 

<ContentPage
    ......>

    <ContentPage.BindingContext>
        <vm:MainPageViewModel />
    </ContentPage.BindingContext>
    <VerticalStackLayout>
        <!--
            SelectionMode设置选择模式,Single-单选、Multiple-多选
            SelectedItems设置初始预选项目,通过ViewModel设置。多选时,SelectedItems为单向绑定
        -->
        <CollectionView
            ItemsSource="{Binding Employees}"
            SelectedItems="{Binding SelectedEmployees}"
            SelectionMode="Multiple">
            <CollectionView.ItemTemplate>
                ......
            </CollectionView.ItemTemplate>
        </CollectionView>
    </VerticalStackLayout>
</ContentPage>

//ViewModel中设置SelectedEmployees
public partial class MainPageViewModel:ObservableObject
{
    ......
    [ObservableProperty]
    private ObservableCollection<Employee> selectedEmployees;
    public MainPageViewModel()
    {
        ......
        selectedEmployees = new ObservableCollection<Employee> { employees[0], employees[3] };
    }
}

  

3)设置选择颜色 

<!--设置待定颜色稍显复杂,需要通过可视状态来设置-->
<!--Style的TargetType需设置为DataTemplate的根元素,本例中为Grid-->
<ContentPage ...>
    <ContentPage.Resources>
        <Style TargetType="Grid">
            <Setter Property="VisualStateManager.VisualStateGroups">
                <VisualStateGroupList>
                    <VisualStateGroup x:Name="CommonStates">
                        <VisualState x:Name="Normal" />
                        <VisualState x:Name="Selected">
                            <VisualState.Setters>
                                <Setter Property="BackgroundColor"
                                        Value="LightSkyBlue" />
                            </VisualState.Setters>
                        </VisualState>
                    </VisualStateGroup>
                </VisualStateGroupList>
            </Setter>
        </Style>
    </ContentPage.Resources>
    <StackLayout Margin="20">
        <CollectionView ItemsSource="{Binding Monkeys}"
                        SelectionMode="Single">
            .....
        </CollectionView>
    </StackLayout>
</ContentPage>

 

2、EmptyView:设置数据源不可用时,显示的替代内容

1)数据不可用时,显示字符串 

<!--数据源EmptyEmployees为null,显示EmptyView的字符串值-->
<CollectionView ItemsSource="{Binding EmptyEmployees}" EmptyView="无数据显示">
    ......
</CollectionView>

  

2)数据不可用时,显示视图(ContentView) 

<CollectionView ItemsSource="{Binding EmptyEmployees}">
    <CollectionView.EmptyView>
        <!--ContentView可以省略-->
        <ContentView>
            <StackLayout HorizontalOptions="CenterAndExpand" VerticalOptions="CenterAndExpand">
                <Label
                        Margin="10,25,10,10"
                        FontAttributes="Bold"
                        FontSize="18"
                        HorizontalOptions="Fill"
                        HorizontalTextAlignment="Center"
                        Text="无数据显示" />
            </StackLayout>
        </ContentView>
    </CollectionView.EmptyView>
    ......
</CollectionView>

 

3)运行时再确定数据不可用时显示的视图方式一:ContentView 

<ContentPage
    ......>
    <!--定义两个ContentView资源-->
    <ContentPage.Resources>
        <ContentView x:Key="EmptyViewA">
            <StackLayout HorizontalOptions="CenterAndExpand" VerticalOptions="CenterAndExpand">
                <Label FontSize="18" Text="无数据显示A" />
            </StackLayout>
        </ContentView>
        <ContentView x:Key="EmptyViewB">
            <StackLayout HorizontalOptions="CenterAndExpand" VerticalOptions="CenterAndExpand">
                <Label FontSize="18" Text="无数据显示B" />
            </StackLayout>
        </ContentView>
    </ContentPage.Resources>

    <VerticalStackLayout>
        <!--通过Swith的Toggled事件,后台代码更改CollectionView的EmptyView属性-->
        <Switch Toggled="Switch_Toggled"/>
        <CollectionView x:Name="collectionView" ItemsSource="{Binding EmptyEmployees}">
            <CollectionView.ItemTemplate>
                ......
            </CollectionView.ItemTemplate>
        </CollectionView>
    </VerticalStackLayout>
</ContentPage>

//Switch的Toggled事件上
private void Switch_Toggled(object sender, ToggledEventArgs e)
{
    collectionView.EmptyView = e.Value ? Resources["EmptyViewA"] : Resources["EmptyViewB"];
}

 

4、下拉刷新?

 

5、增量加载?

 

 

四、列表滚动控制

1、滚动事件

<CollectionView Scrolled="CollectionView_Scrolled">
    ......
</CollectionView>

//ItemsViewScrolledEventArgs类型参数定义了丰富的属性,包括:
//HorizontalDelta/VerticalDelta:水平/垂直滚动量
//HorizontalOffset/VerticalOffset:相对于原点的水平/垂直偏移量
//FirstVisibleItemIndex/LastVisibleItemIndex/CenterItemIndex:列表中可见的第一项/最后一项/中间项索引
private async void CollectionView_Scrolled(object sender, ItemsViewScrolledEventArgs e)
{
    ......    
}

 

2、代码控件滚动

1)滚动到索引 

//滚动到索引为5的数据项,并将数据项显示在列表可视窗的开始位置
collectionView.ScrollTo(5,position:ScrollToPosition.Start);
//分组数据时,滚动到第2组,索引为1的数据项
collectionView.ScrollTo(2, 1);

2)滚动到Item数据项

//获取当前页面的BindingContext,并转化为页面对应的ViewModel对象
var viewModel = BindingContext as MainPageViewModel;
//筛选ViewModel中的Employees数据集合,并选取Name为qq的第一条数据
var person = viewModel.Employees.FirstOrDefault(e=>e.Name=="qq");
//滚动到这条数据项,在列表窗口的开始位置显示,并禁用滚动动画
collectionView.ScrollTo(person,position:ScrollToPosition.Start,animate:false);

//分组数据滚动
GroupedAnimalsViewModel viewModel = BindingContext as GroupedAnimalsViewModel;
AnimalGroup group = viewModel.Animals.FirstOrDefault(a => a.Name == "Monkeys");
Animal monkey = group.FirstOrDefault(m => m.Name == "Proboscis Monkey");
collectionView.ScrollTo(monkey, group);

 

 

五、数据分组显示 :

总体分为两步:一、设置分组数据源;二、在XAML中显示分组;三、设置页眉、页脚、空组显示等,可详见文档。 

 

posted @ 2023-02-01 13:09  functionMC  阅读(1710)  评论(2编辑  收藏  举报