Finnonly.SourceGenerator 1.0.18

dotnet add package Finnonly.SourceGenerator --version 1.0.18
                    
NuGet\Install-Package Finnonly.SourceGenerator -Version 1.0.18
                    
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="Finnonly.SourceGenerator" Version="1.0.18">
  <PrivateAssets>all</PrivateAssets>
  <IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
</PackageReference>
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="Finnonly.SourceGenerator" Version="1.0.18" />
                    
Directory.Packages.props
<PackageReference Include="Finnonly.SourceGenerator">
  <PrivateAssets>all</PrivateAssets>
  <IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
</PackageReference>
                    
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 Finnonly.SourceGenerator --version 1.0.18
                    
#r "nuget: Finnonly.SourceGenerator, 1.0.18"
                    
#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 Finnonly.SourceGenerator@1.0.18
                    
#: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=Finnonly.SourceGenerator&version=1.0.18
                    
Install as a Cake Addin
#tool nuget:?package=Finnonly.SourceGenerator&version=1.0.18
                    
Install as a Cake Tool

Finnonly.Avalonia

Avalonia extensions for event binding using Source Generators + High Performance VirtualizingWrapPanel.

Features

  • v:Event - 简洁的事件绑定标记扩展
  • v:RoutedEvent - 绑定路由事件到 ViewModel
  • v:RawEvent - 处理非 EventArgs 事件(如 WindowClosing)
  • Attributes - EventBind, CopyTo, Compare, Table, Column, PrimaryKey, SqliteConfig, etc.
  • SQLite Source Generator - 编译时生成 DbContext + 迁移管理器,支持 11 项 PRAGMA 配置
  • VirtualizingWrapPanel - 高性能虚拟化 WrapPanel(.NET 10 / C# 12+ 优化)

Installation

<PackageReference Include="Finnonly.Avalonia" Version="1.0.16" />
<PackageReference Include="Finnonly.SourceGenerator" Version="1.0.16" OutputItemType="Analyzer" ReferenceOutputAssembly="false" />

⚠️ 必须同时引用两个包:Finnonly.Avalonia(运行时标记扩展 + 特性)+ Finnonly.SourceGenerator(Analyzer,生成 MethodDispatcher / 事件元数据 / 模块初始化注册)。 只引运行时库时生成器不产出任何代码,{v:RoutedEvent} / {v:RawEvent} 会静默空转(EventBindProvider 三个属性全空,运行时仅打 Debug 警告,UI 无任何反应)。

Avalonia 12.1 支持:当前适配 Avalonia 12.1.0。从 1.0.11 开始支持 Avalonia 12.0.0-rc1+。 Avalonia 12 默认启用编译型绑定(Compiled Bindings),XAML 中需要声明 x:DataType 指令。 内部 API 变更:IBinding → BindingBase,BindingOperations.Apply() → AvaloniaObject.Bind()。


v:Event 事件绑定

v:Event 是最简洁的事件绑定方式,自动推断事件类型。

✨ 特性

  • 🎯 智能推断 - 自动识别路由事件和普通事件
  • 📝 简洁语法 - 一个标记扩展处理所有事件
  • 🔗 直接绑定 - 无需 Command,直接绑定 ViewModel 方法
  • ⚡ 源生成器 - 编译时生成代码,零运行时反射开销

📦 基本用法

<Window xmlns:v="using:Finnonly.Avalonia"
        x:DataType="local:MainViewModel"
        Loaded="{v:RoutedEvent OnLoaded}"
        Closing="{v:RawEvent OnClosing}">
    
    <StackPanel>
        <Button Content="点击" Click="{v:RoutedEvent OnButtonClick}"/>
        <Button Content="点击" Click="{v:RoutedEvent OnButtonClick,'!sender'}"/>
        <Button Content="点击" Click="{v:RoutedEvent OnButtonClick,'!args'}"/>
        <Button Content="点击" Click="{v:RoutedEvent OnButtonClick,'!sender','!args'}"/>
        <TextBox TextChanged="{v:RoutedEvent OnTextChanged}"/>
        <ListBox SelectionChanged="{v:RoutedEvent OnSelectionChanged}"/>
    </StackPanel>
</Window>
// ViewModel
public partial class MainViewModel : ViewModelBase
{
    [EventBind]
    public void OnLoaded()
    {
        // 窗口加载完成
    }

    [EventBind]
    public void OnClosing()
    {
        // 窗口关闭前
    }

    [EventBind]
    public void OnButtonClick()
    {
        // 按钮点击
    }

    [EventBind]
    public void OnTextChanged(object sender, TextChangedEventArgs e)
    {
        // 文本变化,可选参数
    }

    [EventBind]
    public void OnSelectionChanged()
    {
        // 选择变化
    }
}

🎛️ 事件方法参数

基本参数模式
// 无参数 - 最简洁
[EventBind]
public void OnClick() { }

// 只有 sender
[EventBind]
public void OnClick(object sender) { }

// 完整参数
[EventBind]
public void OnClick(object sender, RoutedEventArgs e) { }

// 异步方法
[EventBind]
public async Task OnClickAsync()
{
    await DoSomethingAsync();
}

🔒 方法可访问性(v1.0.16+)

[EventBind] 方法的可访问性不再是障碍,public / internal / private 均可:

[EventBind]
public void OnPublicClick() { }        // ✅ 直接调用

[EventBind]
internal void OnInternalClick() { }    // ✅ 直接调用

[EventBind]
private void OnPrivateClick() { }      // ✅ 经生成器 partial 包装桥接(见下方说明)

private / protected 方法的桥接机制:生成器在方法所在类型的 partial 声明中生成 __finnonly_* 内部包装方法,MethodDispatcher 经包装调用原方法,全程编译期强类型、零反射、Native AOT 安全:

// 生成器自动产出(Method.Wrappers.g.cs),开发者无需手写:
partial class MainViewModel
{
    internal void __finnonly_OnPrivateClick() => OnPrivateClick();
}

⚠️ private 方法的要求:

  • 方法所在类型必须声明为 partial(如 public partial class MainViewModel)。MVVM 场景的 ViewModel 通常已是 partial([ObservableProperty] 前提)。
  • 类型不是 partial 且方法为 private/protected → 编译错误 FNLY001(提示:将类型声明为 partial,或把方法改为 internal)。
  • 嵌套类型同样支持:生成器按类型链逐层展开 partial 声明(partial class Outer { partial class Inner { ... } })。

private + 参数完整支持:无参 / 有参(含 '!sender'/'!args'/{Binding} 传参)/ async Task / ref / out / in 参数修饰符全部透传。

标记扩展参数速查
标记 含义 示例
'MethodName' 第一个参数始终是方法名 {v:RoutedEvent OnClick}
'!sender' 传入事件的 sender 对象 {v:RoutedEvent OnClick, '!sender'}
'!args' 传入事件参数(EventArgs 子类或 object) {v:RoutedEvent OnClick, '!args'}
'!static' 首个参数标记,表示后续为静态目标对象 见下方 !static 章节
{Binding ...} 传入 ViewModel 属性的绑定值 {v:RoutedEvent OnClick, {Binding Name}}
'literal' 直接传入字符串字面量 {v:RoutedEvent OnClick, 'hello'}
🔗 !static — 调用静态对象 / DI 服务的方法

当目标方法不属于 ViewModel,而是属于静态实例或依赖注入的服务对象时,使用 !static 标记:


<Button Content="静态调用"
        Click="{v:RoutedEvent OnStaticAction, '!static', {x:Static local:StaticService.Instance}}" />
<Button Content="带参数"
        Click="{v:RoutedEvent OnStaticActionWithParam, '!static', {x:Static local:StaticService.Instance}, 'hello'}" />
// 单例服务
public class StaticService
{
    public static StaticService Instance { get; } = new();

    [EventBind]
    public void OnStaticAction() { }

    [EventBind]
    public void OnStaticActionWithParam(string msg) { }
}

注意:!static 必须是第一个参数标记,紧接方法名之后。第二个参数是目标对象(支持 {x:Static}、{Binding} 等),后续参数会正常传递给方法。

📎 {Binding} — 传入 ViewModel 属性值
<Button Content="带绑定参数"
        Click="{v:RoutedEvent OnButtonClickWithBinding, {Binding BoundParam}}" />
public class MainViewModel
{
    public string BoundParam => "某个值";

    [EventBind]
    public void OnButtonClickWithBinding(string param)
    {
        // param == "某个值"
    }
}

{Binding} 支持编译型绑定(需声明 x:DataType)和反射绑定,与 Avalonia 原生绑定行为一致。


v:RoutedEvent / v:RawEvent

针对特定场景的专用标记扩展。

v:RoutedEvent - 路由事件

用于明确绑定 Avalonia 路由事件。

<Window Loaded="{v:RoutedEvent OnLoaded}"
        Unloaded="{v:RoutedEvent OnUnloaded}">
    <Button Click="{v:RoutedEvent OnButtonClick}"/>
</Window>

v:RawEvent - 原始事件

用于处理特殊的非标准事件(如 WindowClosing)。

<Window Closing="{v:RawEvent OnWindowClosing}">
[EventBind]
public void OnWindowClosing()
{
    // 可以取消关闭
}

SQLite 源生成器

编译时自动生成 SQLite 数据库上下文(SqliteDbContext)和迁移管理器(SqliteMigrationManager),零反射、零手写 SQL。

📦 定义实体

[Table("users")]
[Migration(1)]
public class User
{
    [PrimaryKey(true)]
    public int Id { get; set; }

    [Column("user_name")]
    public string Name { get; set; } = string.Empty;

    public string? Email { get; set; }

    [Ignore]
    public string DisplayName => $"{Name} <{Email}>";
}

🔧 PRAGMA 配置

通过 [assembly: SqliteConfig] 属性配置数据库性能参数,不配置则使用性能优化的默认值:

// 默认配置(开箱即用,已针对性能优化)
// 无需任何代码

// 自定义配置示例
[assembly: SqliteConfig(
    JournalMode = "WAL",          // 日志模式(默认 WAL)
    Synchronous = "NORMAL",       // 同步模式(默认 NORMAL)
    CacheSize = -8000,            // 缓存 8MB(默认 -8000)
    TempStore = "MEMORY",         // 临时存储(默认 MEMORY)
    MmapSize = 536870912,         // 内存映射 512MB(默认)
    BusyTimeout = 5000,           // 忙等待 5s(默认 5000)
    ForeignKeys = true,           // 外键约束(默认 true)
    AutoVacuum = "INCREMENTAL",   // 自动回收(默认 INCREMENTAL)
    WalAutoCheckpoint = 2000,     // WAL 检查点间隔(默认 2000)
    PageSize = 4096,              // 页大小(默认 4096)
    SecureDelete = false           // 安全删除(默认 false)
)]

🚀 自动生成的 API

using var db = new SqliteDbContext("Data Source=app.db");

// CRUD
await db.InsertUserAsync(user);
var user = await db.GetUserByIdAsync(1);
var all = await db.GetAllUserAsync();
await db.UpdateUserAsync(user);
await db.DeleteUserAsync(1);

// Upsert
await db.InsertOrUpdateUserAsync(user);
await db.BulkInsertOrUpdateUserAsync(users);

// 条件查询(v1.0.15+ params 语法)
// 方法名格式:Get{完整类名}ByConditionAsync / Get{完整类名}ListByConditionAsync
var result = await db.GetUserEntityByConditionAsync("user_name = @name", new SqliteParameter("@name", "finn"));
var list = await db.GetUserEntityListByConditionAsync("age > @min", new SqliteParameter("@min", 18));

// 分页 + 排序(排序重载不可用 params,因可选参数在后)
var page = await db.GetUserListByConditionAsync(
    "1=1", new[] { new SqliteParameter() },
    orderBy: "Id DESC", limit: 20, offset: 0);

VirtualizingWrapPanel

高性能虚拟化 WrapPanel,专为 .NET 10 / C# 12+ 优化,支持大量数据的流畅滚动。

✨ 特性

  • 🚀 高性能虚拟化 - 仅渲染可见元素,支持万级数据流畅滚动
  • 📐 固定/自适应尺寸 - 支持固定项目大小或自动测量
  • 📏 自定义间距 - 支持水平和垂直间距配置
  • 🔄 无闪烁刷新 - 集合变化时平滑过渡
  • ⚡ 防抖优化 - 尺寸变化时使用防抖减少重绘
  • 📜 加载更多 - 内置滚动到底部加载更多数据支持

📦 基本用法

<Window xmlns:v="using:Finnonly.Avalonia"
        x:DataType="local:MainViewModel">
    <ScrollViewer HorizontalScrollBarVisibility="Disabled" 
                  VerticalScrollBarVisibility="Auto">
        <ItemsControl ItemsSource="{Binding Items}">
            <ItemsControl.ItemsPanel>
                <ItemsPanelTemplate>
                    <v:VirtualizingWrapPanel 
                        EstimatedItemWidth="200"
                        EstimatedItemHeight="300"
                        ItemHorizontalSpacing="8"
                        ItemVerticalSpacing="8"
                        UseFixedItemSize="True"
                        FillAvailableSpace="True"/>
                </ItemsPanelTemplate>
            </ItemsControl.ItemsPanel>
            <ItemsControl.ItemTemplate>
                <DataTemplate>
                    <Border Background="LightBlue" CornerRadius="8">
                        <TextBlock Text="{Binding Title}" 
                                   HorizontalAlignment="Center" 
                                   VerticalAlignment="Center"/>
                    </Border>
                </DataTemplate>
            </ItemsControl.ItemTemplate>
        </ItemsControl>
    </ScrollViewer>
</Window>

⚙️ 属性说明

属性 类型 默认值 说明
EstimatedItemWidth double 200 预设项目宽度(用于虚拟化计算)
EstimatedItemHeight double 200 预设项目高度
ItemHorizontalSpacing double 0 项目水平间距
ItemVerticalSpacing double 0 项目垂直间距
UseFixedItemSize bool true 是否使用固定尺寸(忽略实际测量)
FillAvailableSpace bool true 是否填充满整个可用空间

📡 事件

事件 说明
LoadMoreRequested 滚动到底部时触发,用于加载更多数据

🎛️ 加载更多示例

// XAML
<v:VirtualizingWrapPanel 
    x:Name="VPanel"
    HasMoreItems="True"/>

// Code-behind or ViewModel
VPanel.LoadMoreRequested += async (s, e) =>
{
    await LoadMoreDataAsync();
};

📋 动态刷新集合

// ✅ 推荐:使用 ObservableCollection
public ObservableCollection<ItemModel> Items { get; } = new();

// 刷新数据(自动触发UI更新)
public void RefreshData()
{
    Items.Clear();
    foreach (var item in newData)
    {
        Items.Add(item);
    }
}

🔧 性能优化建议

  1. 使用固定尺寸 - 设置 UseFixedItemSize="True" 避免测量开销
  2. 合理设置缓冲区 - 控件默认缓冲 5 行元素,减少频繁创建/销毁
  3. 简化 ItemTemplate - 避免复杂嵌套和重型绑定
  4. 分批加载 - 利用 LoadMoreRequested 实现分页加载

⚠️ 注意事项

  • 控件必须放在 ScrollViewer 内部
  • ScrollViewer 需要设置 HorizontalScrollBarVisibility="Disabled"
  • 集合变化时会自动强制刷新,无需手动处理

License

MIT

There are no supported framework assets in this package.

Learn more about Target Frameworks and .NET Standard.

This package has no dependencies.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
1.0.18 91 9/23/2026
1.0.16 117 8/3/2026
1.0.15 110 7/29/2026
1.0.14 99 7/29/2026
1.0.13 108 7/13/2026
1.0.12 131 4/8/2026
1.0.12-rc2 117 4/3/2026
1.0.12-rc1 132 3/26/2026
1.0.11 123 3/19/2026
1.0.10 113 3/19/2026
1.0.9 118 3/19/2026
1.0.8 122 2/3/2026