xamarin 自定义集合视图实现

jfewjypa  于 2023-01-28  发布在  其他
关注(0)|答案(2)|浏览(167)

我正在尝试实现一个CollectionView,如下图所示。正如你所看到的,如果一个元素被选中,它在右上角有一个对应的复选标记

首先,我试图在SelectionChanged事件中找到AbstractLayout类别ID,并且已经在其中查找了名为=“showIfSelected”的元素,但是在collectionView中搜索时,我总是得到null。我已经阅读了几篇关于为什么会出现这种情况的文章,但是我还没有找到解决问题的方法。也许有人可以告诉我最终如何达到我需要的结果?
.xaml:

<?xml version="1.0" encoding="utf-8" ?>
<ContentPage 
    xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
    xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
    xmlns:viewmodels="clr-namespace:Paraglider.MobileApp.ViewModels"
    xmlns:models="clr-namespace:Paraglider.MobileApp.Models"
    x:DataType="viewmodels:CatalogPageViewModel"
    x:Class="Paraglider.MobileApp.Pages.CatalogPage">

    <Grid BackgroundColor="#FFFEF6">
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="*" />
            <ColumnDefinition Width="*" />
        </Grid.ColumnDefinitions>

        <Grid.RowDefinitions>
            <RowDefinition Height="*" />
            <RowDefinition Height="10*" />
        </Grid.RowDefinitions>

        ...

        <ScrollView Grid.Row="1" Grid.ColumnSpan="2" Margin="20, 0" VerticalScrollBarVisibility="Never">

            <CollectionView
                x:Name="collectionView"
                Grid.Row="2" Grid.ColumnSpan="2"
                ItemsSource="{Binding Categories}"
                SelectionMode="Multiple"
                SelectionChanged="CollectionView_SelectionChanged">

                <CollectionView.Header>...</CollectionView.Header>

                <CollectionView.ItemsLayout>
                    <GridItemsLayout Orientation="Vertical" Span="{OnIdiom Default=2}" />
                </CollectionView.ItemsLayout>

                <CollectionView.ItemTemplate>
                    <DataTemplate x:DataType="models:Category">
                        <AbsoluteLayout x:Name="{Binding Id}">
                            
                            <Image 
                                Aspect="AspectFit" 
                                WidthRequest="165" 
                                Source="pzv.svg" 
                                Margin="0, 10" />
                            
                            <Image
                                x:Name="showIfSelected"
                                Aspect="AspectFit" 
                                AbsoluteLayout.LayoutBounds="130, 0, autoSize, autoSize"  
                                Source="selected_category.svg" 
                                IsVisible="True" >
                                <Image.Shadow>
                                    <Shadow Brush="Black" Offset="0, 10" Opacity="0.1" Radius="15" />
                                </Image.Shadow>
                            </Image>
                            
                        </AbsoluteLayout>
                    </DataTemplate>
                </CollectionView.ItemTemplate>

            </CollectionView>

        </ScrollView>

    </Grid>
</ContentPage>

.xaml.cs:

public partial class CatalogPage : ContentPage
{
    public CatalogPage(CatalogPageViewModel viewModel)
    {
        InitializeComponent();
        BindingContext = viewModel;
    }

    private void CollectionView_SelectionChanged(object sender, SelectionChangedEventArgs e)
    {
        var prev = e.PreviousSelection.Select(x => (Category)x).ToList();
        var current = e.CurrentSelection.Select(x => (Category)x).ToList();

        var unselected = prev.ExceptBy(current.Select(x => x.Id), x => x.Id).ToList();

        foreach (var item in unselected)
        {
            var layout = this.FindByName<AbsoluteLayout>($"{item.Id}");
            var image = layout.FindByName<Image>("showIfSelected");
            image.IsVisible = false;
        }

        var selected = current.ExceptBy(prev.Select(x => x.Id), x => x.Id).ToList();

        foreach (var item in selected)
        {
            var layout = this.FindByName<AbsoluteLayout>($"{item.Id}");
            var image = layout.FindByName<Image>("showIfSelected");
            image.IsVisible = true;
        }
    }
}
xoefb8l8

xoefb8l81#

合并样式:https://learn.microsoft.com/en-us/dotnet/maui/user-interface/styles/xaml?view=net-maui-7.0
和视觉状态:https://learn.microsoft.com/en-us/dotnet/maui/user-interface/visual-states?view=net-maui-7.0
您可以使用VisualState来确定要使用的样式。CollectionView具有一个选定的VisualState。
您可以命名您的可视元素,比方说图片,并使用样式来更改该可视元素的属性。
不需要这堵文本墙,一切都可以在XAML中完成,不需要任何代码。
编辑:其他用户建议,你可能需要例子如何做到这一点。这是一个工作代码,从我的一个测试项目(在MAUI NET 7.0)

<Setter Property="VisualStateManager.VisualStateGroups">
        <VisualStateGroupList>
            <VisualStateGroup x:Name="CommonStates">
                <VisualState x:Name="Normal" />
                <VisualState x:Name="Selected">
                    <VisualState.Setters>
                        <Setter Property="Stroke".../>
                        <Setter Property="BackgroundColor" ..."/>
                        <Setter TargetName="line1"
                            Property="Label.TextColor"
                            Value="Black" />...

您可以注意到的重要部分是,我有一个Label VisualElement,它的名称为“line1”,我访问属性TextColor,并在选中项目时应用值Black。
如果应用此样式,并且未能提供Label类型的子VisualElement和指定的Name,则会引发严重的运行时异常。
从好的方面来说,这种方法的替代方案更耗时(在量级上),也容易出错。
希望这能帮上忙。

bqjvbblv

bqjvbblv2#

正如Jason所建议的,您可以直接将IsVisible绑定到您的Model的属性,如下所示:

    • XAML格式:**
<Image
     Aspect="AspectFit"          
     Source="selected_category.png" 
     IsVisible="{Binding IsSelected, Mode=TwoWay}" >
                  
 </Image>
    • 型号:**
public partial class Category : ObservableObject
{
    [ObservableProperty]
    private bool isSelected;
}

相关问题