自定义控件中ItemsControl的ItemsSource绑定泛型DependencyProperty问题
错误根源
你注册依赖属性时犯了关键错误:第一个参数传成了类型名ObservableCollection<dynamic>,但必须传CLR属性的名称InternalControl。WPF靠这个参数关联CLR属性和底层依赖属性,参数不匹配会导致绑定系统识别不到这是一个有效的依赖属性,从而抛出XamlParseException。
分步修复
1. 修正依赖属性注册代码
把依赖属性的注册参数改对,同时为了适配所有IEnumerable类型的集合,建议将属性类型从ObservableCollection<dynamic>改为更通用的IEnumerable(另外注意依赖属性的命名规范应为「属性名+Property」,之前的SetInternalControlProperty不符合规范,一并修正):
public static readonly DependencyProperty InternalControlProperty = DependencyProperty.Register( nameof(InternalControl), // 这里必须是CLR属性的名称 typeof(IEnumerable), // 适配所有枚举类型集合 typeof(UtilityExpander)); public IEnumerable InternalControl { get { return (IEnumerable)GetValue(InternalControlProperty); } set { SetValue(InternalControlProperty, value); } }
2. 修正UserControl内的ItemsControl绑定
默认情况下,ItemsControl会继承外部的DataContext,直接绑定InternalControl会去外部ViewModel找这个属性,而不是控件自身。需要通过RelativeSource指定绑定到控件实例:
<Grid> <ItemsControl ItemsSource="{Binding Path=InternalControl, RelativeSource={RelativeSource AncestorType=local:UtilityExpander}}"> <ItemsControl.ItemsPanel> <ItemsPanelTemplate> <StackPanel /> </ItemsPanelTemplate> </ItemsControl.ItemsPanel> </ItemsControl> </Grid>
3. 关于动态类型适配的说明
如果需要保留动态类型的操作能力,也可以继续使用ObservableCollection<dynamic>作为属性类型,但注册时的属性名必须正确。不过用IEnumerable更灵活,因为ObservableCollection<UtilityGas>本身就实现了IEnumerable,绑定无需额外转换。
验证
修改完成后,主界面的{Binding GasControl}绑定就能正确关联到UtilityExpander的InternalControl依赖属性,初始化时的报错会消失。
内容的提问来源于stack exchange,提问作者Oiproks

