定制单元格
当然,很少有人对应用程序的第一个版本感到满意,也许对于简单的EntryForm程序来说也是如此。 修改后的设计要求可能会从PersonalInformation中消除整数Age属性,并将文本AgeRange属性替换为某些固定范围。 另外两个属性被添加到仅与程序员有关的类中:这些是类型字符串的属性,表示程序员的首选计算机语言和平台,可从语言和平台列表中选择。
这是经过修改的ViewModel类,现在称为ProgrammerInformation:
class ProgrammerInformation : ViewModelBase
{
string name, emailAddress, phoneNumber, ageRange;
bool isProgrammer;
string language, platform;
public string Name
{
set { SetProperty(ref name, value); }
get { return name; }
}
public string EmailAddress
{
set { SetProperty(ref emailAddress, value); }
get { return emailAddress; }
}
public string PhoneNumber
{
set { SetProperty(ref phoneNumber, value); }
get { return phoneNumber; }
}
public string AgeRange
{
set { SetProperty(ref ageRange, value); }
get { return ageRange; }
}
public bool IsProgrammer
{
set { SetProperty(ref isProgrammer, value); }
get { return isProgrammer; }
}
public string Language
{
set { SetProperty(ref language, value); }
get { return language; }
}
public string Platform
{
set { SetProperty(ref platform, value); }
get { return platform; }
}
}
AgeRange,Language和Platform属性似乎非常适合Picker,但在TableView中使用Picker需要Picker成为ViewCell的一部分。我们如何做到这一点?
使用ListView时,创建自定义单元格的最简单方法是在XAML中的DataTemplate中定义ViewCell中的可视树。这种方法很有意义,因为您定义的可视化树可能专门针对ListView中的项目进行了定制,并且可能不会在其他地方重用。
您可以在TableView中使用相同的技术,但使用TableView,您更有可能重用特定类型的交互式单元格。例如,ProgrammerInformation类有三个适用于Picker的属性。这意味着创建一个可以在这里和其他地方使用的cus?tom PickerCell类更有意义。
Xamarin.FormsBook.Toolkit库包含一个派生自ViewCell的PickerCell类,它基本上是一个Picker视图的包装器。该类由XAML文件和代码隐藏文件组成。代码隐藏文件定义了三个由可绑定属性支持的属性:Label(标识单元格,就像EntryCell中的Label属性一样),Title(对应于Picker的Title属性)和SelectedValue,它是在中选择的实际字符串选择器。此外,get-only Items属性公开Picker的Items集合:
namespace Xamarin.FormsBook.Toolkit
{
[ContentProperty("Items")]
public partial class PickerCell : ViewCell
{
public static readonly BindableProperty LabelProperty =
BindableProperty.Create(
"Label", typeof(string), typeof(PickerCell), default(string));
public static readonly BindableProperty TitleProperty =
BindableProperty.Create(
"Title", typeof(string), typeof(PickerCell), default(string));
public static readonly BindableProperty SelectedValueProperty =
BindableProperty.Create(
"SelectedValue", typeof(string), typeof(PickerCell), null,
BindingMode.TwoWay,
propertyChanged: (sender, oldValue, newValue) =>
{
PickerCell pickerCell = (PickerCell)sender;
if (String.IsNullOrEmpty(newValue))
{
pickerCell.picker.SelectedIndex = -1;
}
else
{
pickerCell.picker.SelectedIndex =
pickerCell.Items.IndexOf(newValue);
}
});
public PickerCell()
{
InitializeComponent();
}
public string Label
{
set { SetValue(LabelProperty, value); }
get { return (string)GetValue(LabelProperty); }
}
public string Title
{
get { return (string)GetValue(TitleProperty); }
set { SetValue(TitleProperty, value); }
}
public string SelectedValue
{
get { return (string)GetValue(SelectedValueProperty); }
set { SetValue(SelectedValueProperty, value); }
}
// Items property.
public IList<string> Items
{
get { return picker.Items; }
}
void OnPickerSelectedIndexChanged(object sender, EventArgs args)
{
if (picker.SelectedIndex == -1)
{
SelectedValue = null;
}
else
{
SelectedValue = Items[picker.SelectedIndex];
}
}
}
}
XAML文件定义了PickerCell的可视树,它只包含一个标识Label和Picker本身。 请注意,XAML文件的根元素是ViewCell,它是PickerCell派生自的类:
<ViewCell xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="Xamarin.FormsBook.Toolkit.PickerCell"
x:Name="cell">
<ViewCell.View>
<StackLayout Orientation="Horizontal"
BindingContext="{x:Reference cell}"
Padding="16, 0">
<Label Text="{Binding Label}"
VerticalOptions="Center" />
<Picker x:Name="picker"
Title="{Binding Title}"
VerticalOptions="Center"
HorizontalOptions="FillAndExpand"
SelectedIndexChanged="OnPickerSelectedIndexChanged" />
</StackLayout>
</ViewCell.View>
</ViewCell>
StackLayout上设置的Padding值是根据经验选择的,与Xamarin.Forms EntryCell在视觉上一致。
通常,此XAML文件中不需要ViewCell.View属性元素标记,因为View是ViewCell的content属性。但是,代码隐藏文件将PickerCell的content属性定义为Items集合,这意味着content属性不再是View,而ViewCell.View标记是必需的。
XAML文件的根元素具有x:Name属性,该属性为对象提供名称“cell”,StackLayout将其BindingContext设置为该对象,这意味着StackLayout的子节点的BindingContext是PickerCell实例本身。这允许Label和Picker包含对代码隐藏文件中PickerCell定义的Label和Title属性的绑定。
Picker触发在代码隐藏文件中处理的SelectedIndexChanged事件,以便代码隐藏文件可以将Picker的SelectedIndex转换为PickerCell的SelectedValue。
这不是创建自定义PickerCell类的唯一方法。您还可以通过为每个平台定义单独的PickerCellRenderer类来创建它。
ConditionalCells程序中的TableView将此PickerCell用于ProgrammerInformation类中的三个属性,并使用字符串集合初始化每个PickerCell:
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:ConditionalCells"
xmlns:toolkit=
"clr-namespace:Xamarin.FormsBook.Toolkit;assembly=Xamarin.FormsBook.Toolkit"
x:Class="ConditionalCells.ConditionalCellsPage">
<ContentPage.Padding>
<OnPlatform x:TypeArguments="Thickness"
iOS="0, 20, 0, 0" />
</ContentPage.Padding>
<StackLayout>
<TableView Intent="Form">
<TableView.BindingContext>
<local:ProgrammerInformation />
</TableView.BindingContext>
<TableRoot Title="Data Form">
<TableSection Title="Personal Information">
<EntryCell Label="Name:"
Text="{Binding Name}"
Placeholder="Enter name"
Keyboard="Text" />
<EntryCell Label="Email:"
Text="{Binding EmailAddress}"
Placeholder="Enter email address"
Keyboard="Email" />
<EntryCell Label="Phone:"
Text="{Binding PhoneNumber}"
Placeholder="Enter phone number"
Keyboard="Telephone" />
<toolkit:PickerCell Label="Age Range:"
Title="Age Range"
SelectedValue="{Binding AgeRange}">
<x:String>10 - 19</x:String>
<x:String>20 - 29</x:String>
<x:String>30 - 39</x:String>
<x:String>40 - 49</x:String>
<x:String>50 - 59</x:String>
<x:String>60 - 99</x:String>
</toolkit:PickerCell>
<SwitchCell Text="Are you a programmer?"
On="{Binding IsProgrammer}" />
<toolkit:PickerCell Label="Language:"
Title="Language"
IsEnabled="{Binding IsProgrammer}"
SelectedValue="{Binding Language}">
<x:String>C</x:String>
<x:String>C++</x:String>
<x:String>C#</x:String>
<x:String>Objective C</x:String>
<x:String>Java</x:String>
<x:String>Other</x:String>
</toolkit:PickerCell>
<toolkit:PickerCell Label="Platform:"
Title="Platform"
IsEnabled="{Binding IsProgrammer}"
SelectedValue="{Binding Platform}">
<x:String>iPhone</x:String>
<x:String>Android</x:String>
<x:String>Windows Phone</x:String>
<x:String>Other</x:String>
</toolkit:PickerCell>
</TableSection>
</TableRoot>
</TableView>
</StackLayout>
</ContentPage>
请注意PickerCell的IsEnabled属性如何与平台和语言属性绑定到IsProgrammer属性,这意味着除非打开SwitchCell并且IsProgrammer属性为true,否则应禁用这些单元格。 这就是为什么这个程序被称为ConditionalCells。
但是,它似乎不起作用,因为此屏幕截图验证:
即使IsProgrammer开关关闭,并且最后两个PickerCell元素中的每一个的IsEnabled属性都设置为false,这些元素仍然响应并允许选择值。 此外,PickerCell在Windows 10移动平台上看起来不太好用。
那么让我们尝试另一种方法。