Skip to content

[Feature] Enhanced Swipe Control #129

@jsuarezruiz

Description

@jsuarezruiz

Abstract

Swipe gestures have become a fundamental interaction pattern in modern mobile and touch-enabled applications. This proposal provides enhancements to the Swipe control that wraps content and reveals contextual actions through swipe gestures, following established patterns.

Requisites

  • Any direction: Container control that reveals swipe items through directional gestures (left, right, up, down)
  • Support for both single items and collections of swipe items per direction
  • Programmatic control over swipe state (open/close) in addition to gesture-driven interaction
  • Events for swipe lifecycle (opening, opened, closing, closed)
  • Platform-appropriate swipe thresholds and animation behaviors

API

/// <summary>
/// Represents a container control that wraps content and reveals swipe items through directional gestures.
/// </summary>
public class Swipe : ContentControl
{
    /// <summary>
    /// Gets or sets the swipe items displayed when swiping from left to right.
    /// </summary>
    public DataTemplate Left { get; set; }
    
    /// <summary>
    /// Gets or sets the swipe items displayed when swiping from right to left.
    /// </summary>
    public DataTemplate Right{ get; set; }
    
    /// <summary>
    /// Gets or sets the swipe items displayed when swiping from top to bottom.
    /// </summary>
    public DataTemplate Top { get; set; }
    
    /// <summary>
    /// Gets or sets the swipe items displayed when swiping from bottom to top.
    /// </summary>
    public DataTemplate Bottom { get; set; }
    
    /// <summary>
    /// Gets or sets the swipe threshold value (0.0 to 1.0) at which swipe items transition to execute/reveal state.
    /// Default is 0.5 (50% of available space).
    /// </summary>
    public double Threshold { get; set; }
    
    /// <summary>
    /// Programmatically opens the swipe view in the specified direction.
    /// </summary>
    /// <param name="openSwipeItem">The direction to open.</param>
    public void Open(OpenSwipeItem openSwipeItem);
    
    /// <summary>
    /// Programmatically closes any currently open swipe items.
    /// </summary>
    public void Close();
    
    /// <summary>
    /// Occurs when a swipe gesture begins and swipe items start to be revealed.
    /// </summary>
    public event EventHandler<SwipeStartedEventArgs>? SwipeStarted;
    
    /// <summary>
    /// Occurs when swipe items are being revealed during the gesture (continuous updates).
    /// </summary>
    public event EventHandler<SwipeChangingEventArgs>? SwipeChanging;
    
    /// <summary>
    /// Occurs when the swipe gesture ends and swipe items transition to their final state (fully open or closed).
    /// </summary>
    public event EventHandler<SwipeEndedEventArgs>? SwipeEnded;
    
    /// <summary>
    /// Occurs when a SwipeItem is invoked (tapped or executed via threshold).
    /// </summary>
    public event EventHandler<SwipeItemInvokedEventArgs>? SwipeItemInvoked;
}

/// <summary>
/// Represents a collection of SwipeItem objects displayed in a SwipeView.
/// </summary>
public class SwipeItems : AvaloniaList<SwipeItem>
{
    /// <summary>
    /// Gets or sets the behavior mode for this collection of swipe items.
    /// </summary>
    public SwipeMode Mode { get; set; }
    
    /// <summary>
    /// Gets or sets how the swipe items are presented visually.
    /// </summary>
    public SwipeBehaviorOnInvoked SwipeBehaviorOnInvoked { get; set; }
}

/// <summary>
/// Specifies the interaction mode for swipe items.
/// </summary>
public enum SwipeMode
{
    /// <summary>
    /// Swipe items are revealed and remain visible for user selection.
    /// User must tap a swipe item to invoke it.
    /// </summary>
    Reveal = 0,
    
    /// <summary>
    /// When swipe threshold is reached, the first swipe item is executed automatically
    /// without requiring an additional tap.
    /// </summary>
    Execute = 1
}

/// <summary>
/// Specifies the behavior when a swipe item is invoked.
/// </summary>
public enum SwipeBehaviorOnInvoked
{
    /// <summary>
    /// The swipe view automatically closes after the item is invoked.
    /// </summary>
    Auto = 0,
    
    /// <summary>
    /// The swipe view closes after the item is invoked.
    /// </summary>
    Close = 1,
    
    /// <summary>
    /// The swipe view remains open after the item is invoked.
    /// </summary>
    RemainOpen = 2
}

/// <summary>
/// Specifies which swipe items to open programmatically.
/// </summary>
public enum OpenSwipeItem
{
    /// <summary>
    /// Open the left swipe items.
    /// </summary>
    Left = 0,
    
    /// <summary>
    /// Open the right swipe items.
    /// </summary>
    Right = 1,
    
    /// <summary>
    /// Open the top swipe items.
    /// </summary>
    Top = 2,
    
    /// <summary>
    /// Open the bottom swipe items.
    /// </summary>
    Bottom = 3
}

Usage Examples

Programmatic Open/Close

<labs:Swipe x:Name="DemoSwipe"
            OpenRequested="DemoSwipe_OpenRequested"
            CloseRequested="DemoSwipe_CloseRequested"
            Margin="10">
  <labs:Swipe.Content>
    <Border Background="LightGray" Padding="16" CornerRadius="4">
      <TextBlock Text="Swipe or use buttons below" HorizontalAlignment="Center" VerticalAlignment="Center"/>
    </Border>
  </labs:Swipe.Content>

  <labs:Swipe.Right>
    <DataTemplate>
      <Border Background="Blue" Width="48" Height="48" CornerRadius="4">
        <TextBlock Text="R" Foreground="White" HorizontalAlignment="Center" VerticalAlignment="Center"/>
      </Border>
    </DataTemplate>
  </labs:Swipe.Right>

  <labs:Swipe.Left>
    <DataTemplate>
      <Border Background="Green" Width="48" Height="48" CornerRadius="4">
        <TextBlock Text="L" Foreground="White" HorizontalAlignment="Center" VerticalAlignment="Center"/>
      </Border>
    </DataTemplate>
  </labs:Swipe.Left>
</labs:Swipe>

<WrapPanel Orientation="Horizontal" HorizontalAlignment="Center" Spacing="5" Margin="5">
  <Button Content="Open Left" Click="OpenLeft" Width="80" />
  <Button Content="Open Right" Click="OpenRight" Width="80" />
  <Button Content="Close" Click="CloseSwipe" Width="60" />
</WrapPanel>

Swipe List

<ItemsControl ItemsSource="{Binding Items}" Margin="5">
  <ItemsControl.ItemTemplate>
    <DataTemplate>
      <labs:Swipe Margin="2">
        <labs:Swipe.Content>
          <Border Background="LightCoral" Padding="10">
            <TextBlock Text="{Binding Name}" />
          </Border>
        </labs:Swipe.Content>

        <labs:Swipe.Right>
          <DataTemplate>
            <Border Background="Blue" Width="48" Height="48" CornerRadius="4" />
          </DataTemplate>
        </labs:Swipe.Right>

        <labs:Swipe.Left>
          <DataTemplate>
            <Border Background="Green" Width="48" Height="48" CornerRadius="4" />
          </DataTemplate>
        </labs:Swipe.Left>
      </labs:Swipe>
    </DataTemplate>
  </ItemsControl.ItemTemplate>
</ItemsControl>

Summary

Requested changes:

  • Add explicit properties for Left, Right, Top, and Bottom swipe templates as IDataTemplate? to fully support all swipe directions with customizable content.
  • Add Threshold controls how far the user must swipe before the swipe items snap open or execute.
  • Introduce a SwipeDirection enum to cleanly specify directions in API methods and events.
  • Add Open(SwipeDirection direction) and Close() methods for programmatic control of swipe states, enabling external logic to open or close the swipe area in any direction.
  • Add OpenRequested and CloseRequested events to signal when swipe actions start or end, both via gesture and programmatic commands.

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type
No fields configured for issues without a type.

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions