图形用户界面的组件可以大致分为用于向用户显示信息的呈现和用于从用户获得输入的交互的视图。 虽然标签是最基本的演示视图,但Button可能是原型交互视图。 按钮发出一个命令。 这是用户告诉程序启动一些活动的方式 - 做一些事情。
Xamarin.Forms按钮显示文本,带或不带相应的图像。 (本章仅描述文本按钮;第13章“位图”中介绍了向按钮添加图像。)当用户的手指按下某个按钮时,按钮会稍微改变其外观以向用户提供反馈。 当手指被释放时,按钮会触发Clicked事件。 Clicked处理程序的两个参数是Xamarin.Forms事件处理程序的典型代码:
- 第一个参数是发起事件的对象。 对于Clicked处理程序,这是被点击的特定Button对象。
- 第二个参数有时会提供有关该事件的更多信息。 对于Clicked事件,第二个参数只是一个EventArgs对象,它不提供额外的信息。
一旦应用程序开始实现用户交互,就会出现一些特殊需求:如果程序恰好在用户完成工作之前终止,那么应用程序应该努力保存交互的结果。 因此,本章还讨论了应用程序如何保存临时数据,特别是在应用程序生命周期事件的情况下。 这些在“保存瞬态数据”一节中进行了描述。
处理点击
这里有一个名为ButtonLogger的程序,它带有一个Button,它与一个包含另一个StackLayout的ScrollView共享一个StackLayout。 每次单击Button时,程序都会向可滚动的StackLayout添加一个新的Label,实际上记录了所有按钮的点击次数:
public class ButtonLoggerPage : ContentPage
{
StackLayout loggerLayout = new StackLayout();
public ButtonLoggerPage()
{
// Create the Button and attach Clicked handler.
Button button = new Button
{
Text = "Log the Click Time"
};
button.Clicked += OnButtonClicked;
this.Padding = new Thickness(5, Device.OnPlatform(20, 0, 0), 5, 0);
// Assemble the page.
this.Content = new StackLayout
{
Children =
{
button,
new ScrollView
{
VerticalOptions = LayoutOptions.FillAndExpand,
Content = loggerLayout
}
}
};
}
void OnButtonClicked(object sender, EventArgs args)
{
// Add Label to scrollable StackLayout.
loggerLayout.Children.Add(new Label
{
Text = "Button clicked at " + DateTime.Now.ToString("T")
});
}
}
在本书的程序中,事件处理程序的名称以On开头,后跟某种标识触发事件的视图(有时只是视图类型),后面跟着事件名称。 这种情况下的结果名称是OnButtonClicked。
在创建按钮后,构造函数将Clicked处理程序附加到按钮上。 然后页面与包含Button和ScrollView的StackLayout与另一个名为loggerLayout的StackLayout进行汇编。 请注意,ScrollView将其VerticalOptions设置为FillAndExpand,以便它可以与Button共享StackLayout,并且仍然可以看到并滚动。
下面是几次Button单击后的显示:
正如你所看到的,Button在三个屏幕上看起来有点不同。 这是因为按钮是在各个平台上本地渲染的:在iPhone上是UIButton,在Android上是Android按钮,在Windows 10 Mobile上是Windows运行时按钮。 默认情况下,按钮始终填充可用区域并将文本居中放置。
按钮定义了几个属性,可让您自定义其外观:
FontFamily of type string
- FontSize of type double
- FontAttributes of type FontAttributes
- TextColor of type Color (default is Color.Default)
- BorderColor of type Color (default is Color.Default)
- BorderWidth of type double (default is 0)
- BorderRadius of type double (default is 5)
- Image (to be discussed in Chapter 13)
Button还从VisualElement继承BackgroundColor属性(和一堆其他属性),并从View继承HorizontalOptions和VerticalOptions。
某些Button属性可能在各种平台上有点不同。 正如你所看到的,截图中的按钮都没有边框。 (但是,默认情况下,Windows Phone 8.1按钮具有可见的白色边框。)如果将BorderWidth属性设置为非零值,则边框仅在iPhone上可见,并且为黑色。 如果将BorderColor属性设置为Color.Default以外的其他值,则边框仅在Windows 10 Mobile设备上可见。 如果您需要iOS和Windows 10移动设备上的可见边框,请设置BorderWidth和BorderColor。 但是边框仍然不会显示在Android设备上,除非您还设置了backgroundColor属性。 自定义按钮边框是使用Device.OnPlatform的好机会(正如您将在第10章“XAML标记扩展”中看到的那样)。
BorderRadius属性旨在将边框的尖角四舍五入,如果显示边框,则它适用于iOS和Android,但在Windows 10和Windows 10 Mobile上不起作用。 BorderRadius适用于Windows 8.1和Windows Phone 8.1,但如果将其与BackgroundColor一起使用,则背景不会被包围在边框内。
假设您编写了一个类似于ButtonLogger的程序,但没有将loggerLayout对象保存为字段。 你可以访问Clicked事件处理程序中的StackLayout对象吗?
是! 可以通过一种称为行走视觉树的技术获取父母和孩子的视觉元素。 OnButtonClicked处理函数的发件人参数是引发该事件的对象,在本例中为Button,因此您可以通过投射该参数来启动Clicked处理程序:
Button button = (Button)sender;
你知道Button是一个StackLayout的子元素,所以可以从Parent属性访问该对象。 再次,一些铸造是必需的:
StackLayout outerLayout = (StackLayout)button.Parent;
这个StackLayout的第二个孩子是ScrollView,所以Children属性可以被索引来获得:
ScrollView scrollView = (ScrollView)outerLayout.Children[1];
这个ScrollView的Content属性正是您要查找的StackLayout:
StackLayout loggerLayout = (StackLayout)scrollView.Content;
当然,做这种事情的危险在于你可能会在某一天改变布局,并忘记更改类似树的代码。 但是,如果组装页面的代码与该页面上的视图中的代码处理事件分开,该技术就派上用场了。