Files
AtomUI/docs/release-notes/6.1.1-api-changes.md
T
2026-07-27 13:28:03 +08:00

7.4 KiB
Raw Blame History

AtomUI 6.1.1 API 变更示例

本文档补充 6.1.1 相比 6.1.0 的主要 API 变更和迁移写法。正式版本条目见仓库根目录 CHANGELOG.md 和 CHANGELOG.zh-CN.md。

ListView 选择模型迁移

6.1.1 将 ListView 选择状态改为内部稳定模型,并通过 IListViewSelection 暴露只读门面:

  • Selection 从可设置的 Avalonia ISelectionModel 变为只读 IListViewSelection。
  • SelectedItems 从可设置的 IList? 变为只读 IReadOnlyList<object?>。
  • SelectedItem 和 SelectedValue 变为只读快照。
  • 新增只读 SelectedIndexes。
  • 新增 ItemKeySelector,用于按稳定业务 key 维护选择状态。
  • SelectionChanged 事件参数从 Avalonia SelectionChangedEventArgs 变为 ListViewSelectionChangedEventArgs。
  • 移除旧的 ListCollectionViewChangedEventArgs 类型;6.1.1 没有对应公开事件使用该类型。

直接设置选择模型

6.1.0 写法

var selection = new SelectionModel<object>
{
    Source = customers
};
selection.Select(3);

listView.Selection = selection;

6.1.1 写法

listView.SelectedIndex = 3;

多选场景改为操作现有 Selection 门面:

listView.Selection.Clear();

foreach (var sourceIndex in selectedSourceIndexes)
{
    listView.Selection.Select(sourceIndex);
}

绑定或设置 SelectedItems

6.1.0 写法

<atom:ListView
    ItemsSource="{Binding Customers}"
    SelectionMode="Multiple"
    SelectedItems="{Binding SelectedCustomers, Mode=TwoWay}" />

6.1.1 写法

SelectedItems 现在是 ListView 的只读选择快照。需要把选择结果同步到 ViewModel 时,监听 SelectionChanged,并从控件当前快照读取完整选择:

<atom:ListView
    x:Name="CustomersList"
    ItemsSource="{Binding Customers}"
    SelectionMode="Multiple"
    SelectionChanged="HandleCustomersSelectionChanged" />
private void HandleCustomersSelectionChanged(object? sender, ListViewSelectionChangedEventArgs e)
{
    if (sender is not ListView listView)
    {
        return;
    }

    SelectedCustomers.Clear();

    foreach (var customer in listView.SelectedItems.OfType<Customer>())
    {
        SelectedCustomers.Add(customer);
    }
}

需要从 ViewModel 状态回放选择时,按 source index 调用 Selection:

listView.Selection.Clear();

for (var index = 0; index < Customers.Count; index++)
{
    if (selectedCustomerIds.Contains(Customers[index].Id))
    {
        listView.Selection.Select(index);
    }
}

SelectionChanged 事件参数

6.1.0 写法

private void HandleSelectionChanged(object? sender, SelectionChangedEventArgs e)
{
    foreach (var item in e.AddedItems)
    {
        AddSelection(item);
    }

    foreach (var item in e.RemovedItems)
    {
        RemoveSelection(item);
    }
}

6.1.1 写法

private void HandleSelectionChanged(object? sender, ListViewSelectionChangedEventArgs e)
{
    foreach (var item in e.SelectedItems)
    {
        AddSelection(item);
    }

    foreach (var item in e.DeselectedItems)
    {
        RemoveSelection(item);
    }
}

如果需要索引,使用 SelectedIndexes 和 DeselectedIndexes:

private void HandleSelectionChanged(object? sender, ListViewSelectionChangedEventArgs e)
{
    foreach (var sourceIndex in e.SelectedIndexes)
    {
        MarkSelected(sourceIndex);
    }

    foreach (var sourceIndex in e.DeselectedIndexes)
    {
        MarkDeselected(sourceIndex);
    }
}

SelectedValue 回放

6.1.0 写法

<atom:ListView
    ItemsSource="{Binding Customers}"
    SelectedValueBinding="{Binding Id}"
    SelectedValue="{Binding SelectedCustomerId, Mode=TwoWay}" />

6.1.1 写法

SelectedValue 现在跟随当前选择计算。需要写入选择值时,先在数据源中找到 source index,再设置 SelectedIndex:

<atom:ListView
    x:Name="CustomersList"
    ItemsSource="{Binding Customers}"
    SelectedValueBinding="{Binding Id}"
    SelectionChanged="HandleCustomerSelectionChanged" />
private void ApplySelectedCustomerId(EntityKey? selectedCustomerId)
{
    CustomersList.SelectedIndex = selectedCustomerId is null
        ? -1
        : FindCustomerSourceIndex(selectedCustomerId.Value);
}

private void HandleCustomerSelectionChanged(object? sender, ListViewSelectionChangedEventArgs e)
{
    SelectedCustomerId = CustomersList.SelectedValue is EntityKey key ? key : null;
}

private int FindCustomerSourceIndex(EntityKey selectedCustomerId)
{
    for (var index = 0; index < Customers.Count; index++)
    {
        if (Customers[index].Id == selectedCustomerId)
        {
            return index;
        }
    }

    return -1;
}

稳定业务 key

如果同一个 item 会被过滤、分组、分页或重新投影,建议提供 ItemKeySelector。这样选择状态可以跟随业务 key,而不是依赖当前可见行位置:

CustomersList.ItemKeySelector = item => item switch
{
    Customer customer => customer.Id,
    IListItemData data => data.ItemKey,
    _ => null
};

Select 和 Transfer 内部已迁移到新的 source index / item key 语义;自定义 ListView 派生控件也应优先 使用 Selection.IsSelected、Selection.Select 和 Selection.Deselect。

ListItemData 和 SelectOption 迁移

6.1.1 移除了 IListItemData.IsSelected / ListItemData.IsSelected。选择状态不再写在数据项上,而是由 ListView selection 模型维护。

同时,ListItemData、GroupListItemData 和 SelectOption 从 record 调整为 class。继承这些类型的代码 应从 record 改为 class,依赖 with 表达式复制的代码需要改为显式创建新实例。

数据项默认选中

6.1.0 写法

var item = new ListItemData
{
    Content = customer,
    ItemKey = customer.Id,
    IsSelected = true
};

6.1.1 写法

var item = new ListItemData
{
    Content = customer,
    ItemKey = customer.Id
};

listView.Selection.Select(sourceIndex);

record 派生类型

6.1.0 写法

public record RichSelectOption : SelectOption
{
    public string? Description { get; init; }
}

var next = option with
{
    Header = "Updated"
};

6.1.1 写法

public class RichSelectOption : SelectOption
{
    public string? Description { get; init; }
}

var next = new RichSelectOption
{
    Header = "Updated",
    Content = option.Content,
    ItemKey = option.ItemKey,
    Group = option.Group,
    IsEnabled = option.IsEnabled,
    Description = option.Description
};

WindowTitleBar 标题对齐

6.1.1 新增 WindowTitleBar.TitleAlignment 和 Window.TitleAlignment。默认 Auto 会按平台选择标题布局:

  • macOS 默认使用 WindowCenter。
  • Windows 和 Linux 默认使用 Left。

如果之前通过自定义 padding、margin 或 left/right add-on 宽度来调整标题位置,可以改为使用公开属性。

直接配置窗口标题

<atom:Window
    Title="Settings"
    TitleAlignment="WindowCenter">
    <!-- window content -->
</atom:Window>

直接使用 WindowTitleBar

<atom:WindowTitleBar
    Title="Settings"
    TitleAlignment="Center"
    LeftAddOn="{Binding Breadcrumb}"
    RightAddOn="{Binding Actions}" />

Center 表示在标题栏可用内容区域内居中,WindowCenter 表示按物理窗口中心对齐。