WPF数据绑定接口,而不是实际的对象投射可能?

假设我有这样的界面:

public interface ISomeInterface { ... } 

我也有几个类实现这个接口;

 public class SomeClass : ISomeInterface { ... } 

现在我有一个WPF ListBox列出ISomeInterface的项目,使用自定义的DataTemplate。

数据绑定引擎显然不会(我已经能够找出)允许我绑定到接口属性 – 它看到的对象是一个SomeClass对象,数据只显示如果SomeClass应该碰巧有约束属性可用一个非界面属性。

我怎么能告诉DataTemplate行事,如果每个对象是一个ISomeInterface,而不是一个SomeClass等?

谢谢!

为了绑定到显式实现的接口成员,所有你需要做的就是使用括号。 例如:

隐:

 {Binding Path=MyValue} 

明确:

 {Binding Path=(mynamespacealias:IMyInterface.MyValue)} 

来自Microsoft Beatriz Costa – Microsoft的论坛的回答值得一读(相当老):

数据绑定团队刚刚讨论了增加对接口的支持,但最终没有实现它,因为我们不能为它提供一个好的devise。 问题是接口没有像对象types那样的层次结构。 考虑一下你的数据源实现了IMyInterface1IMyInterface2的场景,并且你在这些资源中的这两个IMyInterface1都有DataTemplates:你认为我们应该select哪个DataTemplate?

在为对象types进行隐式数据模板时,我们首先尝试为确切typesfind一个DataTemplate ,然后为其父类,祖父类等等。 有非常明确的types的顺序供我们申请。 当我们谈到添加对接口的支持时,我们考虑使用reflection来找出所有接口并将它们添加到types列表的末尾。 我们遇到的问题是在types实现多个接口时定义接口的顺序。

我们必须记住的另一件事是反思并不是那么便宜,这会降低我们对这种情况的performance。

那么解决scheme是什么? 你不能在XAML中这样做,但是你可以用一些代码轻松地完成。 ItemsControlItemTemplateSelector属性可以用来select每个项目使用哪个DataTemplate 。 在您的模板select器的SelectTemplate方法中,您将收到作为参数的您将模板的项目。 在这里,你可以检查它实现的接口,并返回与之匹配的DataTemplate

简短的答案是DataTemplate不支持接口(考虑多重inheritance,显式隐式等)。 我们倾向于解决这个问题的方式是让基类扩展到允许DataTemplate专业化/泛化。 这意味着一个体面的但不一定是最佳的解决scheme将是:

 public abstract class SomeClassBase { } public class SomeClass : SomeClassBase { } <DataTemplate DataType="{x:Type local:SomeClassBase}"> <!-- ... --> </DataTemplate> 

dummyboybuild议的答案是最好的答案(它应该被投票到最高层)。 它有一个问题,devise者不喜欢它(给出一个错误“对象null不能用作一个PropertyPath的访问器参数),但有一个很好的解决方法。解决方法是定义一个数据模板中的项目,然后将模板设置为标签或其他内容控件。例如,我试图添加一个像这样的图像

 <Image Width="120" Height="120" HorizontalAlignment="Center" Source="{Binding Path=(starbug:IPhotoItem.PhotoSmall)}" Name="mainImage"></Image> 

但它一直给我同样的错误。 解决scheme是创build一个标签,并使用数据模板来显示我的内容

 <Label Content="{Binding}" HorizontalAlignment="Center" MouseDoubleClick="Label_MouseDoubleClick"> <Label.ContentTemplate> <DataTemplate> <StackPanel> <Image Source="{Binding Path=(starbug:IPhotoItem.PhotoSmall)}" Width="120" Height="120" Stretch="Uniform" ></Image> </StackPanel> </DataTemplate> </Label.ContentTemplate> </Label> 

这有它的缺点,但它似乎对我工作得很好。

你有另一种select。 在DataTemplate上设置一个键,并在ItemTemplate中引用该键。 喜欢这个:

 <DataTemplate DataType="{x:Type documents:ISpecificOutcome}" x:Key="SpecificOutcomesTemplate"> <Label Content="{Binding Name}" ToolTip="{Binding Description}" /> </DataTemplate> 

然后通过键在您想要使用它的地方引用模板,如下所示:

 <ListBox ItemsSource="{Binding Path=SpecificOutcomes}" ItemTemplate="{StaticResource SpecificOutcomesTemplate}" > </ListBox> 

渲染

注意:如果接口属性位于path中,也可以使用更复杂的多部分path:

  <TextBlock> <TextBlock.Text> <Binding Path="Packages[0].(myNamespace:IShippingPackage.ShippingMethod).CarrierServiceCode"/> </TextBlock.Text> </TextBlock> 

或者直接使用Binding指令。

  <TextBlock Text="{Binding Path=Packages[0].(myNamespace:IShippingPackage.ShippingMethod).CarrierServiceCode}"/> 

或者当使用一个接口的多个属性时,您可以在本地重新定义DataContext以使代码更具可读性。

  <StackPanel DataContext={Binding Path=Packages[0].(myNamespace:IShippingPackage.ShippingMethod)}"> <TextBlock Text="{Binding CarrierName}"/> <TextBlock Text="{Binding CarrierServiceCode}"/> </StackPanel> 

提示:小心意外结束)}在pathexpression式的末尾。 愚蠢的复制/粘贴错误,我一直在做。

Path="(myNameSpace:IShippingPackage.ShippingMethod)}"


确保使用Path=

发现如果我不显式使用Path=那么它可能无法parsing绑定。 通常我会写这样的东西:

 Text="{Binding FirstName}" 

代替

 Text="{Binding Path=FirstName}" 

但是对于更复杂的接口绑定,我发现需要Path=来避免这个exception:

 System.ArgumentNullException: Key cannot be null. Parameter name: key at System.Collections.Specialized.ListDictionary.get_Item(Object key) at System.Collections.Specialized.HybridDictionary.get_Item(Object key) at System.ComponentModel.PropertyChangedEventManager.RemoveListener(INotifyPropertyChanged source, String propertyName, IWeakEventListener listener, EventHandler`1 handler) at System.ComponentModel.PropertyChangedEventManager.RemoveHandler(INotifyPropertyChanged source, EventHandler`1 handler, String propertyName) at MS.Internal.Data.PropertyPathWorker.ReplaceItem(Int32 k, Object newO, Object parent) at MS.Internal.Data.PropertyPathWorker.UpdateSourceValueState(Int32 k, ICollectionView collectionView, Object newValue, Boolean isASubPropertyChange) 

即不要这样做:

 <TextBlock Text="{Binding Packages[0].(myNamespace:IShippingPackage.ShippingMethod).CarrierServiceCode}"/>