質問

2つの HeaderedContentControl があり、それぞれのコンテンツプロパティが同じベースタイプの2つのビューモデルプロパティの1つにバインドされています(1つのコントロールはウィンドウの左側にあり、1つは右側、したがってビューモデルプロパティ名)。

ただし、ビューモデルプロパティは、4つの異なる派生型のいずれかになります。そのため、左は飛行機で、右は Car です。その後、左がボートになり、右が飛行機になります。ヘッダーコントロールの Style プロパティを、派生型に基づいて動的にしたいと思います。これを宣言的に行う最良の方法は何ですか?

<Window...>
    <StackPanel 
        Grid.Row="2"
        Orientation="Horizontal" VerticalAlignment="Top">
        <Border 
            Height="380" 
            Width="330"
            Margin="0,0,4,0"
            Style="{StaticResource MainBorderStyle}">
            <HeaderedContentControl
                Content="{Binding Path=LeftChild}"
                Header="{Binding LeftChild.DisplayName}"
                Style="{StaticResource StandardHeaderStyle}"
            />
        </Border>

        <Border 
            Height="380" 
            Width="330"
            Style="{StaticResource MainBorderStyle}">
            <HeaderedContentControl
                Content="{Binding Path=RightChild}"
                Header="{Binding RightChild.DisplayName}"
                Style="{StaticResource StandardHeaderStyle}"
            />  
        </Border>
    </StackPanel>
</Window>


<ResourceDictionary 
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:vm="clr-namespace:myViewModelNamespace;assembly=myViewModelAssembly"
    xmlns:vw="clr-namespace:myViewNamespace" >

    <!--***** Item Data Templates ****-->
    <DataTemplate DataType="{x:Type vm:CarViewModel}">
        <vw:CarView />
    </DataTemplate>

    <DataTemplate DataType="{x:Type vm:BoatViewModel}">
        <vw:BoatView />
    </DataTemplate>

    <DataTemplate DataType="{x:Type vm:AirplaneViewModel}">
        <vw:AirplaneView />
    </DataTemplate>

    <!--***** 
        Other stuff including the StandardHeaderStyle and the MainBorderStyle
    ****-->

</ResourceDictionary>
役に立ちましたか?

解決

私の答えは、Archimedの詳細です。さらに質問することをheしないでください!

<Window x:Class="Datatemplate_selector.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Window1" Height="300" Width="300"
xmlns:local="clr-namespace:Datatemplate_selector">
<Window.Resources>
    <DataTemplate DataType="{x:Type local:CarDetail}">
     <Border BorderBrush="Yellow" BorderThickness="2">
        <HeaderedContentControl Margin="4" Foreground="Red">
            <HeaderedContentControl.Header>
                <Border BorderBrush="Aquamarine" BorderThickness="3">
                    <TextBlock Text="{Binding Name}"/>
                </Border>
             </HeaderedContentControl.Header>
            <HeaderedContentControl.Content>
                <Border BorderBrush="CadetBlue" BorderThickness="1">
                    <TextBlock TextWrapping="Wrap" Text="{Binding Description}"/>
                </Border>
            </HeaderedContentControl.Content>
        </HeaderedContentControl>
       </Border>
    </DataTemplate>
    <DataTemplate DataType="{x:Type local:HouseDetail}">
        <HeaderedContentControl Margin="4" Foreground="Yellow" FontSize="20"
                    Header="{Binding Name}">
            <HeaderedContentControl.Content>
                <TextBlock Foreground="BurlyWood"  TextWrapping="Wrap"
                           Text="{Binding Description}"/>
            </HeaderedContentControl.Content>
        </HeaderedContentControl>
    </DataTemplate>
    <DataTemplate DataType="{x:Type local:ItemDetail}">
        <HeaderedContentControl Margin="4" Foreground="Green" FontStyle="Italic" 
                    Content="{Binding Description}"
                    Header="{Binding Name}">
        </HeaderedContentControl>
    </DataTemplate>
</Window.Resources>
<StackPanel>
    <ItemsControl ItemsSource="{Binding ItemDetails}">
        <ItemsControl.ItemsPanel>
            <ItemsPanelTemplate>
                <UniformGrid Columns="2"/>
            </ItemsPanelTemplate>
        </ItemsControl.ItemsPanel>
    </ItemsControl>
</StackPanel>

using System.Collections.ObjectModel;
using System.Windows;

namespace Datatemplate_selector
{
   public partial class Window1 : Window
    {
        public ObservableCollection<ItemDetail> ItemDetails { get; set; }

        public Window1()
        {
            ItemDetails = new ObservableCollection<ItemDetail>
                              {
                                  new CarDetail{Name="Trabant"},
                                  new HouseDetail{Name="Taj Mahal"}
                              };
            DataContext = this;
            InitializeComponent();
        }
    }

    public class ItemDetail:DependencyObject
    {
        public string Name
        {
            get { return (string)GetValue(NameProperty); }
            set { SetValue(NameProperty, value); }
        }

        public static readonly DependencyProperty NameProperty =
            DependencyProperty.Register("Name",
            typeof(string),
            typeof(ItemDetail),
            new UIPropertyMetadata(string.Empty));

        public virtual string Description
        {
             get { return Name + " has a lot of details"; }
        }
    }

    public class CarDetail:ItemDetail
    {
        public override string Description
        {
            get { return string.Format("The car {0} has two doors and a max speed of 90 kms/hr", Name); }
        }
    }

    public class HouseDetail:ItemDetail
    {
        public override string Description
        {
            get { return string.Format("The house {0} has two doors and a backyard", Name); }
        }
    }
}

PS:ジェネリックコレクションでのこの継承の使用は、.Net 3ではサポートされていないと考えました。このコードが機能することを嬉しく思います!

他のヒント

コンテンツの動的タイプに基づいたContentTemplateではなく、HeaderedContentControlのスタイルを変更する必要がありますか?つまり、コントロールのスタイルを変更する必要がありますか、それともアイテムのデータテンプレートを変更するだけですか?

非常に便利なプロパティContentTemplateSelectorがあり、非常に単純なクラスを記述する場合、コンテンツの動的タイプに基づいてDataTemplateを選択できるためです。

そうではなく、スタイルを変更する必要があると確信している場合は、スタイルのどの部分を変更したいのかを少し詳しく説明してもらえますか? / p>

スタイルの変更を主張する場合は、スタイル内でデータトリガーを使用することについて少し考えてください。非常にシンプルなコンバーターを使用すると、スタイルの特定のプロパティ(または必要に応じてすべて)を変更できます。

問題の詳細を詳しく説明できるようになり次第、さらなるサポートを提供させていただきます。

UPD :OK、著者はスタイルを変える必要があると主張しています。以下に、2つの可能な方法を示します。

最初のシンプルなソリューション、ただし非常に限定的なソリューション: Header コンテンツは Content コンテンツで指定できるため、これを行うことができます:

<DataTemplate x:Key="DefaultTemplate">
    <HeaderedContentControl Content="{Binding}"
                            Header="{Binding DisplayName}"
                            Style="{StaticResource DefaultStyle}" />
</DataTemplate>
<DataTemplate x:Key="CarTemplate"
              DataType="dm:Car">
    <HeaderedContentControl Content="{Binding}"
                            Header="{Binding DisplayName}"
                            Style="{StaticResource CarStyle}" />
</DataTemplate>
<DataTemplate x:Key="BoatTemplate"
              DataType="dm:Boat">
    <HeaderedContentControl Content="{Binding}"
                            Header="{Binding DisplayName}"
                            Style="{StaticResource BoatStyle}" />
</DataTemplate>

<u:TypeBasedDataTemplateSelector x:Key="MySelector"
                                 DefaultTemplate="{StaticResource DefaultTemplate}"
                                 NullTemplate="{StaticResource DefaultTemplate}">
    <u:TypeMapping Type="dm:Car" Template="{StaticResource CarTemplate}" />
    <u:TypeMapping Type="dm:Boat" Template="{StaticResource BoatTemplate}" />
</u:TypeBasedDataTemplateSelector>

<ContentPresenter Content="{Binding LeftChild}"
                  ContentTemplateSelector="{StaticResource MySelector}" />

この純粋に宣言的なソリューションをサポートする必要がある唯一のコードは、非常に単純なテンプレートセレクターの実装です。ここにあります:

public class TypeMapping
{
    public Type Type { get; set; }
    public DataTemplate Template { get; set; }
}

public class TypeBasedDataTemplateSelector : DataTemplateSelector, IAddChild
{
    public DataTemplate DefaultTemplate { get; set; }
    public DataTemplate NullTemplate { get; set; }
    private readonly Dictionary<Type, DataTemplate> Mapping = new Dictionary<Type, DataTemplate>();

    public override DataTemplate SelectTemplate(object item, DependencyObject container)
    {
        if (item == null)
            return NullTemplate;

        DataTemplate template;

        if (!Mapping.TryGetValue(item.GetType(), out template))
            template = DefaultTemplate;

        return template;
    }


    #region IAddChild Members

    public void AddChild(object value)
    {
        if (!(value is TypeMapping))
            throw new Exception("...");

        var tm = (TypeMapping)value;

        Mapping.Add(tm.Type, tm.Template);
    }

    public void AddText(string text)
    {
        throw new NotImplementedException();
    }

    #endregion
}

2番目の解決策はより一般的であり、 Header コンテンツが Content コンテンツとは無関係の場合に適用できます。これは、バインディングのコンバーター機能に基づいています。

<Style x:Key="StandardHeaderedStyle">
    <!--...-->
</Style>

<Style x:Key="CarHeaderedStyle"
       BasedOn="{StaticResource StandardHeaderedStyle}">
    <!--...-->
</Style>

<Style x:Key="BoatHeaderedStyle"
       BasedOn="{StaticResource StandardHeaderedStyle}">
    <!--...-->
</Style>

<Style x:Key="UnknownHeaderedStyle"
       BasedOn="{StaticResource StandardHeaderedStyle}">
    <!--...-->
</Style>

<u:StylesMap x:Key="MyStylesMap" 
             FallbackStyle="{StaticResource UnknownHeaderedStyle}">
    <u:StyleMapping Type="Car" Style="{StaticResource CarHeaderedStyle}" />
    <u:StyleMapping Type="Boat" Style="{StaticResource BoatHeaderedStyle}" />
</u:StylesMap>

<u:StyleSelectorConverter x:Key="StyleSelectorConverter" />

<HeaderedContentControl Content="{Binding LeftChild}"
                        Header="{Binding LeftChild.DisplayName}">
    <HeaderedContentControl.Style>
        <Binding Path="LeftChild"
                 Converter="{StaticResource StyleSelectorConverter}"
                 ConverterParameter="{StaticResource MyStylesMap}" />
    </HeaderedContentControl.Style>
</HeaderedContentControl>

バッキングコードも必要です:

public class StyleMapping
{
    public Type Type { get; set; }
    public Style Style { get; set; }
}

public class StylesMap : Dictionary<Type, Style>, IAddChild
{
    public Style FallbackStyle { get; set; }

    #region IAddChild Members

    public void AddChild(object value)
    {
        if (!(value is StyleMapping))
            throw new InvalidOperationException("...");

        var m = (StyleMapping)value;

        this.Add(m.Type, m.Style);
    }

    public void AddText(string text)
    {
        throw new NotImplementedException();
    }

    #endregion
}

public class StyleSelectorConverter : IValueConverter
{
    #region IValueConverter Members

    public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        var m = (StylesMap)parameter;

        if (value == null)
            return m.FallbackStyle;

        Style style;
        if (!m.TryGetValue(value.GetType(), out style))
            style = m.FallbackStyle;

        return style;
    }

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

    #endregion
}

HTH

スタイルセレクタークラスを使用してみてください:

http://msdn.microsoft.com /en-us/library/system.windows.controls.styleselector.aspx

私は自分で具体的に使用したことがないので、チェックアウトするサンプルコードはありませんが、MSDNリンクにはいくつかあります。

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top