13

ItemsCollection をデータバインドしたいのですが、コレクション アイテムをレンダリングする代わりに、コレクション アイテムのプロパティを介して到達したサブオブジェクトをレンダリングしたいと考えています。

より具体的に言うと、これはゲームの 2D マップ ビューアーになります (ただし、現在の状態ではまだ 2D ではありません)。ItemsControl を ObservableCollection<Square> にデータバインドします。Square には Terrain (Terrain 型) というプロパティがあります。Terrain は基本クラスであり、さまざまな子孫があります。

私が欲しいのは、ItemsControl が、コレクション要素自体ではなく、各コレクション要素から Terrain プロパティをレンダリングすることです。

私はすでにこれを機能させることができますが、不必要なオーバーヘッドがあります。不要なオーバーヘッドを取り除く良い方法があるかどうか知りたいです。

私が現在持っているのは、次のクラスです(簡略化):

public class Terrain {}
public class Dirt : Terrain {}
public class SteelPlate : Terrain {}
public class Square
{
    public Square(Terrain terrain)
    {
        Terrain = terrain;
    }
    public Terrain Terrain { get; private set; }
    // additional properties not relevant here
}

また、以下を含む MapView と呼ばれる UserControl:

<UserControl.Resources>
    <DataTemplate DataType="{x:Type TerrainDataModels:Square}">
        <ContentControl Content="{Binding Path=Terrain}"/>
    </DataTemplate>
    <DataTemplate DataType="{x:Type TerrainDataModels:Dirt}">
        <Canvas Width="40" Height="40" Background="Tan"/>
    </DataTemplate>
    <DataTemplate DataType="{x:Type TerrainDataModels:SteelPlate}">
        <Canvas Width="40" Height="40" Background="Silver"/>
    </DataTemplate>
</UserControl.Resources>
<ItemsControl ItemsSource="{Binding}"/>

このコードを考えると、次のようになります。

mapView.DataContext = new ObservableCollection<Square> {
    new Square(new Dirt()),
    new Square(new SteelPlate())
};

期待どおりのものが得られます。黄褐色のボックス (ダート用) と銀色のボックス (スチールプレート用) を含む StackPanel です。しかし、私は不必要なオーバーヘッドでそれを取得します。

私の具体的な懸念は、Square の DataTemplate にあります。

<DataTemplate DataType="{x:Type TerrainDataModels:Square}">
    <ContentControl Content="{Binding Path=Terrain}"/>
</DataTemplate>

私が本当に言いたいのは、「いいえ、わざわざ Square 自体をレンダリングするのではなく、Terrain プロパティを代わりにレンダリングすることです」ということです。これはそれに近いものですが、これにより、すべての Square のビジュアル ツリーに 2 つのコントロールが追加されます。上記の XAML で明示的にコード化された ContentControl と、その ContentPresenter です。ここでは特に ContentControl は必要ありません。Terrain プロパティの DataTemplate を短絡してコントロール ツリーに直接挿入したいのです。

しかし、collectionitem をレンダリングする (そして Square オブジェクトの DataTemplate を探す) のではなく、collectionitem.Terrain をレンダリングする (Terrain オブジェクトの上記の DataTemplates の 1 つを検索する) ように ItemsControl に指示するにはどうすればよいでしょうか?

地形には DataTemplates を使用したいのですが、必ずしも広場には必要ありません。これは、適切に機能することがわかった最初のアプローチでした。実際、私が本当にやりたいことは、まったく別のことです。実際には、ItemsControl の DisplayMemberPath を「Terrain」に設定したいと考えています。これにより、追加の ContentControl または ContentPresenter を追加することなく、適切なオブジェクト (Dirt または SteelPlate オブジェクト) が直接レンダリングされます。残念ながら、DisplayMemberPath は常に文字列をレンダリングし、地形の DataTemplates を無視します。それは正しい考えですが、私には役に立ちません。

このすべてが時期尚早の最適化かもしれません。欲しいものを手に入れる簡単な方法がない場合は、手に入れたもので生きていきます。しかし、コレクション項目全体ではなくプロパティにバインドする方法についてまだ知らない「WPF の方法」があれば、WPF の理解が深まります。

4

3 に答える 3

10

モデルがどのように見えるかは正確にはわかりませんが、いつでも . オブジェクト プロパティにバインドします。例えば:

<DataTemplate DataType="TerrainModels:Square">
  <StackPanel>
    <TextBlock Content="{Binding Path=Feature.Name}"/>
    <TextBlock Content="{Binding Path=Feature.Type}"/>
  </StackPanel>
</DataTemplate>

アップデート

ただし、コレクション内の 2 つの異なるオブジェクトをバインドする方法を探している場合は、ItemTemplateSelectorプロパティを確認することをお勧めします。

あなたのシナリオでは、次のようになります(テストされていません):

public class TerrainSelector : DataTemplateSelector
{
  public override DataTemplate SelectTemplate(object item, DependencyObject container)
  {
    var square = item as Square;
    if (square == null) 
       return null;
    if (square.Terrain is Dirt)
    {
      return Application.Resources["DirtTemplate"] as DataTemplate;
    }
    if (square.Terrain is Steel)
    {
      return Application.Resources["SteelTemplate"] as DataTemplate;
    }
    return null;
  }
}

それを使用するには、次のようになります。

App.xaml

<Application ..>
  <Application.Resources>
    <DataTemplate x:Key="DirtTemplate">
      <!-- template here -->
    </DataTemplate>
    <DataTemplate x:Key="SteelTemplate">
      <!-- template here -->
    </DataTemplate>
  </Application.Resources>
</Application>

Window.xaml

<Window  ..>
  <Window.Resources>
    <local:TerrainSelector x:Key="templateSelector" />
  </Window.Resources>
  <ItemsControl ItemSource="{Binding Path=Terrain}" ItemTemplateSelector="{StaticResource templateSelector}" />
</Window>
于 2009-04-26T14:15:36.400 に答える
2

これは、私の他の回答とは異なる問題の見方であるため、別の回答を追加しています。

Canvas の背景を変更しようとしている場合は、次のように DataTrigger を使用できます。

<DataTemplate DataType="{x:Type WpfApplication1:Square}">
    <DataTemplate.Resources>
        <WpfApplication1:TypeOfConverter x:Key="typeOfConverter" />
    </DataTemplate.Resources>
    <Canvas Name="background" Fill="Green" />
    <DataTemplate.Triggers>
        <DataTrigger Binding="{Binding Path="Terrain" Converter={StaticResource typeOfConverter}}" Value="{x:Type WpfApplication1:Dirt}">
            <Setter  TargetName="background"Property="Fill" Value="Tan" />
        </DataTrigger>
        <DataTrigger Binding="{Binding Path="Terrain" Converter={StaticResource typeOfConverter}}" Value="{x:Type WpfApplication1:SteelPlate}">
            <Setter TargetName="background" Property="Fill" Value="Silver" />
        </DataTrigger>
    </DataTemplate.Triggers>
</DataTemplate>

このコンバーターも使用する必要があります。

public class TypeOfConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        return value.GetType();
    }

    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
    {
        throw new System.NotImplementedException();
    }

}
于 2009-04-27T00:29:45.690 に答える
2

ビジュアル ツリーのオーバーヘッド (および冗長性) を排除するためにできる最善の方法は次のとおりだと思います。

<ItemsControl ItemsSource="{Binding Squares}">
    <ItemsControl.ItemTemplate>
        <DataTemplate>
            <ContentPresenter Content="{Binding Terrain}"/>
        </DataTemplate>
    </ItemsControl.ItemTemplate>
</ItemsControl>

の各アイテムに対して生成されたのContentプロパティに直接割り当てることで、これをさらに一歩進めることができると断言できました。ContentPresenterItemsControl

<ItemsControl ItemsSource="{Binding Squares}">
    <ItemsControl.ItemContainerStyle>
        <Style>
            <Setter Property="ContentPresenter.Content" Content="{Binding Terrain}"/>
        </Style>
    </ItemsControl.ItemContainerStyle>
</ItemsControl>

ただし、 はではなくContentPresenter親を持っているようDataContextです。これは私には意味がありません。または他のサブクラスで問題なく動作します。おそらくこれは WPF のバグです - 確かではありません。私はそれをさらに調べなければならないでしょう。DataContextSquareListBoxItemsControl

于 2009-04-26T15:09:04.587 に答える