OutWit.Common.MVVM.Avalonia 3.1.0

dotnet add package OutWit.Common.MVVM.Avalonia --version 3.1.0
                    
NuGet\Install-Package OutWit.Common.MVVM.Avalonia -Version 3.1.0
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="OutWit.Common.MVVM.Avalonia" Version="3.1.0" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="OutWit.Common.MVVM.Avalonia" Version="3.1.0" />
                    
Directory.Packages.props
<PackageReference Include="OutWit.Common.MVVM.Avalonia" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add OutWit.Common.MVVM.Avalonia --version 3.1.0
                    
#r "nuget: OutWit.Common.MVVM.Avalonia, 3.1.0"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package OutWit.Common.MVVM.Avalonia@3.1.0
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=OutWit.Common.MVVM.Avalonia&version=3.1.0
                    
Install as a Cake Addin
#tool nuget:?package=OutWit.Common.MVVM.Avalonia&version=3.1.0
                    
Install as a Cake Tool

OutWit.Common.MVVM.Avalonia

Avalonia-specific MVVM components and utilities, including source generator for automatic StyledProperty, DirectProperty, and AttachedProperty generation.

Features

  • Source Generator for Properties: Automatically generate StyledProperty, DirectProperty, and AttachedProperty from attributes
  • AvaloniaDispatcher: IDispatcher implementation for UI thread invocation
  • Binding Utilities: Helper methods for property registration
  • Visual Tree Traversal: Extension methods for navigating Avalonia visual tree
  • BindingProxy: Binding proxy for DataContext access
  • DataTemplate Utilities: Helper methods for DataTemplate creation

Installation

dotnet add package OutWit.Common.MVVM.Avalonia

This automatically includes:

  • OutWit.Common.MVVM (base cross-platform package)
  • OutWit.Common.MVVM.Avalonia.Generator (source generator)
  • OutWit.Common.Logging

Quick Start

Source Generator for StyledProperty

The simplest way to create StyledProperties:

using Avalonia.Controls;
using OutWit.Common.MVVM.Avalonia.Attributes;

namespace MyApp.Controls
{
    public partial class CustomButton : Button
    {
        [StyledProperty(DefaultValue = "Click Me")]
        public string Label { get; set; }

        [StyledProperty(BindsTwoWayByDefault = true)]
        public bool IsPressed { get; set; }
    }
}

Important: Mark your class as partial to allow source generator to add code.

The generator automatically creates:

// Generated code (you don't write this):
public static readonly StyledProperty<string> LabelProperty = 
    AvaloniaProperty.Register<CustomButton, string>(nameof(Label), "Click Me");
public static readonly StyledProperty<bool> IsPressedProperty = 
    AvaloniaProperty.Register<CustomButton, bool>(nameof(IsPressed), defaultBindingMode: BindingMode.TwoWay);

DirectProperty for Performance

Use DirectProperty for frequently changing values (better performance, no style system participation):

public partial class CounterControl : Control
{
    [DirectProperty(DefaultValue = 0)]
    public int Counter { get; set; }

    [DirectProperty(BindsTwoWayByDefault = true)]
    public string Text { get; set; }
}

Generated code:

// Backing field is generated automatically
private int m_counter = 0;
public static readonly DirectProperty<CounterControl, int> CounterProperty = 
    AvaloniaProperty.RegisterDirect<CounterControl, int>(
        nameof(Counter), 
        o => o.m_counter, 
        (o, v) => o.m_counter = v,
        unsetValue: 0);

Attached Properties

using OutWit.Common.MVVM.Avalonia.Attributes;

public static partial class MyAttachedProperties
{
    [AttachedProperty(DefaultValue = false)]
    public static bool IsHighlighted { get; set; }

    [AttachedProperty(DefaultValue = 1.0, Inherits = true)]
    public static double Opacity { get; set; }
}

// Usage in AXAML:
// <Button local:MyAttachedProperties.IsHighlighted="True" />

Generated code includes Get/Set methods:

public static bool GetIsHighlighted(AvaloniaObject obj) => obj.GetValue(IsHighlightedProperty);
public static void SetIsHighlighted(AvaloniaObject obj, bool value) => obj.SetValue(IsHighlightedProperty, value);

Convention-Based Callbacks

The generator discovers callback methods by naming convention and subscribes them for you. Avalonia, unlike WPF, has no changed-callback parameter on AvaloniaProperty.Register — the notification arrives through the property's Changed observable — so the generated code registers the property and subscribes in one step. Nothing is needed in your constructor:

public partial class SmartControl : Control
{
    // No need to specify OnChanged - automatically discovered!
    [StyledProperty(DefaultValue = "Hello")]
    public string Title { get; set; }

    // Convention: On{PropertyName}Changed
    private void OnTitleChanged(AvaloniaPropertyChangedEventArgs<string> e)
    {
        // Handle title change
    }

    // Convention: {PropertyName}Coerce
    private string TitleCoerce(AvaloniaObject sender, string value)
    {
        return value?.Trim() ?? "";
    }
}

Visual Tree Traversal

using OutWit.Common.MVVM.Avalonia.Utils;

// Find first child of specific type
var button = myPanel.FindFirstChildOf<Button>();

// Find all children
var allButtons = myPanel.FindAllChildrenOf<Button>();

// Find parent
var window = myButton.FindFirstParentOf<Window>();

BindingProxy for DataContext Access

<UserControl.Resources>
    <local:BindingProxy x:Key="Proxy" Data="{Binding}" />
</UserControl.Resources>

<DataGrid Items="{Binding Items}">
    <DataGrid.Columns>
        <DataGridTemplateColumn>
            <DataTemplate>
                <Button Command="{Binding Data.DeleteCommand, Source={StaticResource Proxy}}"
                        CommandParameter="{Binding}" />
            </DataTemplate>
        </DataGridTemplateColumn>
    </DataGrid.Columns>
</DataGrid>

AvaloniaDispatcher

using OutWit.Common.MVVM.Avalonia.Abstractions;

// Get dispatcher for current thread
var dispatcher = AvaloniaDispatcher.UIThread;

// Invoke on UI thread
dispatcher.Invoke(() => UpdateUI());

// Async invoke
await dispatcher.InvokeAsync(() => UpdateUI());

Property Types Comparison

Type Use Case Style System Performance
StyledProperty Most properties Yes Normal
DirectProperty Frequently changing values No Better
AttachedProperty Properties on other objects Yes Normal

StyledProperty Options

Option Type Description
PropertyName string Override property name (default: {Name}Property)
DefaultValue object Default value
BindsTwoWayByDefault bool Enable two-way binding by default
Inherits bool Value inherited by child elements
OnChanged string Property changed callback method name
Coerce string Coerce value callback method name

DirectProperty Options

Option Type Description
PropertyName string Override property name
DefaultValue object Default value (also used as unset value)
BindsTwoWayByDefault bool Enable two-way binding by default
OnChanged string Property changed callback method name

AttachedProperty Options

Option Type Description
PropertyName string Override property name
DefaultValue object Default value
Inherits bool Value inherited by child elements
OnChanged string Property changed callback method name
Coerce string Coerce value callback method name
  • OutWit.Common.MVVM - Cross-platform base classes
  • OutWit.Common.MVVM.WPF - WPF-specific implementation
  • OutWit.Common.MVVM.Blazor - Blazor-specific implementation

Upgrading to 3.1.0

Two things that used to be silent now work, and both can change behaviour in an existing control.

On{Property}Changed is actually called. Before 3.1.0 the generator found the method and emitted a comment suggesting you subscribe to it yourself, so the callbacks never ran. If your control subscribed by hand — a static constructor with XProperty.Changed.AddClassHandler<T>(…) — and the handler it calls is named by the convention, it will now run twice. Delete the manual subscription; the generated one replaces it.

A [DirectProperty] raises change notifications. Its generated setter used to write the backing field directly, which meant no callback, no PropertyChanged, and no binding out of it ever updated. It now goes through SetAndRaise. Nothing to change on your side — but code that relied on a direct property staying quiet will start seeing notifications.

License

Licensed under the Apache License, Version 2.0. See LICENSE.

Attribution (optional)

If you use OutWit.Common.MVVM.Avalonia in a product, a mention is appreciated (but not required), for example: "Powered by OutWit.Common.MVVM.Avalonia (https://ratner.io/)".

Trademark / Project name

"OutWit" and the OutWit logo are used to identify the official project by Dmitry Ratner.

You may:

  • refer to the project name in a factual way (e.g., "built with OutWit.Common.MVVM.Avalonia");
  • use the name to indicate compatibility (e.g., "OutWit.Common.MVVM.Avalonia-compatible").

You may not:

  • use "OutWit.Common.MVVM.Avalonia" as the name of a fork or a derived product in a way that implies it is the official project;
  • use the OutWit.Common.MVVM.Avalonia logo to promote forks or derived products without permission.
Product Compatible and additional computed target framework versions.
.NET net8.0 is compatible.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed.  net9.0 is compatible.  net9.0-android was computed.  net9.0-browser was computed.  net9.0-ios was computed.  net9.0-maccatalyst was computed.  net9.0-macos was computed.  net9.0-tvos was computed.  net9.0-windows was computed.  net10.0 is compatible.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages (1)

Showing the top 1 NuGet packages that depend on OutWit.Common.MVVM.Avalonia:

Package Downloads
OutWit.Common.MVVM.Navigation.Avalonia

Avalonia half of OutWit.Common.MVVM.Navigation: ViewLocator (an IDataTemplate over the view registry and a naming convention), the NavigationOutlet control that keeps views alive across navigations, window and overlay dialog hosts, application resources for modules, and AddAvaloniaNavigation().

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
3.1.0 161 8/23/2026
3.0.0 142 8/9/2026
2.0.4 421 1/25/2026
2.0.3 137 1/8/2026
2.0.2 145 1/2/2026
2.0.1 135 1/2/2026
2.0.0 140 1/2/2026