c# - 如何在 Xamarin 表单中使用 MVVM 仅为 Collection View 中的选定框架设置颜色?

标签 c# xamarin xamarin.forms xamarin.android frame

我正在使用 RelativeSource 绑定(bind) 为 Collection View 中的框架绑定(bind) 背景颜色。但是 Collection View 中所有框架的背景颜色都在变化。我需要仅为我选择的框架设置背景颜色

这是我的 xaml 代码

<StackLayout Padding="10">
    <CollectionView x:Name="list" ItemsSource="{Binding samplelist}">
        <CollectionView.ItemsLayout>
            <GridItemsLayout Orientation="Vertical" Span="2" HorizontalItemSpacing="10" VerticalItemSpacing="10" />
        </CollectionView.ItemsLayout>
        <CollectionView.ItemTemplate>
            <DataTemplate>
                <StackLayout>
                    <VisualStateManager.VisualStateGroups>
                        <VisualStateGroup Name="CommonStates">                        
                            <VisualState Name="Selected">
                                <VisualState.Setters>
                                    <Setter Property="BackgroundColor" Value="Green" />
                                </VisualState.Setters>
                            </VisualState>
                        </VisualStateGroup>
                    </VisualStateManager.VisualStateGroups>
                    <Frame  CornerRadius="10"  HasShadow="False" BackgroundColor="{Binding BackgroundTest,Mode=TwoWay, Converter={StaticResource colorConverter}}" HeightRequest="75" Margin="5,0,0,0" >
                        <StackLayout Orientation="Vertical">
                            <StackLayout.GestureRecognizers>
                                <TapGestureRecognizer Command="{Binding Source={x:Reference test}, Path=BindingContext.TriggerScene}"
                                                              CommandParameter="{Binding .}"/>
                            </StackLayout.GestureRecognizers>

这是我在 Viewmodel

中的代码
public bool FrameColorChange=true;
private Color _backgroundTest;
public Color BackgroundTest
{
    get { return _backgroundTest; }       
    set
    {
        if (value == _backgroundTest)
            return;
    
        _backgroundTest = value;
        OnPropertyChanged(nameof(BackgroundTest));
    }
}
private async void TriggerScene(Scene scene)
{

    if (FrameColorChange==true)
    {
        BackgroundTest = Color.Gray;
        FrameColorChange = false;
    }
    else
    {
        BackgroundTest = Color.White;
        FrameColorChange = true;
    }
}

我已经完成了一些修复,例如

how to access child elements in a collection view?

但没有任何帮助。我也尝试了 SelectionChanged 事件。但是 SelectionChanged 的​​问题是它没有正确触发,因为我的框架中有 TapGestureRecognizer。我希望在我的 TriggerScene 命令 中为所选帧设置颜色绑定(bind) 我的 viewmodel 中的 TapGestureRecognizer。我不想在后面使用代码。我不知道如何解决这个问题有什么建议吗?

最佳答案

我已经在另一个答案中发布了解决您问题的一种方法,现在我想提供一个更简单的解决方案(尽管我不会声称它是最好的)。

注意,在这个解决方案中,与我的其他答案相反,您不需要向 Collection View 中的对象添加多余的属性,但新属性直接定义在ViewModel。


解决问题的一种方法是:

  1. 在您的 ViewModel 中定义一个名为 SelectedItem 的属性:这将跟踪当前选定的项目。
  2. 然后你将 FrameBackgroundColor 绑定(bind)到新属性:SelectedItem,为此你需要一个 ValueConverter 接受 SelectedItemConverterParameter:当前 Frame
  3. Frame 内,在 StackLayout 中有 good old TapGestureRecognizer,其处理程序在调用时将设置所选项目
  4. 设置 SelectedItem 时,会调用 OnPropertyChanged 并为 CollectionView 中的每个项目调用 ValueConverter。转换器然后检查 Frame 的 BindingContext(它绑定(bind)到的项目!)是否与 SelectedItem 相同,如果是,则将其颜色设置为 Gray(选中!)

当然,下面我添加了一个完整的最小工作示例。随意复制粘贴并使用它。

Page1.xaml

<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="App1.Page1"
             x:Name="test"
             xmlns:local="clr-namespace:App1">
    
    <ContentPage.BindingContext>
        <local:ViewModel/>
    </ContentPage.BindingContext>

    <ContentPage.Resources>
        <ResourceDictionary>
            <local:SelectedToColorConverter x:Key="selectedToColorConverter"/>
        </ResourceDictionary>
    </ContentPage.Resources>
    
    <ContentPage.Content>
        <StackLayout Padding="10">
            <CollectionView ItemsSource="{Binding samplelist}">
                
                <CollectionView.ItemsLayout>
                    <GridItemsLayout Orientation="Vertical" Span="2" HorizontalItemSpacing="10" VerticalItemSpacing="10" />
                </CollectionView.ItemsLayout>
                
                <CollectionView.ItemTemplate>
                    <DataTemplate>
                        <StackLayout>

                            <Frame x:Name="frame" CornerRadius="10"  
                                    HasShadow="False" 
                                    BackgroundColor="{Binding Source={x:Reference test}, Path=BindingContext.SelectedItem, Converter={x:StaticResource selectedToColorConverter}, ConverterParameter={x:Reference frame}}" 
                                    HeightRequest="75" 
                                    Margin="5,0,0,0" >
                                <StackLayout Orientation="Vertical">
                                    <StackLayout.GestureRecognizers>
                                        <TapGestureRecognizer Command="{Binding Source={x:Reference test}, Path=BindingContext.TriggerSceneCommand}" CommandParameter="{Binding .}"/>
                                    </StackLayout.GestureRecognizers>
                                    <Label Text="{Binding Text}"/>
                                    <Label Text="{Binding Description}"/>
                                </StackLayout>
                            </Frame>
                            
                        </StackLayout>
                    </DataTemplate>
                </CollectionView.ItemTemplate>
            </CollectionView>
        </StackLayout>

    </ContentPage.Content>
</ContentPage>

ViewModel.cs

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Globalization;
using System.Runtime.CompilerServices;
using Xamarin.Forms;

namespace App1
{
    public class ViewModel : INotifyPropertyChanged
    {

        public ViewModel()
        {
            samplelist = new List<item> 
            { 
                new item { Text = "Uno", Description = "Uno Description bla bla" },
                new item { Text = "Dos", Description = "Dos Description bla bla" },
                new item { Text = "Tres", Description = "Tres Description bla bla" }
            };

            TriggerSceneCommand = new Command<item>(TriggerScene);
        }

        public List<item> samplelist { get; set; }


        private item _selectedItem;
        public item SelectedItem
        {
            get => _selectedItem;
            set
            {
                _selectedItem = value;
                OnPropertyChanged();
            }
        }



        public Command TriggerSceneCommand { get; set; }
        private void TriggerScene(item newSelectedItem)
        {
           SelectedItem = newSelectedItem;
        }


        public event PropertyChangedEventHandler PropertyChanged;
        public void OnPropertyChanged([CallerMemberName] string name = "")
        {
            var propertyChanged = PropertyChanged;

            propertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
        }

    }

    public class item
    {

        public String Text { get; set; }

        public String Description { get; set; }

    }


    public class SelectedToColorConverter : IValueConverter
    {
        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
        {

            Color result = Color.White;


            if (value != null && parameter != null && ((Frame)parameter).BindingContext != null && (item)value == (item)((Frame)parameter).BindingContext)
            {
                result = Color.Gray;
            }


            return result;

        }

        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
        {
            return null;
        }
    }
}

为什么不,让我们在这里放一个奖金

您可以在 TapGestureRecognizer 的事件处理程序中添加两行代码,以便在一段时间(可能是三秒?)后返回原始颜色。

只需将 ViewModel 中的 TriggerScene 方法更改如下(参见代码注释):

private void TriggerScene(item newSelectedItem)
{
    // Highlight selection!
    SelectedItem = newSelectedItem;

    // Sit and wait...
    await Task.Delay(3000);

    // Go back to normal!
    SelectedItem = null;
}

关于c# - 如何在 Xamarin 表单中使用 MVVM 仅为 Collection View 中的选定框架设置颜色?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/69388564/

相关文章:

c# - 如何利用 C# 属性和反射在标记对象上注入(inject)/强制后期绑定(bind)?

c# - 将 html 保存到 csv 并使用 Excel 打开

ios - 将 iOS 静态库绑定(bind)到 Xamarin.iOS 并使用 dll

c# - Xamarin forms 单击一次显示上下文菜单

C# 命名空间/文件夹 : when is getting too organized/creating too many namespaces not right?

c# - 找不到文件 obj\Debug\100\android\assets\Xamarin.Auth.dll.mdb'

c# - 制作两个 SharedPreferences

android - 从 google play 下载的 Xamarin android 应用程序在主屏幕中提供了两个应用程序图标

ios - Xamarin 表单 - 将所有页面的 SetUseSafeArea 全局设置为 true

c# - 记录服务 SOAP 请求和响应