如何使用 Xamarin 表单中的 MVVM 仅为集合视图中的选定框架设置颜色?
How to set color only for the selected frame in collection view using MVVM in Xamarin forms?
我正在使用 RelativeSource 绑定 为集合视图中的框架绑定 背景颜色。但是集合视图中所有帧 的 背景颜色都在变化。我需要 仅为我选择的框架设置背景颜色。
这是我的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;
}
}
我已经完成了一些修复,例如
但没有任何帮助。我也试过 SelectionChanged event.But SelectionChanged 的问题是它没有正确触发 因为有 TapGestureRecognizer 在我的相框里。我想要我的 TriggerScene 命令 中选定帧的颜色绑定
我的 viewmodel 中的 TapGestureRecognizer。我不想使用代码隐藏。我不知道如何解决这个问题,有什么建议吗?
您可以试试下面的代码。
Xaml:
<StackLayout Padding="10">
<CollectionView x:Name="list" ItemsSource="{Binding samplelist}" SelectionMode="Single" SelectionChanged="list_SelectionChanged" >
<CollectionView.ItemsLayout>
<GridItemsLayout Orientation="Vertical" Span="2" HorizontalItemSpacing="10" VerticalItemSpacing="10" />
</CollectionView.ItemsLayout>
<CollectionView.ItemTemplate>
<DataTemplate>
<Frame CornerRadius="10" HasShadow="False" BackgroundColor="{Binding BackgroundTest}" HeightRequest="75" Margin="5,0,0,0" >
<StackLayout Orientation="Vertical">
<Label Text="{Binding str}"></Label>
</StackLayout>
</Frame>
</DataTemplate>
</CollectionView.ItemTemplate>
</CollectionView>
</StackLayout>
后面的代码:
public Page2()
{
InitializeComponent();
this.BindingContext = new MyViewModel();
}
private void list_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
MyModel previous = e.PreviousSelection.FirstOrDefault() as MyModel;
MyModel current = e.CurrentSelection.FirstOrDefault() as MyModel;
//Set the current to the color you want
current.BackgroundTest = "Red";
if (previous != null)
{
//Reset the previous to defaulr color
previous.BackgroundTest = "Gray";
}
}
视图模型:
public class MyViewModel
{
public ObservableCollection<MyModel> samplelist { get; set; }
public MyViewModel()
{
samplelist = new ObservableCollection<MyModel>()
{
new MyModel(){ BackgroundTest="Gray", str="hello1"},
new MyModel(){ BackgroundTest="Gray", str="hello2"},
new MyModel(){ BackgroundTest="Gray", str="hello3"},
new MyModel(){ BackgroundTest="Gray", str="hello4"},
new MyModel(){ BackgroundTest="Gray", str="hello5"},
new MyModel(){ BackgroundTest="Gray", str="hello6"},
new MyModel(){ BackgroundTest="Gray", str="hello7"},
new MyModel(){ BackgroundTest="Gray", str="hello8"},
};
}
}
型号:
public class MyModel : INotifyPropertyChanged
{
public string str { get; set; }
private string _backgroundTest;
public string BackgroundTest
{
get { return _backgroundTest; }
set
{
_backgroundTest = value;
OnPropertyChanged("BackgroundTest");
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = "")
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
更新:
如果您在 DataTemplate 中有 TapGestureRecognizer
,您可以使用 VisualState
而不是 CollectionView 的 SelectionChanged
。
<ContentPage.Resources>
<ResourceDictionary>
<Style TargetType="StackLayout">
<Setter Property="VisualStateManager.VisualStateGroups">
<VisualStateGroupList>
<VisualStateGroup>
<VisualState x:Name="Selected">
<VisualState.Setters>
<Setter Property="BackgroundColor" Value="Accent" />
</VisualState.Setters>
</VisualState>
<VisualState x:Name="UnSelected">
<VisualState.Setters>
<Setter Property="BackgroundColor" Value="Blue" />
</VisualState.Setters>
</VisualState>
</VisualStateGroup>
</VisualStateGroupList>
</Setter>
</Style>
</ResourceDictionary>
</ContentPage.Resources>
Xaml:
<StackLayout.GestureRecognizers>
<TapGestureRecognizer Tapped="TapGestureRecognizer_Tapped"></TapGestureRecognizer>
</StackLayout.GestureRecognizers>
后面的代码:
StackLayout lastElementSelected;
private void TapGestureRecognizer_Tapped(object sender, EventArgs e)
{
if (lastElementSelected != null)
VisualStateManager.GoToState(lastElementSelected, "UnSelected");
VisualStateManager.GoToState((StackLayout)sender, "Selected");
lastElementSelected = (StackLayout)sender;
}
可能有很多方法可以解决你的问题,我不会声称找到了最好的方法,但它仍然是一种(简单的)方法。
我会在下面为您添加一个complete-working-minimal-sample,这正是您想要的,所以请随意copy-paste并根据您的需要进行调整。
实现目标的一种方法是:
- 在 Object 中添加一个名为
Selected
或类似名称的 属性 来填充 Collection (samplelist
) 绑定到您的 CollectionView.
- 将 Frame 的 BackgroundColor 属性 绑定到
Selected
属性 并设置为它是一个 转换器,从 布尔值 值(已选择?)变为 颜色(选择颜色).
- 然后当 collection 中的项目被点击并且 TapGestureRecognizer 触发时,您可以将所选项目作为 CommandParameter到命令
- 在Command-handler中设置传递项的
Selected
属性为true
。
- 当
Selected
属性变化时,调用Converter,BackgroundColor属性 已更新。
以下示例说明了这一点:
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 CornerRadius="10"
HasShadow="False"
BackgroundColor="{Binding Selected, Converter={x:StaticResource selectedToColorConverter}}"
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
{
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<object>(TriggerScene);
}
public List<item> samplelist { get; set; }
public Boolean isMultiSelect = false;
public Command TriggerSceneCommand { get; set; }
private void TriggerScene(object selectedItem)
{
((item)selectedItem).Selected = !((item)selectedItem).Selected;
if (!isMultiSelect)
{
foreach (item otherItem in samplelist)
{
if (otherItem != selectedItem)
{
otherItem.Selected = false;
}
}
}
}
}
public class item : INotifyPropertyChanged
{
public Boolean _selected;
public Boolean Selected
{
get
{
return _selected;
}
set
{
_selected = value;
OnPropertyChanged();
}
}
public String Text { get; set; }
public String Description { get; set; }
public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChanged([CallerMemberName] string name = "")
{
var propertyChanged = PropertyChanged;
propertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
}
}
public class SelectedToColorConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
return ((Boolean)value) ? Color.Gray : Color.White;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
}
请注意,作为 奖金 有一个名为 isMultiSelect
的 属性,如果为真则允许多个项目to be marked/colored and if false
, then when one item is selected all the others get their Selected
属性 set to false
.
我已经在另一个答案中发布了一种解决问题的方法,现在我想提供一个更简单的解决方案(尽管我不会声称它是最好的)。
注意 在此解决方案中,与我的其他答案相反,您不需要向您的对象添加多余的 属性集合视图,但是新的 属性 是直接在 ViewModel 中定义的。
解决您的问题的一种方法是:
- 在您的 ViewModel 中定义一个名为
SelectedItem
的 属性:这将跟踪当前选定的项目。
- 然后您将 Frame 的
BackgroundColor
绑定到新的 属性: SelectedItem
为此您需要一个 ValueConverter 接受 SelectedItem
和 ConverterParameter
:当前 Frame.
- 在 Frame 中,在 StackLayout 中你有 good old TapGestureRecognizer 其处理程序在调用时将设置 Selected Item.
- 设置 SelectedItem 后,将调用 On属性Changed 并为 CollectionView 中的每个项目调用 ValueConverter。然后,转换器检查框架的 BindingContext(它绑定到的项目!)是否与 SelectedItem 相同,如果是,则将其颜色设置为灰色(选中!)
当然,下面我添加了一个完整的最小工作样本。随意复制粘贴并使用它。
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 的事件处理程序中添加两行代码,以 return 在一段时间(也许是三秒?)后恢复原来的颜色。
只需更改 ViewModel 中的 TriggerScene
方法如下(参见代码注释):
private void TriggerScene(item newSelectedItem)
{
// Highlight selection!
SelectedItem = newSelectedItem;
// Sit and wait...
await Task.Delay(3000);
// Go back to normal!
SelectedItem = null;
}
我正在使用 RelativeSource 绑定 为集合视图中的框架绑定 背景颜色。但是集合视图中所有帧 的 背景颜色都在变化。我需要 仅为我选择的框架设置背景颜色。
这是我的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;
}
}
我已经完成了一些修复,例如
但没有任何帮助。我也试过 SelectionChanged event.But SelectionChanged 的问题是它没有正确触发 因为有 TapGestureRecognizer 在我的相框里。我想要我的 TriggerScene 命令 中选定帧的颜色绑定 我的 viewmodel 中的 TapGestureRecognizer。我不想使用代码隐藏。我不知道如何解决这个问题,有什么建议吗?
您可以试试下面的代码。
Xaml:
<StackLayout Padding="10">
<CollectionView x:Name="list" ItemsSource="{Binding samplelist}" SelectionMode="Single" SelectionChanged="list_SelectionChanged" >
<CollectionView.ItemsLayout>
<GridItemsLayout Orientation="Vertical" Span="2" HorizontalItemSpacing="10" VerticalItemSpacing="10" />
</CollectionView.ItemsLayout>
<CollectionView.ItemTemplate>
<DataTemplate>
<Frame CornerRadius="10" HasShadow="False" BackgroundColor="{Binding BackgroundTest}" HeightRequest="75" Margin="5,0,0,0" >
<StackLayout Orientation="Vertical">
<Label Text="{Binding str}"></Label>
</StackLayout>
</Frame>
</DataTemplate>
</CollectionView.ItemTemplate>
</CollectionView>
</StackLayout>
后面的代码:
public Page2()
{
InitializeComponent();
this.BindingContext = new MyViewModel();
}
private void list_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
MyModel previous = e.PreviousSelection.FirstOrDefault() as MyModel;
MyModel current = e.CurrentSelection.FirstOrDefault() as MyModel;
//Set the current to the color you want
current.BackgroundTest = "Red";
if (previous != null)
{
//Reset the previous to defaulr color
previous.BackgroundTest = "Gray";
}
}
视图模型:
public class MyViewModel
{
public ObservableCollection<MyModel> samplelist { get; set; }
public MyViewModel()
{
samplelist = new ObservableCollection<MyModel>()
{
new MyModel(){ BackgroundTest="Gray", str="hello1"},
new MyModel(){ BackgroundTest="Gray", str="hello2"},
new MyModel(){ BackgroundTest="Gray", str="hello3"},
new MyModel(){ BackgroundTest="Gray", str="hello4"},
new MyModel(){ BackgroundTest="Gray", str="hello5"},
new MyModel(){ BackgroundTest="Gray", str="hello6"},
new MyModel(){ BackgroundTest="Gray", str="hello7"},
new MyModel(){ BackgroundTest="Gray", str="hello8"},
};
}
}
型号:
public class MyModel : INotifyPropertyChanged
{
public string str { get; set; }
private string _backgroundTest;
public string BackgroundTest
{
get { return _backgroundTest; }
set
{
_backgroundTest = value;
OnPropertyChanged("BackgroundTest");
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = "")
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
更新:
如果您在 DataTemplate 中有 TapGestureRecognizer
,您可以使用 VisualState
而不是 CollectionView 的 SelectionChanged
。
<ContentPage.Resources>
<ResourceDictionary>
<Style TargetType="StackLayout">
<Setter Property="VisualStateManager.VisualStateGroups">
<VisualStateGroupList>
<VisualStateGroup>
<VisualState x:Name="Selected">
<VisualState.Setters>
<Setter Property="BackgroundColor" Value="Accent" />
</VisualState.Setters>
</VisualState>
<VisualState x:Name="UnSelected">
<VisualState.Setters>
<Setter Property="BackgroundColor" Value="Blue" />
</VisualState.Setters>
</VisualState>
</VisualStateGroup>
</VisualStateGroupList>
</Setter>
</Style>
</ResourceDictionary>
</ContentPage.Resources>
Xaml:
<StackLayout.GestureRecognizers>
<TapGestureRecognizer Tapped="TapGestureRecognizer_Tapped"></TapGestureRecognizer>
</StackLayout.GestureRecognizers>
后面的代码:
StackLayout lastElementSelected;
private void TapGestureRecognizer_Tapped(object sender, EventArgs e)
{
if (lastElementSelected != null)
VisualStateManager.GoToState(lastElementSelected, "UnSelected");
VisualStateManager.GoToState((StackLayout)sender, "Selected");
lastElementSelected = (StackLayout)sender;
}
可能有很多方法可以解决你的问题,我不会声称找到了最好的方法,但它仍然是一种(简单的)方法。
我会在下面为您添加一个complete-working-minimal-sample,这正是您想要的,所以请随意copy-paste并根据您的需要进行调整。
实现目标的一种方法是:
- 在 Object 中添加一个名为
Selected
或类似名称的 属性 来填充 Collection (samplelist
) 绑定到您的 CollectionView. - 将 Frame 的 BackgroundColor 属性 绑定到
Selected
属性 并设置为它是一个 转换器,从 布尔值 值(已选择?)变为 颜色(选择颜色). - 然后当 collection 中的项目被点击并且 TapGestureRecognizer 触发时,您可以将所选项目作为 CommandParameter到命令
- 在Command-handler中设置传递项的
Selected
属性为true
。 - 当
Selected
属性变化时,调用Converter,BackgroundColor属性 已更新。
以下示例说明了这一点:
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 CornerRadius="10"
HasShadow="False"
BackgroundColor="{Binding Selected, Converter={x:StaticResource selectedToColorConverter}}"
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
{
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<object>(TriggerScene);
}
public List<item> samplelist { get; set; }
public Boolean isMultiSelect = false;
public Command TriggerSceneCommand { get; set; }
private void TriggerScene(object selectedItem)
{
((item)selectedItem).Selected = !((item)selectedItem).Selected;
if (!isMultiSelect)
{
foreach (item otherItem in samplelist)
{
if (otherItem != selectedItem)
{
otherItem.Selected = false;
}
}
}
}
}
public class item : INotifyPropertyChanged
{
public Boolean _selected;
public Boolean Selected
{
get
{
return _selected;
}
set
{
_selected = value;
OnPropertyChanged();
}
}
public String Text { get; set; }
public String Description { get; set; }
public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChanged([CallerMemberName] string name = "")
{
var propertyChanged = PropertyChanged;
propertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
}
}
public class SelectedToColorConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
return ((Boolean)value) ? Color.Gray : Color.White;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
}
请注意,作为 奖金 有一个名为 isMultiSelect
的 属性,如果为真则允许多个项目to be marked/colored and if false
, then when one item is selected all the others get their Selected
属性 set to false
.
我已经在另一个答案中发布了一种解决问题的方法,现在我想提供一个更简单的解决方案(尽管我不会声称它是最好的)。
注意 在此解决方案中,与我的其他答案相反,您不需要向您的对象添加多余的 属性集合视图,但是新的 属性 是直接在 ViewModel 中定义的。
解决您的问题的一种方法是:
- 在您的 ViewModel 中定义一个名为
SelectedItem
的 属性:这将跟踪当前选定的项目。 - 然后您将 Frame 的
BackgroundColor
绑定到新的 属性:SelectedItem
为此您需要一个 ValueConverter 接受SelectedItem
和ConverterParameter
:当前 Frame. - 在 Frame 中,在 StackLayout 中你有 good old TapGestureRecognizer 其处理程序在调用时将设置 Selected Item.
- 设置 SelectedItem 后,将调用 On属性Changed 并为 CollectionView 中的每个项目调用 ValueConverter。然后,转换器检查框架的 BindingContext(它绑定到的项目!)是否与 SelectedItem 相同,如果是,则将其颜色设置为灰色(选中!)
当然,下面我添加了一个完整的最小工作样本。随意复制粘贴并使用它。
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 的事件处理程序中添加两行代码,以 return 在一段时间(也许是三秒?)后恢复原来的颜色。
只需更改 ViewModel 中的 TriggerScene
方法如下(参见代码注释):
private void TriggerScene(item newSelectedItem)
{
// Highlight selection!
SelectedItem = newSelectedItem;
// Sit and wait...
await Task.Delay(3000);
// Go back to normal!
SelectedItem = null;
}