私がこのようなインターフェースを持っているとしましょう:
public interface ISomeInterface
{
...
}
このインターフェイスを実装するクラスもいくつかあります。
public class SomeClass : ISomeInterface
{
...
}
これで、カスタムDataTemplateを使用して、ISomeInterfaceの項目をリストするWPF ListBoxができました。
データバインディングエンジンは明らかに(私が理解できた)インターフェイスプロパティにバインドすることを許可しません-オブジェクトがSomeClassオブジェクトであることがわかり、SomeClassがバインドされたプロパティをたまたま利用できる場合にのみデータが表示されます非インターフェイスプロパティ。
すべてのオブジェクトが、SomeClassなどではなく、ISomeInterfaceであるかのように動作するようにDataTemplateに指示するにはどうすればよいですか?
ありがとう!
明示的に実装されたインターフェイスメンバーにバインドするには、括弧を使用するだけです。例えば:
暗黙:
{Binding Path=MyValue}
明示的:
{Binding Path=(mynamespacealias:IMyInterface.MyValue)}
この答え Microsoftフォーラムから Beatriz Costa-MSFT は読む価値があります(かなり古い):
データバインディングチームは、しばらく前にインターフェイスのサポートを追加することについて話し合いましたが、適切な設計を思い付かなかったため、実装されませんでした。問題は、オブジェクト型のようにインターフェイスに階層がないことでした。データソースが
IMyInterface1
とIMyInterface2
の両方を実装し、リソースの両方のインターフェイスにDataTemplatesがあるシナリオを考えてみましょう。どのDataTemplateを取得する必要があると思いますか?オブジェクトタイプの暗黙的なデータテンプレートを作成する場合、最初に正確なタイプの
DataTemplate
を検索し、次にその親、祖父母などを検索します。適用するタイプの順序は非常に明確に定義されています。インターフェースのサポートの追加について説明したとき、リフレクションを使用してすべてのインターフェースを見つけ、タイプのリストの最後に追加することを検討しました。私たちが遭遇した問題は、型が複数のインターフェースを実装するときにインターフェースの順序を定義することでした。もう1つ覚えておかなければならないことは、反射はそれほど安くはないということです。これにより、このシナリオではパフォーマンスが少し低下します。
それで、解決策は何ですか?これをすべてXAMLで行うことはできませんが、少しのコードで簡単に行うことができます。
ItemTemplateSelector
のItemsControl
プロパティを使用して、各アイテムに使用するDataTemplate
を選択できます。テンプレートセレクターのSelectTemplate
メソッドでは、テンプレート化するアイテムをパラメーターとして受け取ります。ここでは、実装するインターフェースを確認し、それに一致するDataTemplate
を返すことができます。
簡単に言えば、DataTemplateはインターフェースをサポートしていません(多重継承、明示的対暗黙的などについて考えてください)。私たちがこれを回避する傾向がある方法は、基本クラスを拡張して、DataTemplateの特殊化/一般化を可能にすることです。これはまともな、しかし必ずしも最適ではないということを意味します。
public abstract class SomeClassBase
{
}
public class SomeClass : SomeClassBase
{
}
<DataTemplate DataType="{x:Type local:SomeClassBase}">
<!-- ... -->
</DataTemplate>
別のオプションがあります。 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>
Dummyboyによって提案された答えが最良の答えです(一番上のimoに投票する必要があります)。デザイナーが気に入らないという問題はありますが(「オブジェクトnullはPropertyPathのアクセサーパラメーターとして使用できません」というエラーが表示されます)、適切な回避策があります。回避策は、データテンプレートでアイテムを定義してから、テンプレートをラベルまたはその他のコンテンツコントロールに設定します。例として、私はこのような画像を追加しようとしました
<Image Width="120" Height="120" HorizontalAlignment="Center" Source="{Binding Path=(starbug:IPhotoItem.PhotoSmall)}" Name="mainImage"></Image>
しかし、それは私に同じエラーを与え続けました。解決策は、ラベルを作成し、データテンプレートを使用してコンテンツを表示することでした
<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>
これには欠点がありますが、私にはかなりうまくいくようです。
注:インターフェースプロパティがパス内にある場合は、次のようなより複雑なマルチパートパスを使用することもできます。
_ <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>
_
ヒント:パス式の最後に誤って_)}
_が付くことに注意してください。間抜けなコピー/貼り付けエラーが繰り返し発生します。
Path="(myNameSpace:IShippingPackage.ShippingMethod)}"
Path=
_を使用してください_Path=
_を明示的に使用しないと、バインディングを解析できない可能性があることを発見しました。通常は、次のように記述します。
_Text="{Binding FirstName}"
_
の代わりに
_Text="{Binding Path=FirstName}"
_
しかし、より複雑なインターフェイスバインディングでは、この例外を回避するために_Path=
_が必要であることがわかりました。
_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}"/>
_