Xamarin.Forms Gatilhos
Os gatilhos permitem expressar ações declarativamente em XAML que alteram a aparência dos controles com base em eventos ou alterações de propriedade. Além disso, os gatilhos de estado, que são um grupo especializado de gatilhos, definem quando se deve aplicar VisualState
.
Você pode atribuir um gatilho diretamente a um controle ou adicioná-lo a um dicionário de recursos na página ou aplicativo a ser aplicado a vários controles.
Gatilhos de propriedades
Um gatilho simples pode ser expresso puramente em XAML, adicionando um elemento Trigger
a uma coleção de gatilhos do controle.
Este exemplo mostra um gatilho que altera uma cor da tela de fundo Entry
quando recebe o foco:
<Entry Placeholder="enter name">
<Entry.Triggers>
<Trigger TargetType="Entry"
Property="IsFocused" Value="True">
<Setter Property="BackgroundColor" Value="Yellow" />
<!-- multiple Setters elements are allowed -->
</Trigger>
</Entry.Triggers>
</Entry>
As partes importantes da declaração do gatilho são:
TargetType – o tipo de controle ao qual o gatilho se aplica.
Propriedade – a propriedade no controle que é monitorado.
Valor – o valor, quando ele ocorre para a propriedade monitorada, que faz o gatilho ativar.
Setter – uma coleção de elementos
Setter
pode ser adicionada quando a condição do gatilho for atendida. Você deve especificarProperty
eValue
para definir.EnterActions e ExitActions (não mostrado) – são escritos em código e podem ser usados em (ou em vez de) elementos
Setter
. Eles são descritos abaixo.
Aplicação de um gatilho usando um estilo
Gatilhos também podem ser adicionados a uma declaração Style
em um controle, em uma página ou um aplicativo ResourceDictionary
. Este exemplo declara um estilo implícito (por exemplo, nenhum Key
está definido), o que significa que ele se aplicará a todos os controles Entry
na página.
<ContentPage.Resources>
<ResourceDictionary>
<Style TargetType="Entry">
<Style.Triggers>
<Trigger TargetType="Entry"
Property="IsFocused" Value="True">
<Setter Property="BackgroundColor" Value="Yellow" />
<!-- multiple Setters elements are allowed -->
</Trigger>
</Style.Triggers>
</Style>
</ResourceDictionary>
</ContentPage.Resources>
Gatilhos de dados
Os gatilhos de dados usam a associação de dados para monitorar outro controle para fazer com que Setter
s sejam chamados. Em vez do atributo Property
em um gatilho de propriedade, defina o atributo Binding
para monitorar o valor especificado.
O exemplo a seguir usa a sintaxe de associação de dados {Binding Source={x:Reference entry}, Path=Text.Length}
que é como nos referimos às propriedades de outro controle. Quando o tamanho do entry
for zero, o gatilho será ativado. Neste exemplo, o gatilho desabilita o botão quando a entrada está vazia.
<!-- the x:Name is referenced below in DataTrigger-->
<!-- tip: make sure to set the Text="" (or some other default) -->
<Entry x:Name="entry"
Text=""
Placeholder="required field" />
<Button x:Name="button" Text="Save"
FontSize="Large"
HorizontalOptions="Center">
<Button.Triggers>
<DataTrigger TargetType="Button"
Binding="{Binding Source={x:Reference entry},
Path=Text.Length}"
Value="0">
<Setter Property="IsEnabled" Value="False" />
<!-- multiple Setters elements are allowed -->
</DataTrigger>
</Button.Triggers>
</Button>
Dica
Ao avaliar Path=Text.Length
, sempre forneça um valor padrão para a propriedade de destino (por exemplo, Text=""
), caso contrário, será null
e o gatilho não funcionará como você espera.
Além de especificar Setter
s, você também pode fornecer EnterActions
e ExitActions
.
Gatilhos de evento
O elemento EventTrigger
requer apenas uma propriedade Event
, como "Clicked"
no exemplo a seguir.
<EventTrigger Event="Clicked">
<local:NumericValidationTriggerAction />
</EventTrigger>
Observe que não há elementos Setter
, mas, em vez disso, uma referência a uma classe definida por local:NumericValidationTriggerAction
, que requer que o xmlns:local
seja declarado no XAML da página:
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:WorkingWithTriggers;assembly=WorkingWithTriggers"
A classe em si implementa TriggerAction
, o que significa que ela deve fornecer uma substituição para o método Invoke
que é chamado sempre que ocorre o evento de gatilho.
Uma implementação de ação do gatilho deve:
Implementar a classe
TriggerAction<T>
genérica com o parâmetro genérico correspondente com o tipo de controle ao qual o gatilho será aplicado. É possível usar superclasses, comoVisualElement
, para gravar ações de gatilho que funcionam com uma variedade de controles ou especificar um tipo de controle, comoEntry
.Substituir o método
Invoke
– isso é chamado sempre que os critérios do gatilho forem atendidos.Outra opção é expor propriedades que podem ser definidas no XAML quando o gatilho é declarado. Para obter um exemplo disso, confira a classe
VisualElementPopTriggerAction
no respectivo aplicativo de exemplo.
public class NumericValidationTriggerAction : TriggerAction<Entry>
{
protected override void Invoke (Entry entry)
{
double result;
bool isValid = Double.TryParse (entry.Text, out result);
entry.TextColor = isValid ? Color.Default : Color.Red;
}
}
O gatilho de evento pode ser consumido do XAML:
<EventTrigger Event="TextChanged">
<local:NumericValidationTriggerAction />
</EventTrigger>
Tenha cuidado ao compartilhar gatilhos em um ResourceDictionary
, pois uma instância será compartilhada entre os controles para que qualquer estado que está configurado uma vez se aplique a todos.
Observe que os gatilhos de evento não dão suporte a EnterActions
e ExitActions
descritos abaixo.
Múltiplos gatilhos
Um MultiTrigger
é semelhante a um Trigger
ou DataTrigger
, exceto que pode haver mais de uma condição. Todas as condições devem ser verdadeiras antes de Setter
s serem disparados.
Aqui está um exemplo de um gatilho para um botão que é associado a duas entradas diferentes (email
e phone
):
<MultiTrigger TargetType="Button">
<MultiTrigger.Conditions>
<BindingCondition Binding="{Binding Source={x:Reference email},
Path=Text.Length}"
Value="0" />
<BindingCondition Binding="{Binding Source={x:Reference phone},
Path=Text.Length}"
Value="0" />
</MultiTrigger.Conditions>
<Setter Property="IsEnabled" Value="False" />
<!-- multiple Setter elements are allowed -->
</MultiTrigger>
A coleção Conditions
também pode conter elementos PropertyCondition
como este:
<PropertyCondition Property="Text" Value="OK" />
Criar um gatilho múltiplo que “exige tudo”
O gatilho múltiplo apenas atualiza seu controle quando todas as condições são verdadeiras. Testar "todos os comprimentos de campo são zero" (como uma página de logon em que todas as entradas devem estar completas) é complicado porque você deseja uma condição "onde Text.Length > 0", mas isso não pode ser expresso em XAML.
Isso pode ser feito com um IValueConverter
. O código de conversor abaixo transforma o Text.Length
que se associa a um bool
que indica se um campo está vazio ou não:
public class MultiTriggerConverter : IValueConverter
{
public object Convert(object value, Type targetType,
object parameter, CultureInfo culture)
{
if ((int)value > 0) // length > 0 ?
return true; // some data has been entered
else
return false; // input is empty
}
public object ConvertBack(object value, Type targetType,
object parameter, CultureInfo culture)
{
throw new NotSupportedException ();
}
}
Para usar esse conversor em um gatilho múltiplo, primeiro adicione-o ao dicionário de recursos da página (junto com uma definição de namespace xmlns:local
personalizada):
<ResourceDictionary>
<local:MultiTriggerConverter x:Key="dataHasBeenEntered" />
</ResourceDictionary>
O XAML é mostrado abaixo. Observe as seguintes diferenças do primeiro exemplo de gatilho múltiplo:
- O botão tem
IsEnabled="false"
definido por padrão. - As condições de gatilho múltiplo usam o conversor para transformar o valor
Text.Length
em umboolean
. - Quando todas as condições forem
true
, o setter transformará oIsEnabled
do botão na propriedadetrue
.
<Entry x:Name="user" Text="" Placeholder="user name" />
<Entry x:Name="pwd" Text="" Placeholder="password" />
<Button x:Name="loginButton" Text="Login"
FontSize="Large"
HorizontalOptions="Center"
IsEnabled="false">
<Button.Triggers>
<MultiTrigger TargetType="Button">
<MultiTrigger.Conditions>
<BindingCondition Binding="{Binding Source={x:Reference user},
Path=Text.Length,
Converter={StaticResource dataHasBeenEntered}}"
Value="true" />
<BindingCondition Binding="{Binding Source={x:Reference pwd},
Path=Text.Length,
Converter={StaticResource dataHasBeenEntered}}"
Value="true" />
</MultiTrigger.Conditions>
<Setter Property="IsEnabled" Value="True" />
</MultiTrigger>
</Button.Triggers>
</Button>
Essas capturas de tela mostram a diferença entre os dois exemplos de gatilho múltiplo acima. Na parte superior das telas, a entrada de texto em apenas um Entry
é suficiente para habilitar o botão Salvar.
Na parte inferior das telas, o botão Logon permanece inativo até que ambos os campos contenham dados.
EnterActions e ExitActions
Outra maneira de implementar alterações quando ocorre um gatilho é adicionando as coleções EnterActions
e ExitActions
e especificando as implementações TriggerAction<T>
.
A coleção EnterActions
é usada para definir um IList
de TriggerAction
objetos que serão invocados quando a condição do gatilho for atendida. A coleção ExitActions
é usada para definir um IList
de TriggerAction
objetos que serão invocados depois que a condição do gatilho não for mais atendida.
Observação
Os objetos TriggerAction
definidos nas coleções EnterActions
e ExitActions
são ignorados pela classe EventTrigger
.
Você pode fornecer ambos EnterActions
e ExitActions
, bem como Setter
s em um gatilho, mas lembre-se de que Setter
s são chamados imediatamente (eles não aguardam o EnterAction
ou o ExitAction
ser concluído). Como alternativa, você pode executar tudo no código e não usar Setter
s.
<Entry Placeholder="enter job title">
<Entry.Triggers>
<Trigger TargetType="Entry"
Property="Entry.IsFocused" Value="True">
<Trigger.EnterActions>
<local:FadeTriggerAction StartsFrom="0" />
</Trigger.EnterActions>
<Trigger.ExitActions>
<local:FadeTriggerAction StartsFrom="1" />
</Trigger.ExitActions>
<!-- You can use both Enter/Exit and Setter together if required -->
</Trigger>
</Entry.Triggers>
</Entry>
Como sempre, quando uma classe é referenciada no XAML você deve declarar um namespace como xmlns:local
, conforme mostrado aqui:
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:WorkingWithTriggers;assembly=WorkingWithTriggers"
O código FadeTriggerAction
é mostrado abaixo:
public class FadeTriggerAction : TriggerAction<VisualElement>
{
public int StartsFrom { set; get; }
protected override void Invoke(VisualElement sender)
{
sender.Animate("FadeTriggerAction", new Animation((d) =>
{
var val = StartsFrom == 1 ? d : 1 - d;
// so i was aiming for a different color, but then i liked the pink :)
sender.BackgroundColor = Color.FromRgb(1, val, 1);
}),
length: 1000, // milliseconds
easing: Easing.Linear);
}
}
Gatilhos de estado
Os gatilhos de estado são um grupo especializado de gatilhos que definem as condições sob as quais um VisualState
deve ser aplicado.
Os gatilhos de estado são adicionados à coleção de StateTriggers
de um VisualState
. Essa coleção pode conter um ou vários gatilhos de estado. Um VisualState
será aplicado quando qualquer gatilho de estado na coleção estiver ativo.
Ao usar gatilhos de estado para controlar estados visuais, Xamarin.Forms usa as seguintes regras de precedência para determinar qual gatilho (e correspondente VisualState
) estará ativo:
- Um gatilho que deriva de
StateTriggerBase
. - Um
AdaptiveTrigger
ativado porque a condiçãoMinWindowWidth
foi atendida. - Um
AdaptiveTrigger
ativado porque a condiçãoMinWindowHeight
foi atendida.
Se múltiplos gatinhos estiverem simultaneamente ativos (por exemplo, dois gatilhos personalizados), o primeiro gatilho declarado na marcação terá precedência.
Observação
Os gatilhos de estado podem ser definidos em Style
ou diretamente nos elementos.
Para obter mais informações sobre estados visuais, consulte Xamarin.Forms Gerenciador de Estado Visual.
Gatilho de estado
A classe StateTrigger
, que deriva da classe StateTriggerBase
, tem uma propriedade associável IsActive
. Um StateTrigger
aciona o gatilho de uma alteração VisualState
quando a propriedade IsActive
muda de valor.
A classe StateTriggerBase
, que é a classe base de todos os gatilhos de estado, tem uma propriedade IsActive
e um evento IsActiveChanged
. Esse evento é disparado sempre que ocorre uma alteração em VisualState
. Além disso, a StateTriggerBase
classe tem métodos substituíveis OnAttached
e OnDetached
.
Importante
A propriedade associável StateTrigger.IsActive
oculta a propriedade herdada StateTriggerBase.IsActive
.
O exemplo de XAML a seguir mostra um Style
que inclui os objetos StateTrigger
:
<Style TargetType="Grid">
<Setter Property="VisualStateManager.VisualStateGroups">
<VisualStateGroupList>
<VisualStateGroup>
<VisualState x:Name="Checked">
<VisualState.StateTriggers>
<StateTrigger IsActive="{Binding IsToggled}"
IsActiveChanged="OnCheckedStateIsActiveChanged" />
</VisualState.StateTriggers>
<VisualState.Setters>
<Setter Property="BackgroundColor"
Value="Black" />
</VisualState.Setters>
</VisualState>
<VisualState x:Name="Unchecked">
<VisualState.StateTriggers>
<StateTrigger IsActive="{Binding IsToggled, Converter={StaticResource inverseBooleanConverter}}"
IsActiveChanged="OnUncheckedStateIsActiveChanged" />
</VisualState.StateTriggers>
<VisualState.Setters>
<Setter Property="BackgroundColor"
Value="White" />
</VisualState.Setters>
</VisualState>
</VisualStateGroup>
</VisualStateGroupList>
</Setter>
</Style>
Nesse exemplo, o Style
implícito tem como destino os objetos Grid
. Quando a propriedade IsToggled
do objeto associado é true
, a cor da tela de fundo de Grid
é definida como preto. Quando a propriedade IsToggled
do objeto associado se torna false
, uma alteração de VisualState
é disparada, e a cor da tela de fundo de Grid
se torna branca.
Além disso, a cada vez que a alteração VisualState
ocorre, o evento IsActiveChanged
de VisualState
é disparado. Cada VisualState
registra um manipulador de eventos para esse evento:
void OnCheckedStateIsActiveChanged(object sender, EventArgs e)
{
StateTriggerBase stateTrigger = sender as StateTriggerBase;
Console.WriteLine($"Checked state active: {stateTrigger.IsActive}");
}
void OnUncheckedStateIsActiveChanged(object sender, EventArgs e)
{
StateTriggerBase stateTrigger = sender as StateTriggerBase;
Console.WriteLine($"Unchecked state active: {stateTrigger.IsActive}");
}
Nesse exemplo, quando um manipulador do evento IsActiveChanged
é disparado, o manipulador informa se VisualState
está ativo ou não. Por exemplo, as mensagens a seguir são enviadas para a janela do console ao alterar do estado visual Checked
para o estado visual Unchecked
:
Checked state active: False
Unchecked state active: True
Observação
Os gatilhos de estado personalizados podem ser criados derivando da StateTriggerBase
classe e substituindo os OnAttached
métodos and OnDetached
para executar todos os registros e limpezas necessários.
Gatilho adaptável
Um AdaptiveTrigger
dispara uma alteração VisualState
quando a janela tem a altura ou largura especificada. Esse gatilho tem duas propriedades associáveis:
MinWindowHeight
, do tipodouble
, que indica a altura mínima da janela na qualVisualState
deve ser aplicado.MinWindowWidth
, do tipodouble
, que indica a largura mínima da janela na qualVisualState
deve ser aplicado.
Observação
AdaptiveTrigger
deriva da classe StateTriggerBase
e, portanto, pode anexar um manipulador de eventos ao evento IsActiveChanged
.
O exemplo de XAML a seguir mostra um Style
que inclui os objetos AdaptiveTrigger
:
<Style TargetType="StackLayout">
<Setter Property="VisualStateManager.VisualStateGroups">
<VisualStateGroupList>
<VisualStateGroup>
<VisualState x:Name="Vertical">
<VisualState.StateTriggers>
<AdaptiveTrigger MinWindowWidth="0" />
</VisualState.StateTriggers>
<VisualState.Setters>
<Setter Property="Orientation"
Value="Vertical" />
</VisualState.Setters>
</VisualState>
<VisualState x:Name="Horizontal">
<VisualState.StateTriggers>
<AdaptiveTrigger MinWindowWidth="800" />
</VisualState.StateTriggers>
<VisualState.Setters>
<Setter Property="Orientation"
Value="Horizontal" />
</VisualState.Setters>
</VisualState>
</VisualStateGroup>
</VisualStateGroupList>
</Setter>
</Style>
Nesse exemplo, o Style
implícito tem como destino os objetos StackLayout
. Quando a largura da janela está entre 0 e 800 unidades independentes de dispositivo, os objetos de StackLayout
aos quais Style
é aplicado terão uma orientação vertical. Quando a largura da janela é >= 800 unidades independentes do dispositivo, a VisualState
alteração é acionada e a StackLayout
orientação muda para horizontal:
As propriedades MinWindowHeight
e MinWindowWidth
podem ser usadas de forma independente ou em conjunto uma com a outra. O XAML a seguir mostra um exemplo da definição das duas propriedades:
<AdaptiveTrigger MinWindowWidth="800"
MinWindowHeight="1200"/>
Neste exemplo, o AdaptiveTrigger
indica que o correspondente VisualState
será aplicado quando a largura da janela atual for >= 800 unidades independentes do dispositivo e a altura da janela atual for >= 1200 unidades independentes do dispositivo.
Comparar gatilho de estado
O CompareStateTrigger
dispara uma alteração de VisualState
quando a propriedade é igual a um valor específico. Esse gatilho tem duas propriedades associáveis:
Property
, do tipoobject
, que indica a propriedade comparada pelo gatilho.Value
, do tipoobject
, que indica o valor no qualVisualState
deve ser aplicado.
Observação
CompareStateTrigger
deriva da classe StateTriggerBase
e, portanto, pode anexar um manipulador de eventos ao evento IsActiveChanged
.
O exemplo de XAML a seguir mostra um Style
que inclui os objetos CompareStateTrigger
:
<Style TargetType="Grid">
<Setter Property="VisualStateManager.VisualStateGroups">
<VisualStateGroupList>
<VisualStateGroup>
<VisualState x:Name="Checked">
<VisualState.StateTriggers>
<CompareStateTrigger Property="{Binding Source={x:Reference checkBox}, Path=IsChecked}"
Value="True" />
</VisualState.StateTriggers>
<VisualState.Setters>
<Setter Property="BackgroundColor"
Value="Black" />
</VisualState.Setters>
</VisualState>
<VisualState x:Name="Unchecked">
<VisualState.StateTriggers>
<CompareStateTrigger Property="{Binding Source={x:Reference checkBox}, Path=IsChecked}"
Value="False" />
</VisualState.StateTriggers>
<VisualState.Setters>
<Setter Property="BackgroundColor"
Value="White" />
</VisualState.Setters>
</VisualState>
</VisualStateGroup>
</VisualStateGroupList>
</Setter>
</Style>
...
<Grid>
<Frame BackgroundColor="White"
CornerRadius="12"
Margin="24"
HorizontalOptions="Center"
VerticalOptions="Center">
<StackLayout Orientation="Horizontal">
<CheckBox x:Name="checkBox"
VerticalOptions="Center" />
<Label Text="Check the CheckBox to modify the Grid background color."
VerticalOptions="Center" />
</StackLayout>
</Frame>
</Grid>
Nesse exemplo, o Style
implícito tem como destino os objetos Grid
. Quando a propriedade IsChecked
do objeto CheckBox
é false
, a cor da tela de fundo de Grid
é definida como branca. Quando a propriedade CheckBox.IsChecked
se torna true
, uma alteração de VisualState
é disparada, e a cor da tela de fundo de Grid
se torna preta:
Gatilho de estado de dispositivo
O DeviceStateTrigger
dispara uma alteração de VisualState
com base na plataforma do dispositivo na qual o aplicativo está em execução. Esse gatilho tem uma única propriedade associável:
Device
, do tipostring
, que indica a plataforma do dispositivo na qualVisualState
deve ser aplicado.
Observação
DeviceStateTrigger
deriva da classe StateTriggerBase
e, portanto, pode anexar um manipulador de eventos ao evento IsActiveChanged
.
O exemplo de XAML a seguir mostra um Style
que inclui os objetos DeviceStateTrigger
:
<Style x:Key="DeviceStateTriggerPageStyle"
TargetType="ContentPage">
<Setter Property="VisualStateManager.VisualStateGroups">
<VisualStateGroupList>
<VisualStateGroup>
<VisualState x:Name="iOS">
<VisualState.StateTriggers>
<DeviceStateTrigger Device="iOS" />
</VisualState.StateTriggers>
<VisualState.Setters>
<Setter Property="BackgroundColor"
Value="Silver" />
</VisualState.Setters>
</VisualState>
<VisualState x:Name="Android">
<VisualState.StateTriggers>
<DeviceStateTrigger Device="Android" />
</VisualState.StateTriggers>
<VisualState.Setters>
<Setter Property="BackgroundColor"
Value="#2196F3" />
</VisualState.Setters>
</VisualState>
<VisualState x:Name="UWP">
<VisualState.StateTriggers>
<DeviceStateTrigger Device="UWP" />
</VisualState.StateTriggers>
<VisualState.Setters>
<Setter Property="BackgroundColor"
Value="Aquamarine" />
</VisualState.Setters>
</VisualState>
</VisualStateGroup>
</VisualStateGroupList>
</Setter>
</Style>
Nesse exemplo, o Style
explícito tem como destino os objetos ContentPage
. Os objetos ContentPage
que consomem o estilo definem a cor de plano de fundo como prata no iOS, azul claro no Android e água-marinha no UWP. As capturas de tela a seguir mostram as páginas resultantes no iOS e Android:
Gatilho de estado de orientação
O OrientationStateTrigger
dispara uma alteração VisualState
quando a orientação do dispositivo é alterada. Esse gatilho tem uma única propriedade associável:
Orientation
, do tipoDeviceOrientation
, que indica a orientação que deve ser aplicada aVisualState
.
Observação
OrientationStateTrigger
deriva da classe StateTriggerBase
e, portanto, pode anexar um manipulador de eventos ao evento IsActiveChanged
.
O exemplo de XAML a seguir mostra um Style
que inclui os objetos OrientationStateTrigger
:
<Style x:Key="OrientationStateTriggerPageStyle"
TargetType="ContentPage">
<Setter Property="VisualStateManager.VisualStateGroups">
<VisualStateGroupList>
<VisualStateGroup>
<VisualState x:Name="Portrait">
<VisualState.StateTriggers>
<OrientationStateTrigger Orientation="Portrait" />
</VisualState.StateTriggers>
<VisualState.Setters>
<Setter Property="BackgroundColor"
Value="Silver" />
</VisualState.Setters>
</VisualState>
<VisualState x:Name="Landscape">
<VisualState.StateTriggers>
<OrientationStateTrigger Orientation="Landscape" />
</VisualState.StateTriggers>
<VisualState.Setters>
<Setter Property="BackgroundColor"
Value="White" />
</VisualState.Setters>
</VisualState>
</VisualStateGroup>
</VisualStateGroupList>
</Setter>
</Style>
Nesse exemplo, o Style
explícito tem como destino os objetos ContentPage
. Os objetos ContentPage
que consomem o estilo definem sua cor de plano de fundo como prata quando a orientação é retrato e como branco quando a orientação é paisagem.