第二十三章:触发器和行为(十一)

简介: 单选按钮内置于旧汽车仪表板中的无线电通常具有一排六个(左右)按钮,可以为各种无线电台“编程”。 按下其中一个按钮会导致无线电跳转到该预选电台,并且还会弹出前一个选择按钮。那些旧的汽车收音机现在是古董,但我们的电脑屏幕上的互斥选项仍然由我们称为单选按钮的视觉对象表示。

单选按钮
内置于旧汽车仪表板中的无线电通常具有一排六个(左右)按钮,可以为各种无线电台“编程”。 按下其中一个按钮会导致无线电跳转到该预选电台,并且还会弹出前一个选择按钮。
那些旧的汽车收音机现在是古董,但我们的电脑屏幕上的互斥选项仍然由我们称为单选按钮的视觉对象表示。
单选按钮有点类似于切换或复选框。 但是单选按钮总是在两个或更多组中找到。 选择或选中该组中的任何按钮会导致其他按钮取消选中。
单选按钮背后的逻辑很复杂,因为应用程序可能在同一页面上有几组单选按钮,这些组应该独立运行。 按下一个组中的按钮只会影响该组中的其他按钮,而不会影响任何其他组中的按钮。
传统上,单选按钮与共同父项分组。在Xamarin.Forms术语中,作为一个StackLayout的子节点的单选按钮被认为是在同一组中,而作为另一个StackLayout的子节点的单选按钮位于另一个独立组中。
但是,有一种更通用的方法来区分单选按钮组,即通过为每个组提供唯一的名称,这实际上意味着该组中的每个单选按钮引用相同的名称。
这些名称的问题在于它们会增加一些额外的开销,特别是当您只需要一组单选按钮时。因此,应该允许一组未通过名称识别的单选按钮。这称为默认组。
这是Xamarin.FormsBook.Toolkit库中基于这些原则的RadioBehavior类。您将此行为附加到要转换为单选按钮的每个视图。与ToggleBehavior类一样,RadioBehavior在它附加的可视元素上设置TapGestureRecognizer。它没有定义像ToggleBehavior这样的IsToggled属性,但它确实定义了一个非常相似的IsChecked属性,并指示单选按钮是选中还是未选中。 RadioBehavior类还定义了string类型的GroupName属性以标识该组;空值或空字符串表示默认组。
RadioBehavior类需要按组存储所有实例化的单选按钮,因此它定义了两个静态集合,其中一个是默认组中所有对象的简单List ,另一个是具有对应键的Dictionary到引用该命名组中所有对象的List 集合的组名:

namespace Xamarin.FormsBook.Toolkit
{
    public class RadioBehavior : Behavior<View>
    {
        TapGestureRecognizer tapRecognizer;
        static List<RadioBehavior> defaultGroup = new List<RadioBehavior>();
        static Dictionary<string, List<RadioBehavior>> radioGroups =
                                    new Dictionary<string, List<RadioBehavior>>();
        public RadioBehavior()
        {
            defaultGroup.Add(this);
        }
        public static readonly BindableProperty IsCheckedProperty =
        BindableProperty.Create("IsChecked",
                                typeof(bool),
                                typeof(RadioBehavior),
                                false,
                                propertyChanged: OnIsCheckedChanged);
        public bool IsChecked
        {
            set { SetValue(IsCheckedProperty, value); }
            get { return (bool)GetValue(IsCheckedProperty); }
        }

        static void OnIsCheckedChanged(BindableObject bindable, object oldValue,
            object newValue)
        {
            RadioBehavior behavior = (RadioBehavior)bindable;
            if ((bool)newValue)
            {
                string groupName = behavior.GroupName;
                List<RadioBehavior> behaviors = null;
                if (String.IsNullOrEmpty(groupName))
                {
                    behaviors = defaultGroup;
                }
                else
                {
                    behaviors = radioGroups[groupName];
                }
                foreach (RadioBehavior otherBehavior in behaviors)
                {
                    if (otherBehavior != behavior)
                    {
                        otherBehavior.IsChecked = false;
                    }
                }
            }
        }
        public static readonly BindableProperty GroupNameProperty =
        BindableProperty.Create("GroupName",
                                typeof(string),
                                typeof(RadioBehavior),
                                null,
                                propertyChanged: OnGroupNameChanged);
        public string GroupName
        {
            set { SetValue(GroupNameProperty, value); }
            get { return (string)GetValue(GroupNameProperty); }
        }
        static void OnGroupNameChanged(BindableObject bindable, object oldValue,
                                       object newValue)
        {
            RadioBehavior behavior = (RadioBehavior)bindable;
            string oldGroupName = (string)oldValue;
            string newGroupName = (string)newValue;
            if (String.IsNullOrEmpty(oldGroupName))
            {
                // Remove the Behavior from the default group.
                defaultGroup.Remove(behavior);
            }
            else
            {
                // Remove the RadioBehavior from the radioGroups collection.
                List<RadioBehavior> behaviors = radioGroups[oldGroupName];
                behaviors.Remove(behavior);
                // Get rid of the collection if it's empty.
                if (behaviors.Count == 0)
                {
                    radioGroups.Remove(oldGroupName);
                }
            }
            if (String.IsNullOrEmpty(newGroupName))
            {
                // Add the new Behavior to the default group.
                defaultGroup.Add(behavior);
            }
            else
            {
                List<RadioBehavior> behaviors = null;
                if (radioGroups.ContainsKey(newGroupName))
                {
                    // Get the named group.
                    behaviors = radioGroups[newGroupName];
                }
                else
                {
                    // If that group doesn't exist, create it.
                    behaviors = new List<RadioBehavior>();
                    radioGroups.Add(newGroupName, behaviors);
                }
                // Add the Behavior to the group.
                behaviors.Add(behavior);
            }
        }
        protected override void OnAttachedTo(View view)
        {
            base.OnAttachedTo(view);
            tapRecognizer = new TapGestureRecognizer();
            tapRecognizer.Tapped += OnTapRecognizerTapped;
            view.GestureRecognizers.Add(tapRecognizer);
        }
        protected override void OnDetachingFrom(View view)
        {
            base.OnDetachingFrom(view);
            view.GestureRecognizers.Remove(tapRecognizer);
            tapRecognizer.Tapped -= OnTapRecognizerTapped;
        }
        void OnTapRecognizerTapped(object sender, EventArgs args)
        {
            IsChecked = true;
        }
    }
}

列表底部的TapGestureRecognizer处理程序非常简单:当轻触可视对象时,附加到该可视对象的RadioBehavior对象将其IsChecked属性设置为true。如果IsChecked属性以前为false,则该更改将导致对OnIsCheckedChanged方法的调用,该方法将同一组中所有RadioBehavior对象的IsChecked属性设置为false。
这是一个简单的演示,用于选择T恤的尺寸。三个单选按钮是简单的Label元素,文本属性为“Small”,“Medium”和“Large”,这就是程序名为RadioLabels的原因。每个Label在其Behaviors集合中都有一个RadioBehavior。每个RadioBehavior都有一个x:Name用于数据绑定,但所有RadioBehavior对象的默认GroupName属性设置为null。每个Label在其Triggers集合中都有一个DataTrigger,它绑定到相应的RadioBehavior,以便在IsChecked属性为true时将Label的TextColor变为绿色。
请注意,中间RadioBehavior属性的IsChecked属性初始化为true,以便在程序启动时选择该对象:

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:toolkit=
                 "clr-namespace:Xamarin.FormsBook.Toolkit;assembly=Xamarin.FormsBook.Toolkit"
             xmlns:local="clr-namespace:RadioLabels"
             x:Class="RadioLabels.RadioLabelsPage"
             Padding="0, 50, 0, 0">
    <StackLayout>
        <Grid>
            <Grid.Resources>
                <ResourceDictionary>
                    <Style TargetType="Label">
                        <Setter Property="FontSize" Value="Medium" />
                        <Setter Property="HorizontalTextAlignment" Value="Center" />
                    </Style>
                </ResourceDictionary>
            </Grid.Resources>
            <Label Text="Small"
                   TextColor="Gray"
                   Grid.Column="0">
                <Label.Behaviors>
                    <toolkit:RadioBehavior x:Name="smallRadio" />
                </Label.Behaviors>
                <Label.Triggers>
                    <DataTrigger TargetType="Label"
                                 Binding="{Binding Source={x:Reference smallRadio},
                                                   Path=IsChecked}"
                                 Value="True">
                        <Setter Property="TextColor" Value="Green" />
                    </DataTrigger>
                </Label.Triggers>
            </Label>
            <Label Text="Medium"
                   TextColor="Gray"
                   Grid.Column="1">
                <Label.Behaviors>
                    <toolkit:RadioBehavior x:Name="mediumRadio"
                                         IsChecked="True" />
                </Label.Behaviors>
                <Label.Triggers>
                    <DataTrigger TargetType="Label"
                                 Binding="{Binding Source={x:Reference mediumRadio},
                                                   Path=IsChecked}"
                                 Value="True">
                        <Setter Property="TextColor" Value="Green" />
                    </DataTrigger>
                </Label.Triggers>
            </Label>

            <Label Text="Large"
                   TextColor="Gray"
                   Grid.Column="2">
                <Label.Behaviors>
                    <toolkit:RadioBehavior x:Name="largeRadio" />
                </Label.Behaviors>
                <Label.Triggers>
                    <DataTrigger TargetType="Label"
                                 Binding="{Binding Source={x:Reference largeRadio},
                                                  Path=IsChecked}"
                                 Value="True">
                        <Setter Property="TextColor" Value="Green" />
                    </DataTrigger>
                </Label.Triggers>
            </Label>
        </Grid>
        <Grid VerticalOptions="CenterAndExpand"
              HorizontalOptions="Center">
            <Image Source="{local:ImageResource RadioLabels.Images.tee200.png}"
                   IsVisible="{Binding Source={x:Reference smallRadio},
                                       Path=IsChecked}" />
            <Image Source="{local:ImageResource RadioLabels.Images.tee250.png}"
                   IsVisible="{Binding Source={x:Reference mediumRadio},
                                       Path=IsChecked}" />

            <Image Source="{local:ImageResource RadioLabels.Images.tee300.png}"
                   IsVisible="{Binding Source={x:Reference largeRadio},
                                      Path=IsChecked}" />
        </Grid>
    </StackLayout>
</ContentPage>

单选按钮固有的另一个复杂因素涉及使用所选项目。 在某些情况下,您希望组中的每个单选按钮都由特定的枚举成员表示。 (在此示例中,此类枚举可能有三个成员,名为Small,Medium和Large。)将一组单选按钮合并到枚举值中显然需要更多代码。
RadioLabels程序避免了这些问题,只是简单地将三个RadioBehavior对象的IsChecked属性绑定到三个Image元素的IsVisible属性,这三个Image元素共享XAML文件底部的单个单元格Grid。 它们根据选择显示不同大小的位图。
这些位图的相对大小在这些屏幕截图中并不那么明显,因为每个平台都会以不同的大小显示位图:
2019_04_11_160259
当选择该项时,附加到每个Label的DataTrigger将TextColor从其样式颜色Gray更改为Green。
如果要在选择该项时更改每个Label的多个属性,可以向DataTrigger添加更多Setter对象。 但更好的方法是在Style中合并Setter对象,然后在DataTrigger中引用Style。
这在RadioStyle程序中得到了证明。 页面的“资源”字典定义了一个样式,其中键为“baseStyle”,用于定义未经检查的Label的外观,以及一个样式,其键为“selectedStyle”,基于“baseStyle”,但定义了已选中Label的外观。 Resources集合以Label的隐式样式结束,与“baseStyle”相同:

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:local="clr-namespace:RadioStyle"
             xmlns:toolkit=
                 "clr-namespace:Xamarin.FormsBook.Toolkit;assembly=Xamarin.FormsBook.Toolkit"
             x:Class="RadioStyle.RadioStylePage"
             Padding="0, 50, 0, 0">
    <ContentPage.Resources>
        <ResourceDictionary>
            <Style x:Key="baseStyle" TargetType="Label">
                <Setter Property="TextColor" Value="Gray" />
                <Setter Property="FontSize" Value="Small" />
                <Setter Property="HorizontalTextAlignment" Value="Center" />
                <Setter Property="VerticalTextAlignment" Value="Center" />
            </Style>
            <Style x:Key="selectedStyle" TargetType="Label"
                   BasedOn="{StaticResource baseStyle}">
                <Setter Property="TextColor" Value="Green" />
                <Setter Property="FontSize" Value="Medium" />
                <Setter Property="FontAttributes" Value="Bold,Italic" />
            </Style>
            <!-- Implicit style -->
            <Style TargetType="Label" BasedOn="{StaticResource baseStyle}" />
        </ResourceDictionary>
    </ContentPage.Resources>
    <StackLayout>
        <Grid>
            <Label Text="Small"
                   Grid.Column="0">
                <Label.Behaviors>
                    <toolkit:RadioBehavior x:Name="smallRadio" />
                </Label.Behaviors>
                <Label.Triggers>
                    <DataTrigger TargetType="Label"
                                 Binding="{Binding Source={x:Reference smallRadio},
                                                   Path=IsChecked}"
                                 Value="True">
                        <Setter Property="Style" Value="{StaticResource selectedStyle}" />
                    </DataTrigger>
                </Label.Triggers>
            </Label>
            <Label Text="Medium"
                   Grid.Column="1">
                <Label.Behaviors>
                    <toolkit:RadioBehavior x:Name="mediumRadio"
                                           IsChecked="True" />
                </Label.Behaviors>
                <Label.Triggers>
                    <DataTrigger TargetType="Label"
                                 Binding="{Binding Source={x:Reference mediumRadio},
                                                   Path=IsChecked}"
                                 Value="True">
                        <Setter Property="Style" Value="{StaticResource selectedStyle}" />
                    </DataTrigger>
                </Label.Triggers>
            </Label>
            <Label Text="Large"
                   Grid.Column="2">
                <Label.Behaviors>
                    <toolkit:RadioBehavior x:Name="largeRadio" />
                </Label.Behaviors>
                <Label.Triggers>
                    <DataTrigger TargetType="Label"
                                 Binding="{Binding Source={x:Reference largeRadio},
                                                   Path=IsChecked}"
                                 Value="True">
                        <Setter Property="Style" Value="{StaticResource selectedStyle}" />
                    </DataTrigger>
                </Label.Triggers>
            </Label>
        </Grid>

        <Grid VerticalOptions="CenterAndExpand"
              HorizontalOptions="Center">

            <Image Source="{local:ImageResource RadioStyle.Images.tee200.png}"
                   IsVisible="{Binding Source={x:Reference smallRadio},
                                       Path=IsChecked}" />

            <Image Source="{local:ImageResource RadioStyle.Images.tee250.png}"
                   IsVisible="{Binding Source={x:Reference mediumRadio},
                                       Path=IsChecked}" />
            <Image Source="{local:ImageResource RadioStyle.Images.tee300.png}"
                   IsVisible="{Binding Source={x:Reference largeRadio},
                                       Path=IsChecked}" />
        </Grid>
    </StackLayout>
</ContentPage>

在本章之前,Setter对象只能在样式定义中找到,因此在DataTrigger中看到Setter对象设置Label的Style属性可能看起来有点奇怪。 但截图显示它工作正常。 现在,除了不同的颜色外,所选项目还使用粗体和斜体的较大字体:
2019_04_11_160820
您也可以创建新类型的视觉效果,以识别一组单选按钮中的所选项目。 RadioImages计划包含四个位图,表明不同的运输方式。 引用这些位图的Image元素每个都是附加了RadioBehavior的ContentView的子元素,以及一个更改ContentView颜色的DataTrigger:

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:local="clr-namespace:RadioImages"
             xmlns:toolkit=
                 "clr-namespace:Xamarin.FormsBook.Toolkit;assembly=Xamarin.FormsBook.Toolkit"
             x:Class="RadioImages.RadioImagesPage">
    <ContentPage.Resources>
        <ResourceDictionary>
            <Style TargetType="ContentView">
                <Setter Property="WidthRequest" Value="75" />
                <Setter Property="HeightRequest" Value="75" />
                <Setter Property="Padding" Value="10" />
            </Style>
            <Color x:Key="selectedColor">#80C0FF</Color>
        </ResourceDictionary>
    </ContentPage.Resources>
    <StackLayout HorizontalOptions="Start"
                 VerticalOptions="Center"
                 Padding="20, 0"
                 Spacing="0">
        <ContentView>
            <ContentView.Behaviors>
                <toolkit:RadioBehavior x:Name="pedestrianRadio" />
            </ContentView.Behaviors>
            <ContentView.Triggers>
                <DataTrigger TargetType="ContentView"
                             Binding="{Binding Source={x:Reference pedestrianRadio},
                                               Path=IsChecked}"
                             Value="True">
                    <Setter Property="BackgroundColor" Value="{StaticResource selectedColor}" />
                </DataTrigger>
            </ContentView.Triggers>

            <Image Source="{local:ImageResource RadioImages.Images.pedestrian.png}" />
        </ContentView>
        <ContentView>
            <ContentView.Behaviors>
                <toolkit:RadioBehavior x:Name="carRadio" />
            </ContentView.Behaviors>
            <ContentView.Triggers>
                <DataTrigger TargetType="ContentView"
                             Binding="{Binding Source={x:Reference carRadio},
                                               Path=IsChecked}"
                             Value="True">
                    <Setter Property="BackgroundColor" Value="{StaticResource selectedColor}" />
                </DataTrigger>
            </ContentView.Triggers>
            <Image Source="{local:ImageResource RadioImages.Images.car.png}" />
        </ContentView>
        <ContentView>
            <ContentView.Behaviors>
                <toolkit:RadioBehavior x:Name="trainRadio" />
            </ContentView.Behaviors>
            <ContentView.Triggers>
                <DataTrigger TargetType="ContentView"
                             Binding="{Binding Source={x:Reference trainRadio},
                                               Path=IsChecked}"
                             Value="True">
                    <Setter Property="BackgroundColor" Value="{StaticResource selectedColor}" />
                </DataTrigger>
            </ContentView.Triggers>
            <Image Source="{local:ImageResource RadioImages.Images.train.png}" />
        </ContentView>
        <ContentView>
            <ContentView.Behaviors>
                <toolkit:RadioBehavior x:Name="busRadio" />
            </ContentView.Behaviors>
            <ContentView.Triggers>
                <DataTrigger TargetType="ContentView"
                             Binding="{Binding Source={x:Reference busRadio},
                                               Path=IsChecked}"
                             Value="True">
                    <Setter Property="BackgroundColor" Value="{StaticResource selectedColor}" />
                </DataTrigger>
            </ContentView.Triggers>
            <Image Source="{local:ImageResource RadioImages.Images.bus.png}" />
        </ContentView>
    </StackLayout>
</ContentPage>

有时,您需要通过将其中一个RadioBehavior对象的IsChecked属性设置为true来设置初始选定项目,有时不会。 该程序在程序启动时将所有程序都取消选中,但是一旦用户选择了其中一个项目,就无法全部取消选中它们。
此方案中的关键因素是ContentView被赋予了重要的Padding值,因此在选择该项时它似乎包围了Image元素:
2019_04_11_161211
当然,即使只有四个项目,重复标记看起来有点不祥。您可以从ContentView派生一个类来合并RadioBehavior和DataTrigger交互,但是您需要在此派生类上定义一个属性来指定与该按钮关联的特定位图,并且很可能是另一个属性或事件来指示何时选择该项目。通常,通过使用Style或其他资源定义公共属性,可以更轻松地将每个单选按钮的标记保持在最低限度。
如果您想创建更传统的单选按钮视觉效果,那也是可能的。 Unicode字符 u25CB和 u25C9类似于传统的未经检查和检查的单选按钮圆圈和点。
TraditionalRadios程序有六个单选按钮,但它们分为两组,每组三个按钮,因此需要为两个组中的至少一个设置GroupName属性。该
程序选择将所有单选按钮的GroupName设置为“platformGroup”或“languageGroup”。每个RadioBehavior都附加到一个水平StackLayout,其中包含一个Label,其中DataTrigger在“&#x25CB;”和“&#x25C9;”字符串之间切换,另一个Label显示该符号右侧的文本:

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:toolkit=
                 "clr-namespace:Xamarin.FormsBook.Toolkit;assembly=Xamarin.FormsBook.Toolkit"
             x:Class="TraditionalRadios.TraditionalRadiosPage">
    <ContentPage.Resources>
        <ResourceDictionary>
            <x:String x:Key="uncheckedRadio">&#x25CB;</x:String>
            <x:String x:Key="checkedRadio">&#x25C9;</x:String>
        </ResourceDictionary>
    </ContentPage.Resources>
    <Grid VerticalOptions="Center" Padding="5, 0">
        <!-- Left column -->
        <StackLayout Grid.Column="0" Spacing="24">

            <!-- Header -->
            <StackLayout HorizontalOptions="Start" Spacing="0">
                <Label Text="Choose Platform" />
                <BoxView Color="Accent" HeightRequest="1" />
            </StackLayout>
            <!-- Stack of radio buttons -->
            <StackLayout Spacing="12">
                <StackLayout Orientation="Horizontal">
                    <StackLayout.Behaviors>
                        <toolkit:RadioBehavior x:Name="iosRadio"
                                               GroupName="platformGroup" />
                    </StackLayout.Behaviors>
                    <Label Text="{StaticResource uncheckedRadio}">
                        <Label.Triggers>
                            <DataTrigger TargetType="Label"
                                         Binding="{Binding Source={x:Reference iosRadio},
                                                           Path=IsChecked}"
                                         Value="True">
                                <Setter Property="Text" Value="{StaticResource checkedRadio}" />
                            </DataTrigger>
                        </Label.Triggers>
                    </Label>
                    <Label Text="iOS" />
                </StackLayout>
                <StackLayout Orientation="Horizontal">
                    <StackLayout.Behaviors>
                        <toolkit:RadioBehavior x:Name="androidRadio"
                                               GroupName="platformGroup" />
                    </StackLayout.Behaviors>
                    <Label Text="{StaticResource uncheckedRadio}">
                        <Label.Triggers>
                            <DataTrigger TargetType="Label"
                                         Binding="{Binding Source={x:Reference androidRadio},
                                                           Path=IsChecked}"
                                         Value="True">
                                <Setter Property="Text" Value="{StaticResource checkedRadio}" />
                            </DataTrigger>
                        </Label.Triggers>
                    </Label>
                    <Label Text="Android" />
                </StackLayout>
                <StackLayout Orientation="Horizontal">
                    <StackLayout.Behaviors>
                        <toolkit:RadioBehavior x:Name="winPhoneRadio"
                                               GroupName="platformGroup" />
                    </StackLayout.Behaviors>
                    <Label Text="{StaticResource uncheckedRadio}">
                        <Label.Triggers>
                            <DataTrigger TargetType="Label"
                                         Binding="{Binding Source={x:Reference winPhoneRadio},
                                                           Path=IsChecked}"
                                         Value="True">
                                <Setter Property="Text" Value="{StaticResource checkedRadio}" />
                            </DataTrigger>
                        </Label.Triggers>
                    </Label>
                    <Label Text="Windows Phone" />
                </StackLayout>
            </StackLayout>
        </StackLayout>
        <!-- Left column -->
        <StackLayout Grid.Column="1" Spacing="24">
            <!-- Header -->
            <StackLayout HorizontalOptions="Start" Spacing="0">
                <Label Text="Choose Language" />
                <BoxView Color="Accent" HeightRequest="1" />
                Chapter 23 Triggers and behaviors 907
            </StackLayout>
            <!-- Stack of radio buttons -->
            <StackLayout Spacing="12">
                <StackLayout Orientation="Horizontal">
                    <StackLayout.Behaviors>
                        <toolkit:RadioBehavior x:Name="objectiveCRadio"
                                               GroupName="languageGroup" />
                    </StackLayout.Behaviors>
                    <Label Text="{StaticResource uncheckedRadio}">
                        <Label.Triggers>
                            <DataTrigger TargetType="Label"
                                         Binding="{Binding Source={x:Reference objectiveCRadio},
                                                           Path=IsChecked}"
                                         Value="True">
                                <Setter Property="Text" Value="{StaticResource checkedRadio}" />
                            </DataTrigger>
                        </Label.Triggers>
                    </Label>
                    <Label Text="Objective-C" />
                </StackLayout>
                <StackLayout Orientation="Horizontal">
                    <StackLayout.Behaviors>
                        <toolkit:RadioBehavior x:Name="javaRadio"
                                               GroupName="languageGroup" />
                    </StackLayout.Behaviors>
                    <Label Text="{StaticResource uncheckedRadio}">
                        <Label.Triggers>
                            <DataTrigger TargetType="Label"
                                         Binding="{Binding Source={x:Reference javaRadio},
                                                           Path=IsChecked}"
                                         Value="True">
                                <Setter Property="Text" Value="{StaticResource checkedRadio}" />
                            </DataTrigger>
                        </Label.Triggers>
                    </Label>
                    <Label Text="Java" />
                </StackLayout>
                <StackLayout Orientation="Horizontal">
                    <StackLayout.Behaviors>
                        <toolkit:RadioBehavior x:Name="cSharpRadio"
                                               GroupName="languageGroup" />
                    </StackLayout.Behaviors>
                    <Label Text="{StaticResource uncheckedRadio}">
                        <Label.Triggers>
                            <DataTrigger TargetType="Label"
                                         Binding="{Binding Source={x:Reference cSharpRadio},
                                                           Path=IsChecked}"
                                         Value="True">
                                <Setter Property="Text" Value="{StaticResource checkedRadio}" />
                            </DataTrigger>
                        </Label.Triggers>
                    </Label>
                    <Label Text="C&#x266F;" />
                </StackLayout>
            </StackLayout>
        </StackLayout>
    </Grid>
</ContentPage>

在现代用户界面的环境中,这些单选按钮看起来非常古怪和老式,但同时又非常真实:
2019_04_11_161849

目录
相关文章
|
JavaScript Android开发
第二十三章:触发器和行为(十二)
淡化和定向在本书中,您已经看到了几个颜色选择程序,可以通过使用三个Slider元素以交互方式形成颜色。 本章的最后一个示例是另一个颜色选择程序,但是这个程序为您提供了选项:它包含三个标记为“RGB Hex”,“RGB Float”和“HSL”的单选按钮(实际上是简单的Label元素)。
708 0
|
JavaScript Android开发
第二十三章:触发器和行为(七)
行为 触发器和行为通常是串联讨论的,因为它们具有一些应用重叠。 有时候你会感到困惑是否使用触发器或行为,因为似乎要么这样做工作。你可以用触发器做任何事情,你也可以做一个行为。 但是,行为总是涉及一些代码,这是一个派生自Behavior 的类。
949 0
|
JavaScript Android开发
第二十三章:触发器和行为(十)
响应水龙头切换视图的各种表现形式演示了一种响应XAML文件中的点击的方法。 如果将tap事件集成到VisualElement类中,您可以使用EventTrigger更直接且更轻松地获取它们。 但是您无法将EventTrigger附加到TapGestureRecognizer。
544 0
|
JavaScript Android开发 Windows
第二十三章:触发器和行为(九)
切换和复选框在第15章“交互式界面”和第16章“数据绑定”中,您了解了如何构造传统的CheckBox视图。 但是,自定义视图的另一种方法是将视图的交互逻辑合并到行为中,然后完全在XAML中实现视觉效果。
730 0
|
Android开发
第二十三章:触发器和行为(八)
具有属性的行为Behavior 类派生自Behavior类,该类派生自BindableObject。这表明您的Behavior 派生可以定义自己的可绑定属性。之前你看过一些Action 衍生产品,比如ScaleAction和ShiverAction,它们定义了一些属性以赋予它们更大的灵活性。
649 0
|
JavaScript Android开发
第二十三章:触发器和行为(六)
MultiTrigger中的组合条件Trigger和DataTrigger都有效地监视属性以确定它是否等于特定值。 这称为触发器的条件,如果条件为真,则调用Setter对象的集合。作为程序员,您可能会开始怀疑是否可以在触发器中具有多个条件。
654 0
|
JavaScript Android开发 iOS开发
第二十三章:触发器和行为(五)
数据触发器到目前为止,您只看到在特定对象的上下文中运行的触发器。 触发器通过更改同一对象的另一个属性或通过调用影响该对象的Action来响应对象属性的更改。 EventTrigger同样响应一个对象触发的事件,以在同一个对象上调用Action。
940 0
|
JavaScript Android开发 Windows
第二十三章:触发器和行为(四)
更多事件触发器前一章关于动画的章节展示了一个按钮,它在点击时旋转或缩放。 虽然大多数动画示例都是为了制作有趣的演示而采取极端措施,但是按钮用一点动画来响应点击并不是不合理的。 这是EventTrigger的完美工作。
779 0
|
JavaScript Android开发
第二十三章:触发器和行为(三)
触发动作和动画虽然某些触发器可以完全在XAML中实现,但其他触发器需要一些代码支持。 如您所知,Xamarin.Forms没有直接支持在XAML中实现动画,因此如果您想使用触发器为元素设置动画,则需要一些代码。
1021 0
|
JavaScript Android开发
第二十三章:触发器和行为(二)
触发器 在最普遍(和最模糊)的意义上,触发器是导致响应的条件。 更具体地说,触发器通过设置另一个属性或运行一些代码来响应属性更改或触发事件。 几乎总是,设置的属性或运行的代码涉及用户界面,并在XAML中表示。
980 0