Xamarin.Forms MVVM TapGestureRecognizer 到 ListView 的 ViewCell 中的标签(在部分文件中)
Xamarin.Forms MVVM TapGestureRecognizer to a Label in a ViewCell of ListView (in partial files)
我已经搜索了很多关于这个问题的信息,坦率地说,我对这个问题很满意。我有一个聊天应用程序。在这个应用程序上有一个视图,其中有来自我和其他聊天成员的消息。从技术上讲,它是一个带有 ItemTemplate 的 ListView,具有 class 绑定 (DataTemplateSelector),其中 returns ViewCells 基于规则(无论要显示的消息是我的还是其他的)
消息(入站或出站)在单独的文件中。
目前,TapGestureRecognizer 没有工作,命令 ChooseNameToMentionCommand 没有触发
有很多“类似”问题,其中 TapGestureRecognizer 无法像这样在 ListView 上工作:
TapGestureRecognizer not working inside ListView
Command not working 那里(以及任何其他相关主题)的答案是:
- 在您的命令绑定上使用
Source={x:Reference MessagesListView}
但是当我使用这个建议时,我的结尾是:
Xamarin.Forms.Xaml.XamlParseException: 'Position 30:21. Can not find the object referenced by MessagesListView'
我如何在我的案例中使用它(ViewCell 在单独的文件中定义)重要说明 - 我正在使用 MVVM 方法并且不想在 ViewCell 的代码隐藏中做任何事情(然后我什至可以使用 Tapped 事件。我已经测试过了。当然这种方法有效:) )
这是我的代码:
MainViewModel.cs
public class MainViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public ObservableRangeCollection<MessageModel> Messages { get; set; }
public Command ChooseNameToMentionCommand { get; set; }
public string NewMessage {get; set;}
public MainViewModel()
{
Messages = new ObservableRangeCollection<MessageModel>();
ChooseNameToMentionCommand = new Command<string>(async (t) => await ChooseNameToMention(t));
}
private Task ChooseNameToMention(string name)
{
this.NewMessage += $"@{name}";
}
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = "")
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
}
}
MainPage.xaml
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage
x:Class="Chat.ClientLibrary.MainPage"
xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:converters="clr-namespace:Chat.ClientLibrary.Converters"
xmlns:local="clr-namespace:Chat.ClientLibrary.CustomCells"
xmlns:partials="clr-namespace:Chat.ClientLibrary.Views.Partials"
BackgroundColor="White"
x:Name="MainChatPage">
<ContentPage.Resources>
<ResourceDictionary>
<local:MyDataTemplateSelector x:Key="MessageTemplateSelector"/>
</ResourceDictionary>
</ContentPage.Resources>
/* REMOVED UNNECESSARY code */
<Grid RowSpacing="0" ColumnSpacing="0">
<Grid.RowDefinitions>
<RowDefinition Height="50" />
<RowDefinition Height="*" />
<RowDefinition Height="1" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<ListView
Grid.Row="1"
FlowDirection="RightToLeft"
Rotation="180"
x:Name="MessagesListView"
ItemTemplate="{StaticResource MessageTemplateSelector}"
ItemsSource="{Binding Messages}"
HasUnevenRows="True"
ItemSelected="MyListView_OnItemSelected"
ItemTapped="MyListView_OnItemTapped"
SeparatorVisibility="None" />
/* REMOVED UNNECESSARY code */
</Grid>
</ContentPage>
MyDataTemplateSelector.cs
class MyDataTemplateSelector : DataTemplateSelector
{
public MyDataTemplateSelector()
{
// Retain instances!
this.incomingDataTemplate = new DataTemplate(typeof(IncomingViewCell));
this.outgoingDataTemplate = new DataTemplate(typeof(OutgoingViewCell));
}
protected override DataTemplate OnSelectTemplate(object item, BindableObject container)
{
var messageVm = item as MessageModel;
if (messageVm == null)
return null;
return messageVm.IsOwnMessage ? this.incomingDataTemplate : this.outgoingDataTemplate;
}
private readonly DataTemplate incomingDataTemplate;
private readonly DataTemplate outgoingDataTemplate;
}
IncomingViewCell.xaml
(我不会postOutgoingViewCell - 几乎一样;)颜色不同)
<?xml version="1.0" encoding="utf-8" ?>
<ViewCell xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="Chat.ClientLibrary.Views.CustomCells.IncomingViewCell"
xmlns:forms9patch="clr-namespace:Forms9Patch;assembly=Forms9Patch">
<Grid ColumnSpacing="2"
Padding="5"
FlowDirection="LeftToRight"
Rotation="180"
>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="40"></ColumnDefinition>
<ColumnDefinition Width="*"></ColumnDefinition>
<ColumnDefinition Width="40"></ColumnDefinition>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"></RowDefinition>
<RowDefinition Height="*"></RowDefinition>
<RowDefinition Height="Auto"></RowDefinition>
</Grid.RowDefinitions>
<Label Grid.Row="0" Grid.Column="1" HorizontalTextAlignment="Start" Text="{Binding UserName}" TextColor="Blue">
<Label.GestureRecognizers>
<TapGestureRecognizer
Command="{Binding Path= BindingContext.ChooseNameToMentionCommand, Source={x:Reference MessagesListView}}" CommandParameter="{Binding UserName}" />
</Label.GestureRecognizers>
</Label>
/* REMOVED UNNECESSARY code */
</Grid>
</ViewCell>
[编辑 12:12 2020 年 10 月 1 日]
我忘了放这里 MainPage.cs 所以这里是:
MainPage.cs
public partial class MainPage : ContentPage
{
MainViewModel vm;
public MainPage()
{
this.BindingContext = vm = new MainViewModel();
InitializeComponent();
}
void MyListView_OnItemSelected(object sender, SelectedItemChangedEventArgs e)
{
MessagesListView.SelectedItem = null;
}
void MyListView_OnItemTapped(object sender, ItemTappedEventArgs e)
{
MessagesListView.SelectedItem = null;
}
}
添加了 ListOnItemTapped 事件(我忘记了 - 因为它取自一些聊天示例。但我认为它不会破坏任何东西。当我直接为标签添加 OnTapped - 它成功了。
由于缺少代码,我做了一个类似的例子供大家参考,在ListView ViewCell中使用TapGestureRecognizer
。
Xaml:
<ContentPage
x:Class="Selector.HomePage"
xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:Selector"
x:Name="MainPage">
<ContentPage.Resources>
<ResourceDictionary>
<DataTemplate x:Key="validPersonTemplate">
<ViewCell>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="0.4*" />
<ColumnDefinition Width="0.3*" />
<ColumnDefinition Width="0.3*" />
</Grid.ColumnDefinitions>
<Label
FontAttributes="Bold"
Text="{Binding Name}"
TextColor="Green">
<Label.GestureRecognizers>
<TapGestureRecognizer Command="{Binding Path=BindingContext.TapCommand, Source={x:Reference MainPage}}" CommandParameter="false" />
</Label.GestureRecognizers>
</Label>
<Label
Grid.Column="1"
Text="{Binding DateOfBirth, StringFormat='{0:d}'}"
TextColor="Green" />
<Label
Grid.Column="2"
HorizontalTextAlignment="End"
Text="{Binding Location}"
TextColor="Green" />
</Grid>
</ViewCell>
</DataTemplate>
<DataTemplate x:Key="invalidPersonTemplate">
<ViewCell>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="0.4*" />
<ColumnDefinition Width="0.3*" />
<ColumnDefinition Width="0.3*" />
</Grid.ColumnDefinitions>
<Label
FontAttributes="Bold"
Text="{Binding Name}"
TextColor="Red">
<Label.GestureRecognizers>
<TapGestureRecognizer Command="{Binding Path=BindingContext.TapCommand, Source={x:Reference MainPage}}" CommandParameter="false" />
</Label.GestureRecognizers>
</Label>
<Label
Grid.Column="1"
Text="{Binding DateOfBirth, StringFormat='{0:d}'}"
TextColor="Red" />
<Label
Grid.Column="2"
HorizontalTextAlignment="End"
Text="{Binding Location}"
TextColor="Red" />
</Grid>
</ViewCell>
</DataTemplate>
<local:PersonDataTemplateSelector
x:Key="personDataTemplateSelector"
InvalidTemplate="{StaticResource invalidPersonTemplate}"
ValidTemplate="{StaticResource validPersonTemplate}" />
</ResourceDictionary>
</ContentPage.Resources>
<StackLayout Margin="20">
<Label
FontAttributes="Bold"
HorizontalOptions="Center"
Text="ListView with a DataTemplateSelector" />
<ListView
x:Name="listView"
Margin="0,20,0,0"
ItemTemplate="{StaticResource personDataTemplateSelector}" />
</StackLayout>
</ContentPage>
PersonDataTemplateSelector.cs:
public class PersonDataTemplateSelector : DataTemplateSelector
{
public DataTemplate ValidTemplate { get; set; }
public DataTemplate InvalidTemplate { get; set; }
protected override DataTemplate OnSelectTemplate (object item, BindableObject container)
{
return ((Person)item).DateOfBirth.Year >= 1980 ? ValidTemplate : InvalidTemplate;
}
}
Person.cs:
public class Person
{
public string Name { get; set; }
public DateTime DateOfBirth { get; set; }
public string Location { get; set; }
}
代码隐藏:
public Command TapCommand
{
get
{
return new Command(val =>
{
DisplayAlert("Alert", val.ToString(), "OK");
});
}
}
public HomePage()
{
InitializeComponent();
var people = new List<Person>
{
new Person { Name = "Kath", DateOfBirth = new DateTime(1985, 11, 20), Location = "France" },
new Person { Name = "Steve", DateOfBirth = new DateTime(1975, 1, 15), Location = "USA" },
new Person { Name = "Lucas", DateOfBirth = new DateTime(1988, 2, 5), Location = "Germany" },
new Person { Name = "John", DateOfBirth = new DateTime(1976, 2, 20), Location = "USA" },
new Person { Name = "Tariq", DateOfBirth = new DateTime(1987, 1, 10), Location = "UK" },
new Person { Name = "Jane", DateOfBirth = new DateTime(1982, 8, 30), Location = "USA" },
new Person { Name = "Tom", DateOfBirth = new DateTime(1977, 3, 10), Location = "UK" }
};
listView.ItemsSource = people;
this.BindingContext = this;
}
截图:
更新:
使用资源字典创建单独的文件。我更改了这两个文件中命令的绑定路径。
MyResourceDictionary.xaml:
<?xml version="1.0" encoding="UTF-8" ?>
<ResourceDictionary
xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:Selector">
<DataTemplate x:Key="validPersonTemplate">
<ViewCell x:Name="MyCell">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="0.4*" />
<ColumnDefinition Width="0.3*" />
<ColumnDefinition Width="0.3*" />
</Grid.ColumnDefinitions>
<Label
FontAttributes="Bold"
Text="{Binding Name}"
TextColor="Green">
<Label.GestureRecognizers>
<TapGestureRecognizer Command="{Binding Path=Parent.BindingContext.TapCommand, Source={x:Reference MyCell}}" CommandParameter="false" />
</Label.GestureRecognizers>
</Label>
<Label
Grid.Column="1"
Text="{Binding DateOfBirth, StringFormat='{0:d}'}"
TextColor="Green" />
<Label
Grid.Column="2"
HorizontalTextAlignment="End"
Text="{Binding Location}"
TextColor="Green" />
</Grid>
</ViewCell>
</DataTemplate>
</ResourceDictionary>
MyResourceDictionary2.xaml:
<?xml version="1.0" encoding="UTF-8" ?>
<ResourceDictionary xmlns="http://xamarin.com/schemas/2014/forms" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml">
<DataTemplate x:Key="invalidPersonTemplate">
<ViewCell x:Name="MyCell2">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="0.4*" />
<ColumnDefinition Width="0.3*" />
<ColumnDefinition Width="0.3*" />
</Grid.ColumnDefinitions>
<Label
FontAttributes="Bold"
Text="{Binding Name}"
TextColor="Red">
<Label.GestureRecognizers>
<TapGestureRecognizer Command="{Binding Path=Parent.BindingContext.TapCommand, Source={x:Reference MyCell2}}" CommandParameter="false" />
</Label.GestureRecognizers>
</Label>
<Label
Grid.Column="1"
Text="{Binding DateOfBirth, StringFormat='{0:d}'}"
TextColor="Red" />
<Label
Grid.Column="2"
HorizontalTextAlignment="End"
Text="{Binding Location}"
TextColor="Red" />
</Grid>
</ViewCell>
</DataTemplate>
</ResourceDictionary>
更改内容页面:
<?xml version="1.0" encoding="UTF-8" ?>
<ContentPage
x:Class="Selector.HomePage"
xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:Selector">
<ContentPage.Resources>
<ResourceDictionary>
<ResourceDictionary Source="MyResourceDictionary.xaml" />
<ResourceDictionary Source="MyResourceDictionary2.xaml" />
<local:PersonDataTemplateSelector
x:Key="personDataTemplateSelector"
InvalidTemplate="{StaticResource invalidPersonTemplate}"
ValidTemplate="{StaticResource validPersonTemplate}" />
</ResourceDictionary>
</ContentPage.Resources>
<StackLayout Margin="20">
<Label
FontAttributes="Bold"
HorizontalOptions="Center"
Text="ListView with a DataTemplateSelector" />
<ListView
x:Name="listView"
Margin="0,20,0,0"
ItemTemplate="{StaticResource personDataTemplateSelector}" />
</StackLayout>
</ContentPage>
选择器和视图模型没有变化,请检查。如果您对此问题仍有疑问,请随时告诉我。
添加一个classViewModelLocator,我用的是MVVM Light
public class ViewModelLocator
{
public ViewModelLocator()
{
ServiceLocator.SetLocatorProvider(() => SimpleIoc.Default);
SimpleIoc.Default.Register<MainViewModel>();
}
public MainViewModel MainVM
{
get { return ServiceLocator.Current.GetInstance<MainViewModel>(); }
}
}
那么你可以在不使用页面引用的情况下使用 BindingContext
BindingContext="{Binding Path=MainVM, Source={StaticResource VMLocator}}"
App.Xaml代码
xmlns:vm="clr-namespace:xxx.xx.ViewModels"
<Application.Resources>
<vm:ViewModelLocator x:Key="VMLocator" />
</Application.Resources>
用法
选项 1:
您想在列表视图中绑定一个标签,但列表视图的绑定上下文指向一个集合,请使用此方法。
<Label.GestureRecognizers>
<TapGestureRecognizer Command="{Binding YourVM.YourCommand,Source={StaticResource VMLocator}}" CommandParameter="{Binding UserName}" />
</Label.GestureRecognizers>
选项 2:
您想将其绑定到当前页面的 Viewmodel(带有页面引用)
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="xxxx.xx.xx.App"
x:Name="MyViewName">
<Label Text="My Text" IsVisible="{Binding Path=BindingContext.IsLoading,Source={x:Reference MyViewName},Mode=TwoWay}"/>
选项 2:
我已经搜索了很多关于这个问题的信息,坦率地说,我对这个问题很满意。我有一个聊天应用程序。在这个应用程序上有一个视图,其中有来自我和其他聊天成员的消息。从技术上讲,它是一个带有 ItemTemplate 的 ListView,具有 class 绑定 (DataTemplateSelector),其中 returns ViewCells 基于规则(无论要显示的消息是我的还是其他的)
消息(入站或出站)在单独的文件中。
目前,TapGestureRecognizer 没有工作,命令 ChooseNameToMentionCommand 没有触发
有很多“类似”问题,其中 TapGestureRecognizer 无法像这样在 ListView 上工作:
TapGestureRecognizer not working inside ListView
Command not working 那里(以及任何其他相关主题)的答案是:
- 在您的命令绑定上使用
Source={x:Reference MessagesListView}
但是当我使用这个建议时,我的结尾是:
Xamarin.Forms.Xaml.XamlParseException: 'Position 30:21. Can not find the object referenced by MessagesListView'
我如何在我的案例中使用它(ViewCell 在单独的文件中定义)重要说明 - 我正在使用 MVVM 方法并且不想在 ViewCell 的代码隐藏中做任何事情(然后我什至可以使用 Tapped 事件。我已经测试过了。当然这种方法有效:) )
这是我的代码:
MainViewModel.cs
public class MainViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public ObservableRangeCollection<MessageModel> Messages { get; set; }
public Command ChooseNameToMentionCommand { get; set; }
public string NewMessage {get; set;}
public MainViewModel()
{
Messages = new ObservableRangeCollection<MessageModel>();
ChooseNameToMentionCommand = new Command<string>(async (t) => await ChooseNameToMention(t));
}
private Task ChooseNameToMention(string name)
{
this.NewMessage += $"@{name}";
}
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = "")
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
}
}
MainPage.xaml
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage
x:Class="Chat.ClientLibrary.MainPage"
xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:converters="clr-namespace:Chat.ClientLibrary.Converters"
xmlns:local="clr-namespace:Chat.ClientLibrary.CustomCells"
xmlns:partials="clr-namespace:Chat.ClientLibrary.Views.Partials"
BackgroundColor="White"
x:Name="MainChatPage">
<ContentPage.Resources>
<ResourceDictionary>
<local:MyDataTemplateSelector x:Key="MessageTemplateSelector"/>
</ResourceDictionary>
</ContentPage.Resources>
/* REMOVED UNNECESSARY code */
<Grid RowSpacing="0" ColumnSpacing="0">
<Grid.RowDefinitions>
<RowDefinition Height="50" />
<RowDefinition Height="*" />
<RowDefinition Height="1" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<ListView
Grid.Row="1"
FlowDirection="RightToLeft"
Rotation="180"
x:Name="MessagesListView"
ItemTemplate="{StaticResource MessageTemplateSelector}"
ItemsSource="{Binding Messages}"
HasUnevenRows="True"
ItemSelected="MyListView_OnItemSelected"
ItemTapped="MyListView_OnItemTapped"
SeparatorVisibility="None" />
/* REMOVED UNNECESSARY code */
</Grid>
</ContentPage>
MyDataTemplateSelector.cs
class MyDataTemplateSelector : DataTemplateSelector
{
public MyDataTemplateSelector()
{
// Retain instances!
this.incomingDataTemplate = new DataTemplate(typeof(IncomingViewCell));
this.outgoingDataTemplate = new DataTemplate(typeof(OutgoingViewCell));
}
protected override DataTemplate OnSelectTemplate(object item, BindableObject container)
{
var messageVm = item as MessageModel;
if (messageVm == null)
return null;
return messageVm.IsOwnMessage ? this.incomingDataTemplate : this.outgoingDataTemplate;
}
private readonly DataTemplate incomingDataTemplate;
private readonly DataTemplate outgoingDataTemplate;
}
IncomingViewCell.xaml
(我不会postOutgoingViewCell - 几乎一样;)颜色不同)
<?xml version="1.0" encoding="utf-8" ?>
<ViewCell xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="Chat.ClientLibrary.Views.CustomCells.IncomingViewCell"
xmlns:forms9patch="clr-namespace:Forms9Patch;assembly=Forms9Patch">
<Grid ColumnSpacing="2"
Padding="5"
FlowDirection="LeftToRight"
Rotation="180"
>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="40"></ColumnDefinition>
<ColumnDefinition Width="*"></ColumnDefinition>
<ColumnDefinition Width="40"></ColumnDefinition>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"></RowDefinition>
<RowDefinition Height="*"></RowDefinition>
<RowDefinition Height="Auto"></RowDefinition>
</Grid.RowDefinitions>
<Label Grid.Row="0" Grid.Column="1" HorizontalTextAlignment="Start" Text="{Binding UserName}" TextColor="Blue">
<Label.GestureRecognizers>
<TapGestureRecognizer
Command="{Binding Path= BindingContext.ChooseNameToMentionCommand, Source={x:Reference MessagesListView}}" CommandParameter="{Binding UserName}" />
</Label.GestureRecognizers>
</Label>
/* REMOVED UNNECESSARY code */
</Grid>
</ViewCell>
[编辑 12:12 2020 年 10 月 1 日] 我忘了放这里 MainPage.cs 所以这里是:
MainPage.cs
public partial class MainPage : ContentPage
{
MainViewModel vm;
public MainPage()
{
this.BindingContext = vm = new MainViewModel();
InitializeComponent();
}
void MyListView_OnItemSelected(object sender, SelectedItemChangedEventArgs e)
{
MessagesListView.SelectedItem = null;
}
void MyListView_OnItemTapped(object sender, ItemTappedEventArgs e)
{
MessagesListView.SelectedItem = null;
}
}
添加了 ListOnItemTapped 事件(我忘记了 - 因为它取自一些聊天示例。但我认为它不会破坏任何东西。当我直接为标签添加 OnTapped - 它成功了。
由于缺少代码,我做了一个类似的例子供大家参考,在ListView ViewCell中使用TapGestureRecognizer
。
Xaml:
<ContentPage
x:Class="Selector.HomePage"
xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:Selector"
x:Name="MainPage">
<ContentPage.Resources>
<ResourceDictionary>
<DataTemplate x:Key="validPersonTemplate">
<ViewCell>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="0.4*" />
<ColumnDefinition Width="0.3*" />
<ColumnDefinition Width="0.3*" />
</Grid.ColumnDefinitions>
<Label
FontAttributes="Bold"
Text="{Binding Name}"
TextColor="Green">
<Label.GestureRecognizers>
<TapGestureRecognizer Command="{Binding Path=BindingContext.TapCommand, Source={x:Reference MainPage}}" CommandParameter="false" />
</Label.GestureRecognizers>
</Label>
<Label
Grid.Column="1"
Text="{Binding DateOfBirth, StringFormat='{0:d}'}"
TextColor="Green" />
<Label
Grid.Column="2"
HorizontalTextAlignment="End"
Text="{Binding Location}"
TextColor="Green" />
</Grid>
</ViewCell>
</DataTemplate>
<DataTemplate x:Key="invalidPersonTemplate">
<ViewCell>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="0.4*" />
<ColumnDefinition Width="0.3*" />
<ColumnDefinition Width="0.3*" />
</Grid.ColumnDefinitions>
<Label
FontAttributes="Bold"
Text="{Binding Name}"
TextColor="Red">
<Label.GestureRecognizers>
<TapGestureRecognizer Command="{Binding Path=BindingContext.TapCommand, Source={x:Reference MainPage}}" CommandParameter="false" />
</Label.GestureRecognizers>
</Label>
<Label
Grid.Column="1"
Text="{Binding DateOfBirth, StringFormat='{0:d}'}"
TextColor="Red" />
<Label
Grid.Column="2"
HorizontalTextAlignment="End"
Text="{Binding Location}"
TextColor="Red" />
</Grid>
</ViewCell>
</DataTemplate>
<local:PersonDataTemplateSelector
x:Key="personDataTemplateSelector"
InvalidTemplate="{StaticResource invalidPersonTemplate}"
ValidTemplate="{StaticResource validPersonTemplate}" />
</ResourceDictionary>
</ContentPage.Resources>
<StackLayout Margin="20">
<Label
FontAttributes="Bold"
HorizontalOptions="Center"
Text="ListView with a DataTemplateSelector" />
<ListView
x:Name="listView"
Margin="0,20,0,0"
ItemTemplate="{StaticResource personDataTemplateSelector}" />
</StackLayout>
</ContentPage>
PersonDataTemplateSelector.cs:
public class PersonDataTemplateSelector : DataTemplateSelector
{
public DataTemplate ValidTemplate { get; set; }
public DataTemplate InvalidTemplate { get; set; }
protected override DataTemplate OnSelectTemplate (object item, BindableObject container)
{
return ((Person)item).DateOfBirth.Year >= 1980 ? ValidTemplate : InvalidTemplate;
}
}
Person.cs:
public class Person
{
public string Name { get; set; }
public DateTime DateOfBirth { get; set; }
public string Location { get; set; }
}
代码隐藏:
public Command TapCommand
{
get
{
return new Command(val =>
{
DisplayAlert("Alert", val.ToString(), "OK");
});
}
}
public HomePage()
{
InitializeComponent();
var people = new List<Person>
{
new Person { Name = "Kath", DateOfBirth = new DateTime(1985, 11, 20), Location = "France" },
new Person { Name = "Steve", DateOfBirth = new DateTime(1975, 1, 15), Location = "USA" },
new Person { Name = "Lucas", DateOfBirth = new DateTime(1988, 2, 5), Location = "Germany" },
new Person { Name = "John", DateOfBirth = new DateTime(1976, 2, 20), Location = "USA" },
new Person { Name = "Tariq", DateOfBirth = new DateTime(1987, 1, 10), Location = "UK" },
new Person { Name = "Jane", DateOfBirth = new DateTime(1982, 8, 30), Location = "USA" },
new Person { Name = "Tom", DateOfBirth = new DateTime(1977, 3, 10), Location = "UK" }
};
listView.ItemsSource = people;
this.BindingContext = this;
}
截图:
更新:
使用资源字典创建单独的文件。我更改了这两个文件中命令的绑定路径。
MyResourceDictionary.xaml:
<?xml version="1.0" encoding="UTF-8" ?>
<ResourceDictionary
xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:Selector">
<DataTemplate x:Key="validPersonTemplate">
<ViewCell x:Name="MyCell">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="0.4*" />
<ColumnDefinition Width="0.3*" />
<ColumnDefinition Width="0.3*" />
</Grid.ColumnDefinitions>
<Label
FontAttributes="Bold"
Text="{Binding Name}"
TextColor="Green">
<Label.GestureRecognizers>
<TapGestureRecognizer Command="{Binding Path=Parent.BindingContext.TapCommand, Source={x:Reference MyCell}}" CommandParameter="false" />
</Label.GestureRecognizers>
</Label>
<Label
Grid.Column="1"
Text="{Binding DateOfBirth, StringFormat='{0:d}'}"
TextColor="Green" />
<Label
Grid.Column="2"
HorizontalTextAlignment="End"
Text="{Binding Location}"
TextColor="Green" />
</Grid>
</ViewCell>
</DataTemplate>
</ResourceDictionary>
MyResourceDictionary2.xaml:
<?xml version="1.0" encoding="UTF-8" ?>
<ResourceDictionary xmlns="http://xamarin.com/schemas/2014/forms" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml">
<DataTemplate x:Key="invalidPersonTemplate">
<ViewCell x:Name="MyCell2">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="0.4*" />
<ColumnDefinition Width="0.3*" />
<ColumnDefinition Width="0.3*" />
</Grid.ColumnDefinitions>
<Label
FontAttributes="Bold"
Text="{Binding Name}"
TextColor="Red">
<Label.GestureRecognizers>
<TapGestureRecognizer Command="{Binding Path=Parent.BindingContext.TapCommand, Source={x:Reference MyCell2}}" CommandParameter="false" />
</Label.GestureRecognizers>
</Label>
<Label
Grid.Column="1"
Text="{Binding DateOfBirth, StringFormat='{0:d}'}"
TextColor="Red" />
<Label
Grid.Column="2"
HorizontalTextAlignment="End"
Text="{Binding Location}"
TextColor="Red" />
</Grid>
</ViewCell>
</DataTemplate>
</ResourceDictionary>
更改内容页面:
<?xml version="1.0" encoding="UTF-8" ?>
<ContentPage
x:Class="Selector.HomePage"
xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:Selector">
<ContentPage.Resources>
<ResourceDictionary>
<ResourceDictionary Source="MyResourceDictionary.xaml" />
<ResourceDictionary Source="MyResourceDictionary2.xaml" />
<local:PersonDataTemplateSelector
x:Key="personDataTemplateSelector"
InvalidTemplate="{StaticResource invalidPersonTemplate}"
ValidTemplate="{StaticResource validPersonTemplate}" />
</ResourceDictionary>
</ContentPage.Resources>
<StackLayout Margin="20">
<Label
FontAttributes="Bold"
HorizontalOptions="Center"
Text="ListView with a DataTemplateSelector" />
<ListView
x:Name="listView"
Margin="0,20,0,0"
ItemTemplate="{StaticResource personDataTemplateSelector}" />
</StackLayout>
</ContentPage>
选择器和视图模型没有变化,请检查。如果您对此问题仍有疑问,请随时告诉我。
添加一个classViewModelLocator,我用的是MVVM Light
public class ViewModelLocator
{
public ViewModelLocator()
{
ServiceLocator.SetLocatorProvider(() => SimpleIoc.Default);
SimpleIoc.Default.Register<MainViewModel>();
}
public MainViewModel MainVM
{
get { return ServiceLocator.Current.GetInstance<MainViewModel>(); }
}
}
那么你可以在不使用页面引用的情况下使用 BindingContext
BindingContext="{Binding Path=MainVM, Source={StaticResource VMLocator}}"
App.Xaml代码
xmlns:vm="clr-namespace:xxx.xx.ViewModels"
<Application.Resources>
<vm:ViewModelLocator x:Key="VMLocator" />
</Application.Resources>
用法
选项 1:
您想在列表视图中绑定一个标签,但列表视图的绑定上下文指向一个集合,请使用此方法。
<Label.GestureRecognizers>
<TapGestureRecognizer Command="{Binding YourVM.YourCommand,Source={StaticResource VMLocator}}" CommandParameter="{Binding UserName}" />
</Label.GestureRecognizers>
选项 2:
您想将其绑定到当前页面的 Viewmodel(带有页面引用)
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="xxxx.xx.xx.App"
x:Name="MyViewName">
<Label Text="My Text" IsVisible="{Binding Path=BindingContext.IsLoading,Source={x:Reference MyViewName},Mode=TwoWay}"/>
选项 2: