数据绑定的ExpandoObject属性在动态XAML中不会更新
我正在使用MVVM Community Toolkit开发一个Avalonia UI应用,在其中需要动态渲染控件并将它们绑定到一个在运行时属性才会确定的视图模型。事实上,我将从JSON文件中读取这些属性以及它们必须绑定的字段。
作为概念验证,我写了这个小应用来测试这些动态绑定。我正在尝试使用一个 ExpandoObject 来封装那些要绑定到动态创建的控件的动态属性。不幸的是,任何动态创建的属性都不会按其动态创建的字段更新。
我的 MainWindow 包含:
<StackPanel>
<StackPanel Orientation="Horizontal">
<Button x:Name="btnCreateDynamicControls" Content="Create Settings Group" Click="btnCreateDynamicControls_Click" />
</StackPanel>
<StackPanel x:Name="stpSettingsGroups" Background="Gray" />
</StackPanel>```
Its code-behind handles the button click by creating an instance for the settings group, having it create dynamic controls, and adding it for display:
using Avalonia.Controls;
using DynamicBindingControls.ViewModels;
namespace DynamicBindingControls.Views {
public partial class MainWindow : Window {
public MainWindow() {
InitializeComponent();
}
private void btnCreateDynamicControls_Click(object? sender, Avalonia.Interactivity.RoutedEventArgs e) {
var dynaSettings = new DynamicSettingsGroup();
dynaSettings.DataContext = new DynamicSettingsGroupVM("Dynamism!");
dynaSettings.CreateDynamicSettings();
stpSettingsGroups.Children.Add(dynaSettings);
}
}
}
The settings group control has a textblock bound to a defined viewmodel property and a space into which to add dynamic fields:
<DockPanel Margin="10,0">
<TextBlock Margin="0,0,5,0" FontWeight="Bold" Text="{Binding Name}" />
<StackPanel x:Name="stpDynamicFields" />
</DockPanel>
Its code-behind creates a dynamic field that it adds to its StackPanel that should bind to its viewmodel property:
using Avalonia.Controls;
using Avalonia.Data;
using System.Collections.Generic;
namespace DynamicBindingControls.Views;
public partial class DynamicSettingsGroup : UserControl {
public DynamicSettingsGroup() {
InitializeComponent();
}
public void CreateDynamicSettings() {
var myDC = DataContext as ViewModels.DynamicSettingsGroupVM;
// Use string indexing to add properties to the ExpandoObject
var props = myDC.DynamicSettingsProperties as IDictionary<string, object>;
props["DynaProp"] = "Brand new";
var settingField = new TextBox {
[!TextBox.TextProperty] = new Binding("DynamicSettingsProperties.DynaProp")
};
stpDynamicFields.Children.Add(settingField);
}
}
Its viewmodel should handle changes to the dynamic field:
using CommunityToolkit.Mvvm.ComponentModel;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.Dynamic;
namespace DynamicBindingControls.ViewModels;
public partial class DynamicSettingsGroupVM : ViewModelBase {
[ObservableProperty]
private string _name;
public dynamic DynamicSettingsProperties { get; set; } = new ExpandoObject();
public DynamicSettingsGroupVM(string aName) {
Name = aName;
((INotifyPropertyChanged)DynamicSettingsProperties).PropertyChanged +=
new PropertyChangedEventHandler(HandlePropertyChanges);
}
private void HandlePropertyChanges(
object? sender, PropertyChangedEventArgs e) {
var dict = DynamicSettingsProperties as IDictionary<string, object>;
Trace.WriteLine($"{e.PropertyName} has changed to {dict[e.PropertyName]}.");
}
}
So upon running, clicking the button adds a settings group, each with a static and dynamic databound field, and the debugging logs:
DynaProp has changed to Brand new.
[Binding]An error occurred binding 'Text' to 'DynamicSettingsProperties.DynaProp' at 'DynaProp': 'Could not find a matching property accessor for 'DynaProp' on 'System.Dynamic.ExpandoObject'.' (TextBox #1589866)
但那初始值从未显示——动态字段看起来完全空白——并且当为动态字段赋予新值时也没有输出,因此我知道 ExpandoObject 的动态添加属性更改没有被监听到。

非常感谢大家的帮助。
解决方案
你的绑定不起作用,因为Avalonia无法将ExpandoObject的键视为真正可绑定的属性。绑定引擎会查找名为 DynaProp 的实际CLR属性,但找不到,就会停止工作。即使值在字典中,Avalonia也看不到或无法跟踪它。
要让它工作,你需要要么一个暴露真实属性并抛出变更通知的包装类,要么使用字典风格的绑定,而不是尝试通过动态属性进行绑定。单独的ExpandoObject无法更新UI。