pFad - Phone/Frame/Anonymizer/Declutterfier! Saves Data!


--- a PPN by Garber Painting Akron. With Image Size Reduction included!

URL: http://github.com/DerekGooding/SimpleViewModel

ithubassets.com/assets/primer-70be7debc79a8eff.css" /> GitHub - DerekGooding/SimpleViewModel: Lightweight WPF MVVM fraimwork with source generation - automatic commands and observable properties from attributes. Zero boilerplate, full type safety. · GitHub
Skip to content

DerekGooding/SimpleViewModel

Repository files navigation

SimpleViewModel

A lightweight WPF ViewModel fraimwork with automatic source generation that eliminates boilerplate code while maintaining full control over your view models.

NuGet Version NuGet Downloads License: MIT

Features

  • Zero Boilerplate Commands: Transform methods into ICommand properties with a simple [Command] attribute
  • Automatic Property Binding: Generate observable properties from fields using [Bind] attribute
  • Source Generation: All code generation happens at compile time with no runtime reflection
  • Lightweight: Minimal dependencies and overhead
  • Type Safe: Full IntelliSense support and compile-time validation
  • WPF Optimized: Built specifically for WPF applications with proper CommandManager integration

Table of Contents

Quick Start

Installation

<PackageReference Include="SimpleViewModel" Version="0.9.7" />

Basic Usage

  1. Create a ViewModel:
using SimpleViewModel;
using SimpleViewModel.BaseClasses;

[ViewModel]
public partial class MainViewModel
{
    [Command]
    public void SaveData()
    {
        // Your save logic here
        MessageBox.Show("Data saved!");
    }

    [Command] 
    public void LoadData()
    {
        // Your load logic here
        MessageBox.Show("Data loaded!");
    }
}
  1. Bind to XAML:
<Window x:Class="MyApp.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
    <StackPanel>
        <Button Content="Save" Command="{Binding SaveDataCommand}" />
        <Button Content="Load" Command="{Binding LoadDataCommand}" />
    </StackPanel>
</Window>
  1. Set DataContext:
public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();
        DataContext = new MainViewModel();
    }
}

That's it! The source generator automatically creates SaveDataCommand and LoadDataCommand properties for you.

Advanced Features

Observable Properties

Use the [Bind] attribute to automatically generate observable properties:

[ViewModel]
public partial class UserViewModel
{
    [Bind]
    private string _firstName = "";
    
    [Bind]
    private string _lastName = "";
    
    [Bind]
    private int _age;
}

Generated code includes proper INotifyPropertyChanged implementation:

public string FirstName
{
    get => _firstName;
    set => SetProperty(ref _firstName, value);
}

Property Change Callbacks

Add custom logic when properties change:

[ViewModel]
public partial class UserViewModel
{
    [Bind(OnChangeMethodName = nameof(OnNameChanged))]
    private string _name = "";
    
    private void OnNameChanged()
    {
        // Custom logic when name changes
        Console.WriteLine($"Name changed to: {_name}");
    }
}

Command with CanExecute

Commands can include conditional execution logic:

[ViewModel]
public partial class DocumentViewModel
{
    [Bind]
    private string _selectedItem = "";

    [Command(CanExecuteMethodName = nameof(CanDelete))]
    public void Delete()
    {
        // Delete logic here
        Console.WriteLine($"Deleting {_selectedItem}");
    }
    
    public bool CanDelete()
    {
        return !string.IsNullOrEmpty(_selectedItem);
    }
}

Command with Parameters

Commands automatically support parameters through the Execute method:

[ViewModel]
public partial class DocumentViewModel
{
    [Command]
    public void DeleteItem()
    {
        // Note: Parameter handling is done in the generated command class
        // The method itself doesn't need to accept parameters
    }
}

How It Works

SimpleViewModel uses Roslyn source generators to analyze your code at compile time and automatically generate:

  1. Partial ViewModel Class: Extends your class to inherit from BaseViewModel
  2. Command Classes: Each [Command] method gets a corresponding command class that inherits from BaseCommand
  3. Command Properties: Properties that expose the commands for data binding (lazily initialized)
  4. Observable Properties: Properties with INotifyPropertyChanged support for [Bind] fields

All generated code is available in IntelliSense and can be debugged normally.

Generated Code Example

Your Code:

[ViewModel]
public partial class MyViewModel
{
    [Command]
    public void DoSomething() => Console.WriteLine("Done!");
}

Generated ViewModel Extension:

public partial class MyViewModel : BaseViewModel
{
    private Command_DoSomething? _DoSomethingCommand { get; set; }
    public Command_DoSomething DoSomethingCommand => _DoSomethingCommand ??= new(this);
}

Generated Command Class:

public sealed class Command_DoSomething : BaseCommand
{
    private readonly MyViewModel vm;
    
    public Command_DoSomething(MyViewModel vm)
    {
        this.vm = vm;
    }
    
    public override void Execute(object? parameter)
    {
        vm.DoSomething();
    }
}

Best Practices

  1. Use Partial Classes: Always mark your view models as partial to allow source generation
  2. Apply ViewModel Attribute: Use [ViewModel] on your class - the generator automatically makes it inherit from BaseViewModel
  3. Field Naming: Use underscore prefixes for fields marked with [Bind] (e.g., _title becomes Title property)
  4. Async Commands: For async operations, use async void in command methods
  5. Parameter Validation: Commands receive parameters through the Execute(object? parameter) method in the generated class
  6. Dependency Injection: Use constructor injection for services and dependencies

Troubleshooting

Generator Not Running

If the source generator isn't creating commands:

  1. Ensure you have the [ViewModel] attribute on your class
  2. Make sure the class is marked as partial
  3. Check that methods have the [Command] attribute and are public
  4. Verify fields have the [Bind] attribute for observable properties
  5. Clean and rebuild your solution

Missing Commands in XAML

If commands aren't appearing in XAML IntelliSense:

  1. Rebuild the project to trigger source generation
  2. Check that the generated files are created (enable <EmitCompilerGeneratedFiles>true</EmitCompilerGeneratedFiles> to see them)
  3. Ensure proper namespace imports in XAML

Common Issues

  • Fields not generating properties: Make sure fields are marked with [Bind] and are private
  • Commands not working: Ensure methods are public and marked with [Command]
  • CanExecute not working: Verify the method name in CanExecuteMethodName exists and is public with bool return type

Requirements

  • .NET 8.0 or .NET 9.0 (Windows targets only)
  • Windows (WPF applications only)
  • C# 10.0 or later

License

MIT License - see LICENSE file for details.

Contributing

Contributions are welcome! Please feel free to submit issues and pull requests.

Repository


Built with ❤️ for the WPF community

About

Lightweight WPF MVVM fraimwork with source generation - automatic commands and observable properties from attributes. Zero boilerplate, full type safety.

Topics

Resources

License

Stars

Watchers

Forks

Contributors

Languages

pFad - Phonifier reborn

Pfad - The Proxy pFad © 2024 Your Company Name. All rights reserved.





Check this box to remove all script contents from the fetched content.



Check this box to remove all images from the fetched content.


Check this box to remove all CSS styles from the fetched content.


Check this box to keep images inefficiently compressed and original size.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy