Command Names
Modern Approach (Recommended)
With ReactiveUI.SourceGenerators, commands are automatically generated with a Command suffix. This is the recommended naming convention for modern ReactiveUI applications.
Using ReactiveUI.SourceGenerators ?
When using the [ReactiveCommand] attribute, the generator automatically creates a property with the Command suffix:
using ReactiveUI;
using ReactiveUI.SourceGenerators;
public partial class MyViewModel : ReactiveObject
{
// Method name: Synchronize
// Generated property: SynchronizeCommand
[ReactiveCommand]
private async Task Synchronize()
{
await SynchronizeImpl(mergeInsteadOfRebase: !IsAhead);
}
private async Task SynchronizeImpl(bool mergeInsteadOfRebase)
{
// Implementation here
}
}
// Usage in view:
this.BindCommand(ViewModel, vm => vm.SynchronizeCommand, v => v.SyncButton);
Benefits:
- ? Consistent naming convention
- ? Clear distinction between methods and commands
- ? Matches platform conventions (WPF, MAUI)
- ? Generated code is predictable
- ? Less boilerplate code
Standard ReactiveCommand Pattern
When creating commands manually (without source generators), suffix command properties with Command:
using ReactiveUI;
using System.Reactive;
public class MyViewModel : ReactiveObject
{
// Property name includes 'Command' suffix
public ReactiveCommand<Unit, Unit> SynchronizeCommand { get; }
public MyViewModel()
{
SynchronizeCommand = ReactiveCommand.CreateFromTask(
() => SynchronizeImpl(mergeInsteadOfRebase: !IsAhead));
}
private async Task SynchronizeImpl(bool mergeInsteadOfRebase)
{
// Implementation here
}
}
// Usage in view:
this.BindCommand(ViewModel, vm => vm.SynchronizeCommand, v => v.SyncButton);
Naming Conventions
Command Property Names
Use verbs that describe the command's action, with Command suffix:
// Good ?
public ReactiveCommand<Unit, Unit> SaveCommand { get; }
public ReactiveCommand<Unit, Unit> DeleteCommand { get; }
public ReactiveCommand<Unit, Unit> RefreshCommand { get; }
public ReactiveCommand<string, Unit> SearchCommand { get; }
// Avoid ?
public ReactiveCommand<Unit, Unit> Save { get; } // Missing 'Command' suffix
public ReactiveCommand<Unit, Unit> PerformSave { get; } // Unclear naming
Implementation Method Names
When implementation is too complex for inline code, suffix the method with Impl:
public partial class MyViewModel : ReactiveObject
{
[ReactiveCommand]
private async Task Save()
{
await SaveImpl();
}
[ReactiveCommand]
private async Task Delete()
{
await DeleteImpl();
}
// Implementation methods
private async Task SaveImpl() { /* ... */ }
private async Task DeleteImpl() { /* ... */ }
}
Complete Examples
Example 1: Simple Command with Source Generators
public partial class MainViewModel : ReactiveObject
{
[Reactive]
private string _searchText = string.Empty;
// Generates: public ReactiveCommand<Unit, Unit> SearchCommand { get; }
[ReactiveCommand]
private async Task Search()
{
var results = await SearchImpl(SearchText);
Results = results;
}
private async Task<List<SearchResult>> SearchImpl(string searchText)
{
// Actual search implementation
return await _searchService.SearchAsync(searchText);
}
}
Example 2: Command with Parameter
public partial class ItemViewModel : ReactiveObject
{
// Generates: public ReactiveCommand<Item, Unit> DeleteItemCommand { get; }
[ReactiveCommand]
private async Task DeleteItem(Item item)
{
await DeleteItemImpl(item);
}
private async Task DeleteItemImpl(Item item)
{
await _repository.DeleteAsync(item);
}
}
Example 3: Command with CanExecute
public partial class EditViewModel : ReactiveObject
{
[Reactive]
private bool _isValid;
private IObservable<bool> _canSave;
public EditViewModel()
{
_canSave = this.WhenAnyValue(x => x.IsValid);
}
// Generates: public ReactiveCommand<Unit, Unit> SaveCommand { get; }
[ReactiveCommand(CanExecute = nameof(_canSave))]
private async Task Save()
{
await SaveImpl();
}
private async Task SaveImpl()
{
await _dataService.SaveAsync(Data);
}
}
Example 4: Manual Command Creation
public class LegacyViewModel : ReactiveObject
{
// When not using source generators, explicitly include 'Command' suffix
public ReactiveCommand<Unit, Unit> LoadDataCommand { get; }
public ReactiveCommand<string, Unit> FilterCommand { get; }
public ReactiveCommand<Unit, Unit> ClearCommand { get; }
public LegacyViewModel()
{
LoadDataCommand = ReactiveCommand.CreateFromTask(LoadDataImpl);
FilterCommand = ReactiveCommand.Create<string>(filter =>
FilterImpl(filter));
var canClear = this.WhenAnyValue(x => x.HasData);
ClearCommand = ReactiveCommand.Create(ClearImpl, canClear);
}
private async Task LoadDataImpl() { /* ... */ }
private void FilterImpl(string filter) { /* ... */ }
private void ClearImpl() { /* ... */ }
}
Legacy Code Warning
Note: Older ReactiveUI code may not follow the
Commandsuffix convention. When modernizing legacy code:
- Update property names to include
Commandsuffix- Migrate to ReactiveUI.SourceGenerators where possible
- Update view bindings to reference new property names
Migration Example
Before (Legacy):
public ReactiveCommand Synchronize { get; private set; }
Synchronize = ReactiveCommand.CreateFromObservable(
_ => SynchronizeImpl(mergeInsteadOfRebase: !IsAhead));
After (Modern):
[ReactiveCommand]
private async Task Synchronize()
{
await SynchronizeImpl(mergeInsteadOfRebase: !IsAhead);
}
Best Practices
- Always use
Commandsuffix - Makes commands easily identifiable - Use source generators - Reduces boilerplate and ensures consistency
- Verb-based names - Clearly describe the action (Save, Delete, Refresh)
- Suffix complex implementations with
Impl- Separates interface from implementation - Consistent casing - Use PascalCase for command properties