> For the complete documentation index, see [llms.txt](https://elguerre.gitbook.io/de0an/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://elguerre.gitbook.io/de0an/capitulo-9-la-construccion.-navegacion-y-configuracion-de-usuario/acciones-comportamientos-y-triggers.md).

# Acciones, Comportamientos y Triggers

Cuando hablamos de **comportamientos** (o *behaviors*), nos referimos a modificaciones en la manera de actuar de los controles más allá de los cambios de datos, propiedades o eventos tradicionales. Estos comportamientos permiten extender la funcionalidad de los controles de manera modular y reutilizable, facilitando así la implementación de interacciones complejas sin necesidad de modificar directamente el código de los controles base.

## Tipos de Acciones Comunes

A continuación, se presentan algunas de las acciones y comportamientos más comunes que se pueden implementar en una aplicación UWP:

### CallMethodAction

**CallMethodAction** permite invocar métodos específicos en respuesta a eventos desencadenados por la interfaz de usuario. Esto es útil para ejecutar lógica personalizada sin necesidad de manejar eventos directamente en el code-behind.

**Ejemplo de Uso:**

```xml
<Button Content="Haz Click" Width="100" Height="50">
    <i:Interaction.Behaviors>
        <core:EventTriggerBehavior EventName="Click">
            <core:CallMethodAction MethodName="OnButtonClick" TargetObject="{Binding}" />
        </core:EventTriggerBehavior>
    </i:Interaction.Behaviors>
</Button>
```

```csharp
public class MainViewModel
{
    public void OnButtonClick()
    {
        // Lógica a ejecutar cuando se hace clic en el botón
        Debug.WriteLine("Botón clickeado!");
    }
}
```

### ChangePropertyAction

**ChangePropertyAction** facilita el cambio de propiedades de un control en respuesta a ciertos eventos o condiciones. Esto permite modificar la apariencia o comportamiento de los controles de manera dinámica.

**Ejemplo de Uso:**

```xml
<TextBlock Text="Estado Inicial" x:Name="statusText">
    <i:Interaction.Behaviors>
        <core:EventTriggerBehavior EventName="PointerEntered">
            <core:ChangePropertyAction TargetObject="{Binding ElementName=statusText}" PropertyName="Text" Value="Puntero sobre el texto"/>
        </core:EventTriggerBehavior>
        <core:EventTriggerBehavior EventName="PointerExited">
            <core:ChangePropertyAction TargetObject="{Binding ElementName=statusText}" PropertyName="Text" Value="Estado Inicial"/>
        </core:EventTriggerBehavior>
    </i:Interaction.Behaviors>
</TextBlock>
```

### DataTriggerBehavior

**DataTriggerBehavior** simplifica la lógica para mostrar u ocultar controles basándose en condiciones específicas de los datos. En lugar de duplicar controles para manejar visibilidad, se puede utilizar este comportamiento para modificar propiedades dinámicamente.

**Ejemplo de Uso:**

```xml
<Image x:Name="imgBookmark" Width="48" Height="48"
       Source="/Assets/Bookmark.png" Stretch="Uniform"
       HorizontalAlignment="Right" VerticalAlignment="Top"
       Margin="0,-5,-10,0"
       Visibility="{Binding IsFavorite, Mode=TwoWay,
             Converter={StaticResource BooleanToVisibilityConverter}}">
  <i:Interaction.Behaviors>
    <core:DataTriggerBehavior Binding="{Binding Image}"
                      Value="{x:Null}" ComparisonCondition="NotEqual">
      <core:ChangePropertyAction TargetObject="{Binding ElementName=imgBookmark}" PropertyName="VerticalAlignment"
                     Value="Bottom"/>
      <core:ChangePropertyAction TargetObject="{Binding ElementName=imgBookmark}" PropertyName="Margin"
                     Value="0,0,-10,2"/>
    </core:DataTriggerBehavior>
  </i:Interaction.Behaviors>
</Image>
```

En este ejemplo, si la propiedad `Image` no es nula, se ajustan las propiedades `VerticalAlignment` y `Margin` del `Image` para posicionarlo correctamente.

### GoToStateAction

**GoToStateAction** permite cambiar entre diferentes estados visuales definidos por el `VisualStateManager` en respuesta a eventos específicos. Esto es útil para gestionar transiciones visuales complejas de manera declarativa.

**Ejemplo de Uso:**

```xml
<Grid>
    <VisualStateManager.VisualStateGroups>
        <VisualStateGroup x:Name="CommonStates">
            <VisualState x:Name="Normal" />
            <VisualState x:Name="PointerOver">
                <Storyboard>
                    <ColorAnimation Storyboard.TargetName="background" Storyboard.TargetProperty="(Shape.Fill).(SolidColorBrush.Color)" To="LightBlue" Duration="0:0:0.2"/>
                </Storyboard>
            </VisualState>
        </VisualStateGroup>
    </VisualStateManager.VisualStateGroups>

    <Rectangle x:Name="background" Fill="Blue" Width="200" Height="100">
        <i:Interaction.Behaviors>
            <core:EventTriggerBehavior EventName="PointerEntered">
                <core:GoToStateAction StateName="PointerOver" UseTransitions="True"/>
            </core:EventTriggerBehavior>
            <core:EventTriggerBehavior EventName="PointerExited">
                <core:GoToStateAction StateName="Normal" UseTransitions="True"/>
            </core:EventTriggerBehavior>
        </i:Interaction.Behaviors>
    </Rectangle>
</Grid>
```

### InvokeCommandAction

**InvokeCommandAction** permite enlazar comandos de MVVM a eventos de la interfaz de usuario, facilitando una arquitectura limpia y separada entre la vista y el modelo de vista.

**Ejemplo de Uso:**

```xml
<Button Content="Guardar">
    <i:Interaction.Behaviors>
        <core:EventTriggerBehavior EventName="Click">
            <core:InvokeCommandAction Command="{Binding SaveCommand}" />
        </core:EventTriggerBehavior>
    </i:Interaction.Behaviors>
</Button>
```

```csharp
public class MainViewModel
{
    public ICommand SaveCommand { get; }

    public MainViewModel()
    {
        SaveCommand = new RelayCommand(Save);
    }

    private void Save()
    {
        // Lógica para guardar datos
        Debug.WriteLine("Datos guardados!");
    }
}
```

### NavigateToPageAction

**NavigateToPageAction** facilita la navegación a diferentes páginas dentro de la aplicación en respuesta a eventos específicos.

**Ejemplo de Uso:**

```xml
<Button Content="Ir a Detalles">
    <i:Interaction.Behaviors>
        <core:EventTriggerBehavior EventName="Click">
            <core:NavigateToPageAction PageType="{x:Type local:DetailsPage}" />
        </core:EventTriggerBehavior>
    </i:Interaction.Behaviors>
</Button>
```

## Creación de Acciones y Comportamientos Personalizados

En algunos casos, las acciones y comportamientos predeterminados no cubren todas las necesidades de la aplicación. En tales situaciones, es posible crear acciones y comportamientos personalizados.

### Implementación de HubSectionSelectedAction

Necesitamos crear una acción personalizada para trabajar con el control *Hub* en la página principal, específicamente para detectar cuándo el usuario se desplaza entre sus secciones. Aunque este comportamiento es fundamental para el control *Hub*, no está disponible de forma nativa para el patrón **MVVM**, por lo que implementaremos nuestra propia acción.

**Paso 1: Incluir los Namespaces Necesarios**

Al trabajar con **Behaviors** y **Actions**, debemos incluir los siguientes namespaces en el XAML:

```xml
xmlns:i="using:Microsoft.Xaml.Interactivity"
xmlns:core="using:Microsoft.Xaml.Interactions.Core"
xmlns:actions="using:MyApp.Actions"
```

**Paso 2: Definir la Acción en XAML**

Añadiremos la acción personalizada al control *Hub* para detectar cambios en las secciones visibles.

```xml
<Hub x:Name="MainHub" HeaderTemplate="{Binding Source={StaticResource AppHeader}}">
    <i:Interaction.Behaviors>
        <i:BehaviorCollection>
            <core:EventTriggerBehavior EventName="SectionsInViewChanged">
                <actions:HubSectionSelectedAction FirstSection="{Binding FirstHubSection, Mode=TwoWay}" />
            </core:EventTriggerBehavior>
        </i:BehaviorCollection>
    </i:Interaction.Behaviors>
    <!-- Secciones del Hub -->
</Hub>
```

**Paso 3: Implementar la Clase de la Acción Personalizada**

Creamos una clase `HubSectionSelectedAction` que implementa la interfaz `IAction` para manejar el evento `SectionsInViewChanged`.

```csharp
public sealed class HubSectionSelectedAction : DependencyObject, IAction
{
    public object FirstSection
    {
        get => (object)GetValue(FirstSectionProperty);
        set => SetValue(FirstSectionProperty, value);
    }

    public static readonly DependencyProperty FirstSectionProperty =
        DependencyProperty.Register("FirstSection", typeof(object), typeof(HubSectionSelectedAction), new PropertyMetadata(null));

    public HubSectionSelectedAction()
    {
    }

    public object Execute(object sender, object parameter)
    {
        if (sender is Hub hub)
        {
            if (hub.SectionsInView.Count > 0)
            {
                FirstSection = hub.SectionsInView[0].Name;
            }
        }
        return null;
    }
}
```

**Paso 4: Actualizar el ViewModel**

En el `MainViewModel`, añadimos la propiedad `FirstHubSection` para almacenar la sección actualmente visible.

```csharp
public class MainViewModel : INotifyPropertyChanged
{
    private string _firstHubSection;
    public string FirstHubSection
    {
        get => _firstHubSection;
        set
        {
            if (_firstHubSection != value)
            {
                _firstHubSection = value;
                OnPropertyChanged();
                // Lógica adicional cuando cambia la sección
            }
        }
    }

    public async Task LoadDataAsync()
    {
        // Lógica para cargar datos
    }

    public event PropertyChangedEventHandler PropertyChanged;
    protected void OnPropertyChanged([CallerMemberName] string name = null) =>
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
}
```

## Nuevos Behaviors y Acciones Personalizadas

Además de las acciones y comportamientos estándar, es posible que necesitemos implementar acciones personalizadas para cubrir necesidades específicas de nuestra aplicación.

### HoldingAction

Por ejemplo, para manejar eventos de sostenido (*holding*) en una lista de tareas, podemos crear una acción personalizada denominada `HoldingAction` que realice acciones como llamar o enviar un correo electrónico directamente desde la interfaz.

**Implementación de HoldingAction:**

```csharp
using Microsoft.Xaml.Interactivity;
using Microsoft.Xaml.Interactions.Core;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;

namespace MyApp.Actions
{
    public sealed class HoldingAction : DependencyObject, IAction
    {
        public TaskItem Task
        {
            get => (TaskItem)GetValue(TaskProperty);
            set => SetValue(TaskProperty, value);
        }

        public static readonly DependencyProperty TaskProperty =
            DependencyProperty.Register("Task", typeof(TaskItem), typeof(HoldingAction), new PropertyMetadata(null));

        public HoldingAction()
        {
        }

        public object Execute(object sender, object parameter)
        {
            if (Task != null)
            {
                // Lógica para enviar un correo electrónico relacionado con la tarea
                EmailService.SendTaskEmail(Task);
            }
            return null;
        }
    }
}
```

**Uso en XAML:**

```xml
<ListView ItemsSource="{x:Bind ViewModel.Tasks}" SelectionMode="None">
    <ListView.ItemTemplate>
        <DataTemplate>
            <StackPanel>
                <TextBlock Text="{Binding Title}" FontSize="16" />
                <TextBlock Text="{Binding DueDate}" FontSize="12" Foreground="Gray" />
            </StackPanel>
            <i:Interaction.Behaviors>
                <core:EventTriggerBehavior EventName="Holding">
                    <actions:HoldingAction Task="{Binding}" />
                </core:EventTriggerBehavior>
            </i:Interaction.Behaviors>
        </DataTemplate>
    </ListView.ItemTemplate>
</ListView>
```

## Uso de los Namespaces Necesarios

Para utilizar **Behaviors** y **Actions** en XAML, es necesario incluir los siguientes namespaces:

```xml
xmlns:i="using:Microsoft.Xaml.Interactivity"
xmlns:core="using:Microsoft.Xaml.Interactions.Core"
xmlns:actions="using:MyApp.Actions"
```

Estos namespaces permiten acceder a las clases necesarias para implementar y utilizar comportamientos y acciones personalizadas.

## Ejemplos Prácticos

A continuación, se presentan algunos ejemplos prácticos que ilustran cómo utilizar diferentes acciones y comportamientos para mejorar la interactividad de la aplicación.

### Ejemplo 1: Cambio de Propiedad en Respuesta a un Evento

En este ejemplo, cambiamos la opacidad de un control cuando se hace clic en un botón.

```xml
<Button Content="Reducir Opacidad" Width="150" Height="50">
    <i:Interaction.Behaviors>
        <core:EventTriggerBehavior EventName="Click">
            <core:ChangePropertyAction TargetObject="{Binding ElementName=targetRectangle}" PropertyName="Opacity" Value="0.5"/>
        </core:EventTriggerBehavior>
    </i:Interaction.Behaviors>
</Button>

<Rectangle x:Name="targetRectangle" Width="200" Height="100" Fill="Green" Opacity="1.0"/>
```

### Ejemplo 2: Invocación de un Comando MVVM

En este ejemplo, vinculamos un evento de clic a un comando en el ViewModel.

```xml
<Button Content="Guardar" Width="100" Height="40">
    <i:Interaction.Behaviors>
        <core:EventTriggerBehavior EventName="Click">
            <core:InvokeCommandAction Command="{Binding SaveCommand}" />
        </core:EventTriggerBehavior>
    </i:Interaction.Behaviors>
</Button>
```

```csharp
public class MainViewModel : INotifyPropertyChanged
{
    public ICommand SaveCommand { get; }

    public MainViewModel()
    {
        SaveCommand = new RelayCommand(Save);
    }

    private void Save()
    {
        // Lógica para guardar datos
        Debug.WriteLine("Datos guardados!");
    }

    public event PropertyChangedEventHandler PropertyChanged;
    protected void OnPropertyChanged([CallerMemberName] string name = null) =>
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
}
```

### Ejemplo 3: Navegación a Otra Página

En este ejemplo, un botón navega a una página de detalles cuando se hace clic.

```xml
<Button Content="Ir a Detalles" Width="150" Height="50">
    <i:Interaction.Behaviors>
        <core:EventTriggerBehavior EventName="Click">
            <core:NavigateToPageAction PageType="{x:Type local:DetailsPage}" />
        </core:EventTriggerBehavior>
    </i:Interaction.Behaviors>
</Button>
```

### Ejemplo 4: Animación en Respuesta a un Evento

En este ejemplo, aplicamos una animación cuando el usuario interactúa con un control.

```xml
<Grid>
    <VisualStateManager.VisualStateGroups>
        <VisualStateGroup x:Name="AnimationStates">
            <VisualState x:Name="Normal" />
            <VisualState x:Name="Animated">
                <Storyboard>
                    <DoubleAnimation Storyboard.TargetName="animatedElement" Storyboard.TargetProperty="Opacity" To="1" Duration="0:0:0.5"/>
                </Storyboard>
            </VisualState>
        </VisualStateGroup>
    </VisualStateManager.VisualStateGroups>

    <Rectangle x:Name="animatedElement" Width="100" Height="100" Fill="Red" Opacity="0">
        <i:Interaction.Behaviors>
            <core:EventTriggerBehavior EventName="PointerEntered">
                <core:GoToStateAction StateName="Animated" />
            </core:EventTriggerBehavior>
            <core:EventTriggerBehavior EventName="PointerExited">
                <core:GoToStateAction StateName="Normal" />
            </core:EventTriggerBehavior>
        </i:Interaction.Behaviors>
    </Rectangle>
</Grid>
```

En este caso, al pasar el puntero sobre el rectángulo, su opacidad aumenta mediante una animación definida en el estado "Animated".

## Ventajas de Usar Behaviors y Actions

El uso de **Behaviors** y **Actions** ofrece múltiples ventajas en el desarrollo de aplicaciones UWP:

* **Separación de Responsabilidades:** Permite mantener el código de la interfaz de usuario limpio y separado de la lógica de negocio, facilitando el mantenimiento y la escalabilidad. Los **Behaviors** permiten que las interacciones con los controles no estén acopladas al code-behind, promoviendo una mejor adherencia al patrón **MVVM**.
* **Reutilización de Código:** Los comportamientos y acciones pueden ser reutilizados en múltiples controles y páginas, lo que reduce la duplicación de código. Al diseñar **Actions** personalizadas, puedes extender fácilmente la funcionalidad a otros controles sin necesidad de escribir código adicional cada vez.
* **Flexibilidad y Modularidad:** Facilita la implementación de interacciones complejas sin necesidad de extender o modificar los controles base. Puedes agregar comportamientos dinámicos sin alterar el comportamiento básico de los controles, permitiendo personalizaciones sobre la marcha en función de eventos o condiciones específicas.
* **Compatibilidad con MVVM:** Mejora la adherencia al patrón **MVVM** al permitir la vinculación de comandos y propiedades directamente desde el ViewModel. Esto garantiza una separación clara entre la lógica de presentación y la lógica de negocio.

## Técnicas para Crear Plantillas de Datos Dinámicas

Existen diversas técnicas para crear plantillas de datos dinámicas en aplicaciones UWP, entre las cuales se incluyen:

* **Converters (Convertidores):** Permiten convertir valores de una propiedad a otra durante el enlace de datos. Aunque son útiles, pueden impactar en el rendimiento cuando se aplican en listas grandes debido al procesamiento adicional requerido.
* **VisualStateManager + DataTriggerBehavior:** Gestiona diferentes estados visuales de los controles de manera eficiente. El uso combinado del **VisualStateManager** con **DataTriggerBehavior** permite cambiar propiedades de los controles basándose en cambios en los datos, mejorando el rendimiento y la responsividad.
* **DataTemplateSelector:** Permite seleccionar dinámicamente una plantilla de datos basada en condiciones lógicas. Es ideal para escenarios donde se requiere cambiar completamente la apariencia de un elemento en función de su estado o tipo.

## Mejora de Rendimiento con VisualStateManager

El **VisualStateManager** mejora el rendimiento en comparación con los converters, especialmente en escenarios donde se manejan listas grandes. Al utilizar **VisualStateManager**, los cambios en la interfaz de usuario se manejan de manera declarativa y están optimizados por el motor de renderizado de XAML.

**Ejemplo de Mejora de Rendimiento:**

Supongamos que tenemos una lista grande y queremos cambiar el color de fondo de un elemento seleccionado. Utilizar un converter para cambiar el color puede resultar en tiempos de refresco más largos. En su lugar, podemos utilizar **VisualStateManager** y **DataTriggerBehavior** para lograr el mismo efecto de manera más eficiente.

```xml
<ListView ItemsSource="{Binding Items}">
    <ListView.ItemTemplate>
        <DataTemplate>
            <Grid>
                <VisualStateManager.VisualStateGroups>
                    <VisualStateGroup x:Name="SelectionStates">
                        <VisualState x:Name="Unselected" />
                        <VisualState x:Name="Selected">
                            <Storyboard>
                                <ColorAnimation Storyboard.TargetName="itemBackground" Storyboard.TargetProperty="(Border.Background).(SolidColorBrush.Color)" To="LightBlue" Duration="0"/>
                            </Storyboard>
                        </VisualState>
                    </VisualStateGroup>
                </VisualStateManager.VisualStateGroups>

                <Border x:Name="itemBackground" Background="White">
                    <TextBlock Text="{Binding Name}" />
                </Border>
            </Grid>
            <i:Interaction.Behaviors>
                <core:DataTriggerBehavior Binding="{Binding IsSelected, RelativeSource={RelativeSource Mode=TemplatedParent}}" Value="True">
                    <core:GoToStateAction StateName="Selected" />
                </core:DataTriggerBehavior>
                <core:DataTriggerBehavior Binding="{Binding IsSelected, RelativeSource={RelativeSource Mode=TemplatedParent}}" Value="False">
                    <core:GoToStateAction StateName="Unselected" />
                </core:DataTriggerBehavior>
            </i:Interaction.Behaviors>
        </DataTemplate>
    </ListView.ItemTemplate>
</ListView>
```

En este ejemplo, cuando un elemento es seleccionado, se cambia el estado visual a "Selected" y se actualiza el color de fondo sin necesidad de utilizar converters, mejorando así el rendimiento.

## Uso de DataTriggerBehavior

El **DataTriggerBehavior** es particularmente útil para cambiar propiedades de controles en respuesta a cambios en los datos. Esto evita la necesidad de duplicar controles o utilizar convertidores complejos.

**Ejemplo de Uso de DataTriggerBehavior:**

```xml
<core:DataTriggerBehavior Binding="{Binding Image}" Value="{x:Null}" ComparisonCondition="NotEqual">
    <core:ChangePropertyAction TargetObject="{Binding ElementName=borderTitle}" PropertyName="Opacity" Value="0.75"/>
    <core:ChangePropertyAction TargetObject="{Binding ElementName=imgBookmark}" PropertyName="VerticalAlignment" Value="Bottom"/>
    <core:ChangePropertyAction TargetObject="{Binding ElementName=imgBookmark}" PropertyName="Margin" Value="0,0,-10,2"/>
</core:DataTriggerBehavior>
```

En este fragmento, si la propiedad `Image` no es nula, se realizan varios cambios en las propiedades de los controles asociados, ajustando su apariencia y posición en la interfaz de usuario.

## Consideraciones Finales

Al implementar **Acciones**, **Comportamientos** y **Triggers**, es importante tener en cuenta las siguientes recomendaciones:

* **Optimizar para el Rendimiento:** Siempre que sea posible, utiliza **VisualStateManager** y **DataTriggerBehavior** en lugar de converters para mejorar el rendimiento, especialmente en listas grandes.
* **Modularidad y Reutilización:** Diseña tus behaviors y actions de manera que puedan ser reutilizados en diferentes partes de la aplicación, promoviendo una arquitectura modular y mantenible.
* **Consistencia en la Interfaz de Usuario:** Asegúrate de que los cambios en los estados visuales sean coherentes y mejoren la experiencia del usuario, evitando cambios abruptos o inconsistentes.
* **Documentación y Mantenimiento:** Documenta tus acciones y comportamientos personalizados para facilitar el mantenimiento y la comprensión por parte de otros desarrolladores.

## Conclusión

La implementación de **Acciones**, **Comportamientos** y **Triggers** en tu aplicación UWP permite una mayor flexibilidad y modularidad en la interfaz de usuario. Al aprovechar estos mecanismos, puedes extender la funcionalidad de los controles de manera eficiente, mantener una arquitectura limpia y mejorar la experiencia del usuario. Además, la creación de acciones y comportamientos personalizados te permite adaptar la aplicación a necesidades específicas, asegurando que la interacción con el usuario sea intuitiva y responsiva.
