문제

나는 두 가지가있다 HeaderedContentControl아래의 것과 마찬가지로 각각의 컨텐츠 속성이 동일한 기본 유형의 두 뷰 모델 속성 중 하나에 묶여있는 것과 같습니다 (하나의 컨트롤은 창의 왼쪽에 있고 오른쪽에 있으므로 뷰 모델 속성 이름).

그러나 어느 뷰 모델 속성도 4 가지 다른 파생 유형 중 하나 일 수 있습니다. 그래서 왼쪽은 Airplane 그리고 오른쪽은 a Car. 그런 다음 나중에 왼쪽은 a Boat 그리고 오른쪽이 될 수 있습니다 Airplane. 나는 원한다 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 's에 대한 정교함입니다. 주저하지 말고 더 물어보십시오!

<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); }
        }
    }
}

추신 : 제네릭 컬렉션에서 상속을 사용하는 것은 .NET 3에서 지원되지 않았다고 생각했습니다.이 코드가 작동한다는 사실에 놀랍게도 놀랍습니다!

다른 팁

컨텐츠의 동적 유형을 기반으로하는 콘텐츠 플레이트가 아니라 헤드러드 콘텐츠 컨트롤의 스타일을 변화시켜야합니까? 즉, 컨트롤 스타일을 변경해야합니까? 아니면 항목의 데이터 테일 플레이트를 변경하면됩니다.

매우 편리한 속성 ContentTemplatesEctor가 있기 때문에 매우 간단한 클래스를 작성하면 컨텐츠의 동적 유형을 바탕으로 데이터 템플릿을 선택할 수 있습니다.

그렇지 않은 경우 스타일을 변화시켜야한다고 확신한다면, 당신이 원하는 스타일의 어떤 부분을 약간 정교하게 구할 수 있습니까? 아마도 동일한 ContentTemplatesElector를 통한 해결 방법이있을 수 있습니다.

스타일을 변경해야한다고 주장하는 경우 스타일 내부의 데이터 트리거 사용에 대해 조금 생각해보십시오. 매우 간단한 변환기를 사용하여 스타일의 특정 속성 (또는 선호하는 경우 모두)을 변경할 수 있습니다.

문제의 세부 사항을 자세히 설명하자마자 추가 지원을 제공하게되어 기쁩니다.

upd: 좋아, 저자는 스타일을 변화시켜야한다고 주장한다. 다음은 어떻게 할 수 있는지에 대한 두 가지 가능한 방법이 있습니다.

첫 번째이자 간단한 솔루션이지만 심각하게 제한된 솔루션 : 이후 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
}

두 번째 솔루션은 더 일반적이며 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