Skip to content

Vetuvium is a code generation toolkit to support ReactiveUI View to View Model binding (MVVM -> V2VM -> Ve-Tu-Viem)

License

Notifications You must be signed in to change notification settings

dpvreony/Vetuviem

Repository files navigation

Vetuviem

Vetuviem is a toolkit to support View to View Model binding (MVVM -> V2VM -> Ve-Tu-Viem) aimed at offering a structure to get more re-usability out of ReactiveUI.

Mission Statement

  • To give a mechanism to reduce the amount of boiler plate code being produced, by allowing some of the ReactiveUI specific logic to be hidden away
  • Allow the developer to think along the lines of standard behaviours for controls by offering a way to produce re-usable behaviours through a class and\or function design pattern
  • Allow the developer to focus on what matters on the ViewModel
  • Reduce the cognitive load by
    • Removing the risk of misusing 1 way or 2 way binding
    • Remove the need for the user to think about having to cater for Bind vs BindCommand
  • Offer a structure that allows for more work to be done potentially with Source Generators to reduce reflection and improve the build time developer experience.

Current Status

This is currently a proof of concept alpha. For understanding of the design reasoning please see https://www.dpvreony.com/articles/designing-vetuviem/

Nuget Packages

Purpose Package NuGet
Command Line Generation Coming soon Coming Soon
Visual Studio Integration Vetuviem.SourceGenerator VetuviemSourceGeneratorBadge
Core Functionality Vetuviem.Core VetuviemCoreBadge

An example

Currently to write binding logic in the codebehind you have to write something similar to this for a single control

// Traditional ReactiveUI binding approach
this.WhenActivated(disposables =>
{
    this.Bind(ViewModel, vm => vm.Forename, v => v.Forename.Text)
        .DisposeWith(disposables);
    this.OneWayBind(ViewModel, vm => vm.ForenameLengthRemaining, v => v.ForenameLengthRemaining.Content)
        .DisposeWith(disposables);
    this.OneWayBind(ViewModel, vm => vm.ForenameLengthRemaining, v => v.ForenameLengthRemaining.Foreground, 
        lengthRemaining => GetBrushForLengthRemaining(lengthRemaining))
        .DisposeWith(disposables);
});

But what if you have a way to simplify logic and offer a way to even make it reusable without all the boilerplate leg work?

// Vetuviem approach using ViewBindingModels
public sealed class QuestionnaireViewBindingModels : AbstractEnableViewToViewModelBindings<QuestionnaireView, QuestionnaireViewModel>
{
    protected override IEnumerable<IControlBindingModel<QuestionnaireView, QuestionnaireViewModel>> GetBindings()
    {
        yield return new TextBoxControlBindingModel<QuestionnaireView, QuestionnaireViewModel>(vw => vw.Forename)
        {
            Text = new TwoWayBinding<QuestionnaireViewModel, string>(vm => vm.Forename),
        };
        
        yield return new LabelControlBindingModel<QuestionnaireView, QuestionnaireViewModel>(vw => vw.ForenameLengthRemaining)
        {
            Content = new OneWayBindingOnOneOrTwoWayBind<QuestionnaireViewModel, object>(vm => vm.ForenameLengthRemaining, o => o?.ToString() ?? string.Empty),
            Foreground = new OneWayBindingWithConversionOnOneOrTwoWayBind<QuestionnaireViewModel, Brush, int>(vm => vm.ForenameLengthRemaining, lengthRemaining => GetBrushForLengthRemaining(lengthRemaining))
        };
    }
}

Configuration

Vetuviem source generators can be configured through MSBuild properties in your project file (.csproj). Add these properties to a <PropertyGroup> section to customize code generation behavior.

Available Properties

Property Description Default Value
Vetuviem_Root_Namespace Override the root namespace for generated code (Project's root namespace)
Vetuviem_Make_Classes_Public Make generated classes public instead of internal false
Vetuviem_Assemblies Comma-separated list of assemblies to scan for controls (Platform-specific defaults)
Vetuviem_Assembly_Mode How to use custom assemblies: Replace or Extend Replace
Vetuviem_Base_Namespace Base namespace when using custom assemblies (none)
Vetuviem_Include_Obsolete_Items Include properties marked with ObsoleteAttribute false
Vetuviem_Allow_Experimental_Properties Include properties marked with ExperimentalAttribute false
Vetuviem_Logging_Implementation_Mode Logging implementation: None or SplatViaServiceLocator SplatViaServiceLocator

Property Details

Vetuviem_Root_Namespace

Override the default root namespace for generated code. This is useful when you want generated binding models to reside in a specific namespace.

Example:

<PropertyGroup>
  <Vetuviem_Root_Namespace>MyApp.Generated</Vetuviem_Root_Namespace>
</PropertyGroup>

Vetuviem_Make_Classes_Public

Controls the visibility of generated binding model classes. By default, classes are generated as internal. Set this to true to make them public.

Example:

<PropertyGroup>
  <Vetuviem_Make_Classes_Public>true</Vetuviem_Make_Classes_Public>
</PropertyGroup>

Vetuviem_Assemblies

Specify a comma-separated list of assemblies to scan for control types. Use in conjunction with Vetuviem_Assembly_Mode to either replace or extend the default platform assemblies.

Example:

<PropertyGroup>
  <Vetuviem_Assemblies>CustomControls.dll,ThirdPartyControls.dll</Vetuviem_Assemblies>
</PropertyGroup>

Vetuviem_Assembly_Mode

Determines how the assemblies specified in Vetuviem_Assemblies are used:

  • Replace (default): Replace platform defaults with your custom assemblies
  • Extend: Add custom assemblies in addition to platform defaults

Example:

<PropertyGroup>
  <Vetuviem_Assembly_Mode>Extend</Vetuviem_Assembly_Mode>
</PropertyGroup>

Vetuviem_Base_Namespace

Used with custom assemblies to specify a base namespace. This allows third parties to use the generator and produce custom namespaces that inherit from the root or use a custom namespace.

Example:

<PropertyGroup>
  <Vetuviem_Base_Namespace>MyCompany.Controls</Vetuviem_Base_Namespace>
</PropertyGroup>

Vetuviem_Include_Obsolete_Items

Controls whether properties marked with ObsoleteAttribute are included in code generation. By default, obsolete items are excluded.

Example:

<PropertyGroup>
  <Vetuviem_Include_Obsolete_Items>true</Vetuviem_Include_Obsolete_Items>
</PropertyGroup>

Vetuviem_Allow_Experimental_Properties

Starting with .NET 10, properties can be marked with ExperimentalAttribute to indicate they are experimental APIs. This property controls how these are handled:

When false or not set (default):

  • Properties marked with ExperimentalAttribute are excluded from code generation
  • No warnings are generated
  • Experimental properties are silently skipped
  • Ensures backward compatibility

When true:

  • Properties marked with ExperimentalAttribute are included in code generation
  • A SuppressMessage attribute is automatically added to suppress experimental warnings
  • The diagnostic ID from the ExperimentalAttribute is extracted and used in the suppression

Example:

<PropertyGroup>
  <Vetuviem_Allow_Experimental_Properties>true</Vetuviem_Allow_Experimental_Properties>
</PropertyGroup>

Example Control Property:

[Experimental("WFDEV001")]
public ThemeMode ThemeMode { get; set; }

Generated Binding Property (when enabled):

[SuppressMessage("Usage", "WFDEV001")]
public IOneOrTwoWayBind<TViewModel, ThemeMode>? ThemeMode { get; init; }

Notes:

  • The diagnostic ID is read from the first constructor argument of ExperimentalAttribute
  • Only properties with public accessibility are considered for generation (standard behavior)

Vetuviem_Logging_Implementation_Mode

Controls the logging implementation generated in binding code:

  • None: No logging implementation
  • SplatViaServiceLocator (default): Uses Splat logging via service locator

Example:

<PropertyGroup>
  <Vetuviem_Logging_Implementation_Mode>None</Vetuviem_Logging_Implementation_Mode>
</PropertyGroup>

Support

For support, please:

  • Check the design article for understanding the design reasoning
  • Report bugs or request features via GitHub Issues
  • Ask questions in GitHub Discussions
  • Review the sample applications in the src directory for practical examples

Contribute

Contributions are welcome! Please:

  • Read the Code of Conduct
  • Fork the repository and create a feature branch
  • Write tests for your changes
  • Ensure all tests pass and code follows the existing style
  • Submit a Pull Request with a clear description of your changes

License

This project is licensed under the MIT License - see the LICENSE file for details.

About

Vetuvium is a code generation toolkit to support ReactiveUI View to View Model binding (MVVM -> V2VM -> Ve-Tu-Viem)

Topics

Resources

License

Code of conduct

Stars

Watchers

Forks

Packages

No packages published

Contributors 8