diff --git a/StabilityMatrix.Avalonia/App.axaml b/StabilityMatrix.Avalonia/App.axaml
index fa522b62..01828799 100644
--- a/StabilityMatrix.Avalonia/App.axaml
+++ b/StabilityMatrix.Avalonia/App.axaml
@@ -57,6 +57,7 @@
+
+
diff --git a/StabilityMatrix.Avalonia/Controls/Paginator.axaml.cs b/StabilityMatrix.Avalonia/Controls/Paginator.axaml.cs
new file mode 100644
index 00000000..9342ec12
--- /dev/null
+++ b/StabilityMatrix.Avalonia/Controls/Paginator.axaml.cs
@@ -0,0 +1,166 @@
+using System.Diagnostics.CodeAnalysis;
+using System.Windows.Input;
+using Avalonia;
+using Avalonia.Controls.Primitives;
+using AvaloniaEdit.Utils;
+using CommunityToolkit.Mvvm.Input;
+
+namespace StabilityMatrix.Avalonia.Controls;
+
+[SuppressMessage("ReSharper", "MemberCanBePrivate.Global")]
+public class Paginator : TemplatedControl
+{
+ private bool isFirstTemplateApplied;
+ private ICommand? firstPageCommandBinding;
+ private ICommand? previousPageCommandBinding;
+ private ICommand? nextPageCommandBinding;
+ private ICommand? lastPageCommandBinding;
+
+ public static readonly StyledProperty CurrentPageNumberProperty =
+ AvaloniaProperty.Register("CurrentPageNumber", 1);
+
+ public int CurrentPageNumber
+ {
+ get => GetValue(CurrentPageNumberProperty);
+ set => SetValue(CurrentPageNumberProperty, value);
+ }
+
+ public static readonly StyledProperty TotalPagesProperty = AvaloniaProperty.Register<
+ Paginator,
+ int
+ >("TotalPages", 1);
+
+ public int TotalPages
+ {
+ get => GetValue(TotalPagesProperty);
+ set => SetValue(TotalPagesProperty, value);
+ }
+
+ public static readonly StyledProperty FirstPageCommandProperty =
+ AvaloniaProperty.Register("FirstPageCommand");
+
+ public ICommand? FirstPageCommand
+ {
+ get => GetValue(FirstPageCommandProperty);
+ set => SetValue(FirstPageCommandProperty, value);
+ }
+
+ public static readonly StyledProperty PreviousPageCommandProperty =
+ AvaloniaProperty.Register("PreviousPageCommand");
+
+ public ICommand? PreviousPageCommand
+ {
+ get => GetValue(PreviousPageCommandProperty);
+ set => SetValue(PreviousPageCommandProperty, value);
+ }
+
+ public static readonly StyledProperty NextPageCommandProperty =
+ AvaloniaProperty.Register("NextPageCommand");
+
+ public ICommand? NextPageCommand
+ {
+ get => GetValue(NextPageCommandProperty);
+ set => SetValue(NextPageCommandProperty, value);
+ }
+
+ public static readonly StyledProperty LastPageCommandProperty =
+ AvaloniaProperty.Register("LastPageCommand");
+
+ public ICommand? LastPageCommand
+ {
+ get => GetValue(LastPageCommandProperty);
+ set => SetValue(LastPageCommandProperty, value);
+ }
+
+ public static readonly StyledProperty CanNavForwardProperty = AvaloniaProperty.Register<
+ Paginator,
+ bool
+ >("CanNavForward");
+
+ public bool CanNavForward
+ {
+ get => GetValue(CanNavForwardProperty);
+ set => SetValue(CanNavForwardProperty, value);
+ }
+
+ public static readonly StyledProperty CanNavBackProperty = AvaloniaProperty.Register<
+ Paginator,
+ bool
+ >("CanNavBack");
+
+ public bool CanNavBack
+ {
+ get => GetValue(CanNavBackProperty);
+ set => SetValue(CanNavBackProperty, value);
+ }
+
+ ///
+ protected override void OnApplyTemplate(TemplateAppliedEventArgs e)
+ {
+ base.OnApplyTemplate(e);
+
+ if (!isFirstTemplateApplied)
+ {
+ firstPageCommandBinding = FirstPageCommand;
+ previousPageCommandBinding = PreviousPageCommand;
+ nextPageCommandBinding = NextPageCommand;
+ lastPageCommandBinding = LastPageCommand;
+ isFirstTemplateApplied = true;
+ }
+
+ // Wrap the commands
+ FirstPageCommand = new RelayCommand(() =>
+ {
+ if (CurrentPageNumber > 1)
+ {
+ CurrentPageNumber = 1;
+ }
+ firstPageCommandBinding?.Execute(null);
+ });
+
+ PreviousPageCommand = new RelayCommand(() =>
+ {
+ if (CurrentPageNumber > 1)
+ {
+ CurrentPageNumber--;
+ }
+ previousPageCommandBinding?.Execute(null);
+ });
+
+ NextPageCommand = new RelayCommand(() =>
+ {
+ if (CurrentPageNumber < TotalPages)
+ {
+ CurrentPageNumber++;
+ }
+ nextPageCommandBinding?.Execute(null);
+ });
+
+ LastPageCommand = new RelayCommand(() =>
+ {
+ if (CurrentPageNumber < TotalPages)
+ {
+ CurrentPageNumber = TotalPages;
+ }
+ lastPageCommandBinding?.Execute(null);
+ });
+ }
+
+ ///
+ protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change)
+ {
+ base.OnPropertyChanged(change);
+
+ // Update the CanNavForward and CanNavBack properties
+ if (change.Property == CurrentPageNumberProperty && change.NewValue is int)
+ {
+ CanNavForward = (int)change.NewValue < TotalPages;
+ CanNavBack = (int)change.NewValue > 1;
+ }
+ else if (change.Property == TotalPagesProperty && change.NewValue is int)
+ {
+ CanNavForward = CurrentPageNumber < (int)change.NewValue;
+ CanNavBack = CurrentPageNumber > 1;
+ }
+ }
+}
diff --git a/StabilityMatrix.Avalonia/Converters/StringFormatConverters.cs b/StabilityMatrix.Avalonia/Converters/StringFormatConverters.cs
new file mode 100644
index 00000000..e4ed2455
--- /dev/null
+++ b/StabilityMatrix.Avalonia/Converters/StringFormatConverters.cs
@@ -0,0 +1,10 @@
+using Avalonia.Data.Converters;
+
+namespace StabilityMatrix.Avalonia.Converters;
+
+public static class StringFormatConverters
+{
+ private static StringFormatValueConverter? _decimalConverter;
+ public static StringFormatValueConverter Decimal =>
+ _decimalConverter ??= new StringFormatValueConverter("{0:D}", null);
+}