Avalonia DataGrid行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
相关产品推荐
相关产品推荐

