OutWit.Common.MVVM.Avalonia
2.0.4
dotnet add package OutWit.Common.MVVM.Avalonia --version 2.0.4
NuGet\Install-Package OutWit.Common.MVVM.Avalonia -Version 2.0.4
<PackageReference Include="OutWit.Common.MVVM.Avalonia" Version="2.0.4" />
<PackageVersion Include="OutWit.Common.MVVM.Avalonia" Version="2.0.4" />
<PackageReference Include="OutWit.Common.MVVM.Avalonia" />
paket add OutWit.Common.MVVM.Avalonia --version 2.0.4
#r "nuget: OutWit.Common.MVVM.Avalonia, 2.0.4"
#:package OutWit.Common.MVVM.Avalonia@2.0.4
#addin nuget:?package=OutWit.Common.MVVM.Avalonia&version=2.0.4
#tool nuget:?package=OutWit.Common.MVVM.Avalonia&version=2.0.4
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:
IDispatcherimplementation 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 automatically discovers callback methods by naming convention:
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 |
Related Packages
OutWit.Common.MVVM- Cross-platform base classesOutWit.Common.MVVM.WPF- WPF-specific implementationOutWit.Common.MVVM.Blazor- Blazor-specific implementation
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 | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net6.0 is compatible. net6.0-android was computed. net6.0-ios was computed. net6.0-maccatalyst was computed. net6.0-macos was computed. net6.0-tvos was computed. net6.0-windows was computed. net7.0 is compatible. net7.0-android was computed. net7.0-ios was computed. net7.0-maccatalyst was computed. net7.0-macos was computed. net7.0-tvos was computed. net7.0-windows was computed. 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. |
-
net10.0
- Avalonia (>= 11.3.11)
- OutWit.Common.Logging (>= 1.2.3)
- OutWit.Common.MVVM (>= 2.0.2)
-
net6.0
- Avalonia (>= 11.3.11)
- OutWit.Common.Logging (>= 1.2.3)
- OutWit.Common.MVVM (>= 2.0.2)
-
net7.0
- Avalonia (>= 11.3.11)
- OutWit.Common.Logging (>= 1.2.3)
- OutWit.Common.MVVM (>= 2.0.2)
-
net8.0
- Avalonia (>= 11.3.11)
- OutWit.Common.Logging (>= 1.2.3)
- OutWit.Common.MVVM (>= 2.0.2)
-
net9.0
- Avalonia (>= 11.3.11)
- OutWit.Common.Logging (>= 1.2.3)
- OutWit.Common.MVVM (>= 2.0.2)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.