You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Avalonia DataGrid行MenuFlyout ItemsSource刷新问题求助

问题解决:Avalonia DataGrid中SplitButton的MenuFlyout ItemsSource不更新

问题根源

Avalonia中,SplitButton的内嵌Flyout属于一次性创建的元素,不属于可视化树的常规节点。当DataGrid行复用(虚拟化)或行的DataContext整体切换时,Flyout的绑定不会自动同步更新,导致始终显示首次加载的列表。


解决方案

方案1:动态绑定Flyout(推荐,ViewModel驱动)

将MenuFlyout作为Model的属性动态生成,确保DataContext切换时Flyout随Model重新创建:

AXAML修改:

<DataGrid ItemsSource="{Binding MyList}" AutoGenerateColumns="False">
    <DataGrid.Columns>
        <DataGridTemplateColumn Header="Actions">
            <DataGridTemplateColumn.CellTemplate>
                <DataTemplate DataType="model:MyModel">
                    <SplitButton Content="My Url List" 
                                 Command="{Binding OpenMyMainUrl}"
                                 Flyout="{Binding Flyout}"/>
                </DataTemplate>
            </DataGridTemplateColumn.CellTemplate>
        </DataGridTemplateColumn>
    </DataGrid.Columns>
</DataGrid>

MyModel代码:

public class MyModel : ViewModelBase
{
    public ObservableCollection<Link> MyUrlList { get; } = new();
    public MenuFlyout Flyout { get; }

    public MyModel()
    {
        Flyout = new MenuFlyout
        {
            ItemsSource = MyUrlList,
            ItemContainerTheme = new ControlTheme(typeof(MenuItem))
            {
                BasedOn = Application.Current.FindResource(typeof(MenuItem)) as ControlTheme,
                Setters =
                {
                    new Setter(MenuItem.HeaderProperty, new Binding("MyText")),
                    new Setter(MenuItem.CommandProperty, new Binding("OpenUrl"))
                }
            }
        };
    }
}

方案2:手动同步DataContext(快速解决)

在SplitButton的DataContextChanged事件中,强制同步Flyout的DataContext并刷新绑定:

AXAML修改:

<DataGrid ItemsSource="{Binding MyList}" AutoGenerateColumns="False">
    <DataGrid.Columns>
        <DataGridTemplateColumn Header="Actions">
            <DataGridTemplateColumn.CellTemplate>
                <DataTemplate DataType="model:MyModel">
                    <SplitButton Content="My Url List" 
                                 Command="{Binding OpenMyMainUrl}"
                                 DataContextChanged="SplitButton_DataContextChanged">
                        <SplitButton.Flyout>
                            <MenuFlyout ItemsSource="{Binding MyUrlList}">
                                <MenuFlyout.ItemContainerTheme>
                                    <ControlTheme TargetType="MenuItem" BasedOn="{StaticResource {x:Type MenuItem}}" x:DataType="model:Link">
                                        <Setter Property="Header" Value="{Binding MyText}"/>
                                        <Setter Property="Command" Value="{Binding OpenUrl}"/>
                                    </ControlTheme>
                                </MenuFlyout.ItemContainerTheme>
                            </MenuFlyout>
                        </SplitButton.Flyout>
                    </SplitButton>
                </DataTemplate>
            </DataGridTemplateColumn.CellTemplate>
        </DataGridTemplateColumn>
    </DataGrid.Columns>
</DataGrid>

后台代码:

private void SplitButton_DataContextChanged(object? sender, EventArgs e)
{
    if (sender is SplitButton splitButton && splitButton.Flyout is MenuFlyout menuFlyout)
    {
        menuFlyout.DataContext = splitButton.DataContext;
        // 强制刷新ItemsSource绑定
        menuFlyout.GetBindingExpression(MenuFlyout.ItemsSourceProperty)?.UpdateTarget();
    }
}

方案3:封装附加属性(复用场景)

通过附加属性封装Flyout的DataContext同步逻辑,可在多个控件中复用:

附加属性代码:

public static class FlyoutSync
{
    public static readonly AttachedProperty<bool> SyncDataContextProperty =
        AvaloniaProperty.RegisterAttached<FlyoutSync, Control, bool>("SyncDataContext", false);

    public static bool GetSyncDataContext(Control element) => element.GetValue(SyncDataContextProperty);
    public static void SetSyncDataContext(Control element, bool value) => element.SetValue(SyncDataContextProperty, value);

    static FlyoutSync()
    {
        SyncDataContextProperty.Changed.Subscribe(OnSyncDataContextChanged);
    }

    private static void OnSyncDataContextChanged(AvaloniaPropertyChangedEventArgs e)
    {
        if (e.Sender is Control control && e.NewValue is bool enable)
        {
            if (enable)
                control.DataContextChanged += OnControlDataContextChanged;
            else
                control.DataContextChanged -= OnControlDataContextChanged;
        }
    }

    private static void OnControlDataContextChanged(object? sender, EventArgs e)
    {
        if (sender is IFlyoutProvider flyoutProvider && flyoutProvider.Flyout is MenuFlyout menuFlyout)
        {
            menuFlyout.DataContext = ((Control)sender).DataContext;
            menuFlyout.GetBindingExpression(MenuFlyout.ItemsSourceProperty)?.UpdateTarget();
        }
    }
}

AXAML使用:

<SplitButton Content="My Url List" 
             Command="{Binding OpenMyMainUrl}"
             local:FlyoutSync.SyncDataContext="True">
    <!-- 原Flyout内容不变 -->
</SplitButton>

内容的提问来源于stack exchange,提问作者Lazaro

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.14 20:03:15