感觉也是很久没有写文章了,今天的问题仍然是与绑定有关,当然不再是简单的集合数据绑定问题,这次还涉及到事件与RelayCommand绑定所相关的Converter,以及View层级上一些理解。
========================================================
上篇文章末尾的时候讲了在List类中绑定ViewModel中的RelayCommand的时候应该用到RelativeSource来重定位DataContext从而跳出ItemsSource的方法,然而在接下来的开发中又遇到相似的问题。上代码:
<TabControl TabStripPlacement="Top"
DataContext="{Binding Source={StaticResource Locator}, Path=LibraryControl}">
<TabItem Header="怪物库">
<ListView ItemsSource="{Binding MonsterSection}" x:Name="monster_ListView">
<ListView.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Name}">
<i:Interaction.Triggers>
<i:EventTrigger EventName="PreviewMouseMove">
<command:EventToCommand Command="{Binding DataContext.MonsterMouseMove,
RelativeSource={RelativeSource AncestorType={x:Type TabControl},
Mode=FindAncestor, AncestorLevel=1}}"
EventArgsConverter="{StaticResource mscv}"
EventArgsConverterParameter="{Binding RelativeSource={RelativeSource AncestorType={x:Type TextBlock}, Mode=FindAncestor}}"
PassEventArgsToCommand="True"/>
</i:EventTrigger>
</TextBlock>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</TabItem>
</TabControl>
为了方便观看我删掉了其他多余的代码,需要注意的是在Command的RelativeSource里面AncestorType是TabControl而不是他的任何一个TabItem,而其实这里只能是TabControl,虽然的确DataContext是具有继承特征的(例如一个TabItem的DataContext为空则会往UI树向上寻找可填充的DataContext),但是这里用TabItem却没办法很好的继承这个DataContext,那是因为DataTemplate,以及其他模板类的标签,在UI树的层面上都是和Item平行的,也就是:
<TabControl>
<Template>
<!--sth..-->
</Template>
<TabItem>
<!--sth..-->
</TabItem>
</TabControl>
TabItem并不是这个模板中内容的祖先,虽然继承了同样的DataContext,却无法被寻找到,在有模板的情况下直接找具有有效Context的UI层级似乎才是最不容易出错的方法。而在模板之中就不用顾虑这些,直接用FindAncestor的方法就可以找到,顺带一提就是RelativeSource在FindAncestor的时候是会自动忽略非目标类型的层级的(详见深入浅出WPF的绑定一章)。
=======================================================
在MVVM模式下编写程序时,常常会遇到事件与RelayCommand的各种复杂的转换,其中就牵涉到事件的各个参数的传递与转换,其中最为基本的路由事件的参数转换器:
public class Converter : IEventArgsConverter
{
public object Convert(object value, object parameter) {
//need to return a value;
}
}
其中value是当前转换的事件的EventArgs参数,另外一个则是在View中传入的,Convert是必须实现的接口,实现之后就可以被外部引用并使用了。
其实这个地方的难点主要在于传入的参数,考虑到后面程序的需要而正确的传入参数很是考验绑定知识的基本功..
顺带一提在这次开发中又发现一个wpf万年老bug,那就是Mouse.GetPositon(Control)
这个函数无法返回正确的相对坐标值,需要利用当前的EventArgs来返回正确的坐标,例如args.GetPosition(Control)
,详细请看StackOverflow,一搜就是。
欢迎指正。