我的WPF应用程序使用CodePlex wpfmdi容器。
我需要将MdiContainer
的子节点绑定到viewModel属性。
<mdi:MdiContainer Name="Container" Grid.Row="1" Background="GhostWhite" Children="{Binding Path=Container}"/>
如果我这样做,我得到这个错误:
类型为"System.Windows.Data"的对象。绑定'不能转换为类型'System.Collections.ObjectModel.ObservableCollection ' 1[WPF.MDI.MdiChild]'
MdiContainer
中的Children
属性是这样的:
public ObservableCollection<MdiChild> Children { get; set; }
我做错了什么?
Children
属性没有作为依赖属性公开,这意味着您不能绑定它。此外,它在MdiContainer
类型的构造函数中初始化一次,然后将处理程序添加到底层ObservableCollection<MdiChild>
的CollectionChanged
事件中。它永远不会更新或删除。
因此,尽管Children
属性有一个setter,但如果使用它来设置不同的集合,它将破坏控制。这也意味着您不能简单地创建附加属性来公开可绑定的Children
依赖属性。
除此之外,MdiChild
是Control
,因此它实际上与视图模型的目的相矛盾。如果从视图模型公开一组用户界面控件,则会与MVVM模式发生冲突。视图模型不应该有任何关于视图的知识。然而,MDI控件似乎没有遵循自定义控件的通常WPF实践,因此这里没有太多的改进空间,不支持数据模板,MdiContainer
是UserControl
,并且依赖属性非常有限。
如果您真的想继续使用当前的方法来处理这个控件,您可以:
-
创建一个自定义附加行为来同步您的视图模型集合与
MdiContainer
的Children
集合,反之亦然,参见WPF中的XAML行为。 -
使用
Loaded
事件将Children
集合分配给视图模型属性。<mdi:MdiContainer Name="Container" Grid.Row="1" Background="GhostWhite" Loaded="MdiContainer_OnLoaded">
private void MdiContainer_OnLoaded(object sender, RoutedEventArgs e) { var mdiContainer = (MdiContainer)sender; var dataContext = (Main)mdiContainer.DataContext; if (dataContext == null) return; dataContext.Children = mdiContainer.Children; }
-
在
Loaded
事件上使用EventTrigger
,通过自定义触发动作设置Children
集合。这只是前一种方法的不同变体,不需要代码隐藏。WPF包的新XAML行为取代了
System.Windows.Interactivity
命名空间中遗留的Blend行为,它已经包含了这样一个触发器动作。安装Microsoft.Xaml.Behaviors。WpfNuGet包并使用:<mdi:MdiContainer Name="Container" Grid.Row="1" Background="GhostWhite"> <behaviors:Interaction.Triggers> <behaviors:EventTrigger EventName="Loaded"> <behaviors:ChangePropertyAction TargetObject="{Binding DataContext, ElementName=Container}" PropertyName="Children" Value="{Binding Children, ElementName=Container}"/> </behaviors:EventTrigger> </behaviors:Interaction.Triggers> </mdi:MdiContainer>
注意,使用这些方法,您要么同步到您自己的集合,要么直接处理传递给视图模型的MdiContainer
集合。这些只是变通方法。如果你想以干净和MVVM兼容的方式实现这一点,我认为你需要扩展或修复控件本身,这是相当昂贵的,不推荐,因为它似乎已经死了。