Skip to content

finnonly/Finnonly

Repository files navigation

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.12-rc1" />
<PackageReference Include="Finnonly.SourceGenerator" Version="1.0.12-rc1" OutputItemType="Analyzer" ReferenceOutputAssembly="false" />

Avalonia 12 支持:从 1.0.11 开始,Finnonly 支持 Avalonia 12.0.0-rc1+。 Avalonia 12 默认启用编译型绑定(Compiled Bindings),XAML 中需要声明 x:DataType 指令。 内部 API 变更:IBindingBindingBaseBindingOperations.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();
}

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);

// 条件查询
var result = await db.GetUserByConditionAsync(
    "user_name = @name", 
    new[] { new SqliteParameter("@name", "finn") });

// 分页
var page = await db.GetUserListByConditionAsync(
    "1=1", Array.Empty<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

About

Finnonly Code

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages