代码之家  ›  专栏  ›  技术社区  ›  Mark Bostleman

如何将属性绑定到类型

  •  1
  • Mark Bostleman  · 技术社区  · 15 年前

    我有两个 HeaderedContentControl 与下面类似,每个控件的内容属性都绑定到同一基类型的两个视图模型属性中的一个(一个控件位于窗口的左侧,另一个位于右侧,因此视图模型属性名)。

    但是,任一视图模型属性都可以是四种不同的派生类型之一。所以左边可能是 Airplane 右边可以是 Car . 后来,左边可能是 Boat 正确的可能是 飞机 .我想要 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>
    
    3 回复  |  直到 13 年前
        1
  •  0
  •   Dabblernl    15 年前

    我的回答是对阿基米德的详细阐述。请不要犹豫,再问下去!

    <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:我认为.NET3不支持在泛型集合中使用继承。令我高兴的是,这段代码确实有效!

        2
  •  1
  •   HolisticElastic    15 年前

    是否确实需要更改HeaderedContentControl的样式,而不是基于内容的动态类型的ContentTemplate?换句话说:您需要改变控件的样式还是只需要改变项的数据模板?

    因为有非常方便的属性ContentTemplateSelector,并且如果编写非常简单的类,您就可以根据内容的动态类型选择DataTemplate。

    如果不是这样的话,并且你确定你需要改变风格,那么你能详细说明一下你想改变的风格的哪一部分吗?也许有一个解决方法可以通过相同的内容模板选择。

    如果您坚持要改变样式,可以考虑在样式中使用数据触发器-使用一个非常简单的转换器,您可以改变样式的某些属性(如果愿意,可以全部改变)。

    一旦你详细说明你的问题,我很乐意为你提供进一步的帮助。

    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
    }
    

    第二个解决方案更通用,可以应用于 页眉 内容与此无关 内容 内容。它基于绑定的转换器功能。

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

    高温高压

        3
  •  0
  •   Alastair Pitts    15 年前

    尝试使用样式选择器类:

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

    我自己没有具体使用过它,所以我没有任何示例代码供您查看,但是msdn链接有一些示例代码。