上下文菜单
单元格可以定义以特定于平台的方式调用的上下文菜单。 这样的上下文菜单通常允许用户对ListView中的特定项执行操作。 例如,当与显示学生的ListView一起使用时,这样的上下文菜单允许用户对特定学生执行动作。
CellContextMenu程序演示了这种技术。 它定义了一个包含四个项的上下文菜单:
- 重置GPA(将学生的平均成绩设置为2.5)
- 移至顶部(将学生移至列表顶部)
- 移动到底部(类似地将学生移到底部)
- 删除(从列表中删除学生)
在iOS上,通过向左滑动项目来调用上下文菜单。 在Android和Windows 10 Mobile上,您将手指按到该项目并按住它直到出现菜单。 这是结果:
iOS屏幕上只显示一个菜单项,这是从列表中删除学生的项目。 必须为iOS特别标记从ListView中删除条目的菜单项。 Android屏幕列出了屏幕顶部的前两个菜单项。 只有Windows运行时才会列出它们。
要查看其他菜单项,请点击iOS上的“更多”按钮和Android上的垂直省略号。 其他项目显示在iOS屏幕底部的列表中以及Android屏幕右上角的下拉列表中:
点击其中一个菜单项执行该操作。
要为单元格创建上下文菜单,可以将MenuItem类型的对象添加到Cell类定义的ContextActions集合中。 你已经遇到过MenuItem。 它是第13章“位图”中描述的ToolbarItem类的基类。
MenuItem定义了五个属性:
- 字符串类型的文本
- FileImageSource类型的图标,用于从平台项目访问位图
- IsDestructive类型为bool
- 类型为ICommand的命令
- 类型为object的CommandParameter
此外,MenuItem定义了Clicked事件。 您可以在Clicked处理程序中处理菜单操作,或者如果菜单操作是在ViewModel-ICommand对象中实现的话。
以下是在CellContextMenu程序中初始化ContextActions集合的方法:
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:school="clr-namespace:SchoolOfFineArt;assembly=SchoolOfFineArt"
x:Class="CellContextMenu.CellContextMenuPage">
<ContentPage.Padding>
<OnPlatform x:TypeArguments="Thickness"
iOS="0, 20, 0, 0" />
</ContentPage.Padding>
<ContentPage.BindingContext>
<school:SchoolViewModel />
</ContentPage.BindingContext>
<StackLayout BindingContext="{Binding StudentBody}">
<Label Text="{Binding School}"
FontSize="Large"
FontAttributes="Bold"
HorizontalTextAlignment="Center" />
<ListView ItemsSource="{Binding Students}">
<ListView.ItemTemplate>
<DataTemplate>
<ImageCell ImageSource="{Binding PhotoFilename}"
Text="{Binding FullName}"
Detail="{Binding GradePointAverage,
StringFormat='G.P.A. = {0:F2}'}">
<ImageCell.ContextActions>
<MenuItem Text="Reset GPA"
Command="{Binding ResetGpaCommand}" />
<MenuItem Text="Move to top"
Command="{Binding MoveToTopCommand}" />
<MenuItem Text="Move to bottom"
Command="{Binding MoveToBottomCommand}" />
<MenuItem Text="Remove"
IsDestructive="True"
Command="{Binding RemoveCommand}" />
</ImageCell.ContextActions>
</ImageCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</StackLayout>
</ContentPage>
请注意,“删除”项的IsDestructive属性设置为True。 这是导致项目在iOS屏幕上以红色显示的属性,按照惯例,该属性会从集合中删除该项目。
MenuItem定义了一个Icon属性,您可以将其设置为存储在平台项目中的位图(非常类似于ToolbarItem使用的图标),但它仅适用于Android,并且位图替换了Text描述。
所有四个MenuItem对象的Command属性都绑定到Student类中的属性。
Student对象是单元格的绑定上下文,因此它也是这些MenuItem对象的绑定上下文。 以下是在Student中定义和初始化属性的方法:
public class Student : ViewModelBase
{
__
public Student()
{
ResetGpaCommand = new Command(() => GradePointAverage = 2.5);
MoveToTopCommand = new Command(() => StudentBody.MoveStudentToTop(this));
MoveToBottomCommand = new Command(() => StudentBody.MoveStudentToBottom(this));
RemoveCommand = new Command(() => StudentBody.RemoveStudent(this));
}
__
// Properties for implementing commands.
[XmlIgnore]
public ICommand ResetGpaCommand { private set; get; }
[XmlIgnore]
public ICommand MoveToTopCommand { private set; get; }
[XmlIgnore]
public ICommand MoveToBottomCommand { private set; get; }
[XmlIgnore]
public ICommand RemoveCommand { private set; get; }
[XmlIgnore]
public StudentBody StudentBody { set; get; }
}
只有ResetGpaCommand可以在Student类中完全处理。 其他三个命令需要访问StudentBody类中的学生集合。 因此,当首次加载数据时,SchoolViewModel将每个Student对象中的StudentBody属性设置为带有学生集合的StudentBody对象。 这允许通过调用StudentBody中的以下方法来实现Move和Remove命令:
public class StudentBody : ViewModelBase
{
__
public void MoveStudentToTop(Student student)
{
Students.Move(Students.IndexOf(student), 0);
}
public void MoveStudentToBottom(Student student)
{
Students.Move(Students.IndexOf(student), Students.Count - 1);
}
public void RemoveStudent(Student student)
{
Students.Remove(student);
}
}
由于Students集合是ObservableCollection,因此ListView重绘自身以反映学生的新数字或新顺序。