diff --git a/README.md b/README.md new file mode 100644 index 0000000..b7b7cbb --- /dev/null +++ b/README.md @@ -0,0 +1,421 @@ +# ReactiveMarbles.Command + +A reactive command library for C# applications that encapsulates user actions behind a reactive interface. This library provides a modern, reactive approach to handling commands in user interface applications using Reactive Extensions (Rx). + +## Installation + +```bash +dotnet add package ReactiveMarbles.Command +``` + +## Overview + +ReactiveMarbles.Command provides: +- **RxCommand**: A reactive implementation of ICommand +- **Asynchronous execution**: Built-in support for async operations +- **Execution state tracking**: Monitor when commands are executing +- **Error handling**: Reactive error handling with ThrownExceptions observable +- **Cancellation support**: Built-in cancellation token support +- **Can execute logic**: Reactive can execute conditions + +## Basic Usage + +### Simple Synchronous Command + +```csharp +using ReactiveMarbles.Command; +using System.Reactive; + +// Create a simple command that executes immediately +var saveCommand = RxCommand.Create(() => +{ + Console.WriteLine("Data saved!"); +}); + +// Execute the command +saveCommand.Execute().Subscribe(); +``` + +### Command with Parameter + +```csharp +// Create a command that takes a parameter +var deleteCommand = RxCommand.Create(fileName => +{ + Console.WriteLine($"Deleting file: {fileName}"); +}); + +// Execute with parameter +deleteCommand.Execute("document.txt").Subscribe(); +``` + +### Asynchronous Command + +```csharp +// Create an async command +var loadDataCommand = RxCommand.Create(async () => +{ + await Task.Delay(2000); // Simulate network call + Console.WriteLine("Data loaded!"); +}); + +// Execute and subscribe to results +loadDataCommand.Execute().Subscribe(); +``` + +### Command with Return Value + +```csharp +// Create a command that returns a value +var calculateCommand = RxCommand.Create(number => +{ + var result = number * 2; + return $"Result: {result}"; +}); + +// Execute and handle the result +calculateCommand.Execute(5).Subscribe(result => +{ + Console.WriteLine(result); // Output: "Result: 10" +}); +``` + +## WPF/MVVM Example + +Here's how to use ReactiveMarbles.Command in a WPF MVVM application: + +### ViewModel Implementation + +```csharp +using ReactiveMarbles.Command; +using ReactiveMarbles.Mvvm; +using System.Reactive; +using System.Reactive.Linq; + +public class MainViewModel : RxObject +{ + private string _name = string.Empty; + private bool _isBusy; + + public MainViewModel() + { + // Simple command + SaveCommand = RxCommand.Create(Save); + + // Async command with cancellation + LoadDataCommand = RxCommand.Create(LoadDataAsync); + + // Command with can execute logic + var canDelete = this.WhenAnyValue(x => x.Name) + .Select(name => !string.IsNullOrEmpty(name)); + DeleteCommand = RxCommand.Create(Delete, canDelete); + + // Command that returns a value + ProcessCommand = RxCommand.Create(ProcessData); + + // Subscribe to command results + ProcessCommand.Subscribe(result => + { + Console.WriteLine($"Processing completed with result: {result}"); + }); + + // Handle errors + ProcessCommand.ThrownExceptions.Subscribe(ex => + { + Console.WriteLine($"Error occurred: {ex.Message}"); + }); + + // Track execution state + LoadDataCommand.IsExecuting.Subscribe(isExecuting => + { + IsBusy = isExecuting; + }); + } + + public string Name + { + get => _name; + set => RaiseAndSetIfChanged(ref _name, value); + } + + public bool IsBusy + { + get => _isBusy; + set => RaiseAndSetIfChanged(ref _isBusy, value); + } + + public RxCommand SaveCommand { get; } + public RxCommand LoadDataCommand { get; } + public RxCommand DeleteCommand { get; } + public RxCommand ProcessCommand { get; } + + private void Save() + { + // Save logic here + Console.WriteLine($"Saving: {Name}"); + } + + private async Task LoadDataAsync() + { + // Simulate async operation + await Task.Delay(3000); + Name = "Loaded Data"; + } + + private void Delete() + { + Name = string.Empty; + Console.WriteLine("Deleted!"); + } + + private int ProcessData(string input) + { + if (string.IsNullOrEmpty(input)) + throw new ArgumentException("Input cannot be empty"); + + return input.Length; + } +} +``` + +### XAML Binding + +```xml + + + + + +