2016-11-06 45 views
1

我有一个继承的数据模型,我想在我的xaml标记中为每个子类显示正确的字段。基于UWP中的类的条件xaml布局

public abstract class Model { 
    public int Id { set; get; } 
} 
public class ModelOne : Model { 
    public int Tasks { set; get; } 
} 
public class ModelTwo : Model { 
    public DateTime date { set; get; } 
} 

我的xaml的数据上下文将是一个Model类型的字段。每个模型都有不同的字段,我想要显示,但其余的xaml将是相同的,所以我希望我可以避免创建两个视图。我可以创建一个将类转换为可见性的转换器,但我认为这不是最好的解决方案。 UWP-xaml中是否有任何功能可以帮助我实现这一目标?

回答

1

有多种方法可以解决这个问题。但对我而言,最简单也是最合乎逻辑的就是像往常一样创建DataTemplate资源,但让更多派生类的模板使用基类的模板。

举例来说,像这样给出的模型类:

class MainModel 
{ 
    public Model BaseModel { get; set; } 
    public ModelOne ModelOne { get; set; } 
    public ModelTwo ModelTwo { get; set; } 
} 

class Model 
{ 
    public int BaseValue { get; set; } 
} 

class ModelOne : Model 
{ 
    public int OneValue { get; set; } 
} 

class ModelTwo : Model 
{ 
    public int TwoValue { get; set; } 
} 

您可以编写XAML看起来像这样:

<Page 
    x:Class="TestSO40445037UwpTemplateInherit.MainPage" 
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
    xmlns:l="using:TestSO40445037UwpTemplateInherit" 
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
    mc:Ignorable="d"> 

    <Page.DataContext> 
    <l:MainModel> 
     <l:MainModel.BaseModel> 
     <l:Model BaseValue="17"/> 
     </l:MainModel.BaseModel> 
     <l:MainModel.ModelOne> 
     <l:ModelOne BaseValue="19" OneValue="29"/> 
     </l:MainModel.ModelOne> 
     <l:MainModel.ModelTwo> 
     <l:ModelTwo BaseValue="23" TwoValue="37"/> 
     </l:MainModel.ModelTwo> 
    </l:MainModel> 
    </Page.DataContext> 

    <Page.Resources> 
    <DataTemplate x:Key="baseModelTemplate" x:DataType="l:Model"> 
     <TextBlock Text="{Binding BaseValue}"/> 
    </DataTemplate> 
    <DataTemplate x:Key="modelOneTemplate" x:DataType="l:ModelOne"> 
     <StackPanel> 
     <ContentControl Content="{Binding}" ContentTemplate="{StaticResource baseModelTemplate}"/> 
     <TextBlock Text="{Binding OneValue}"/> 
     </StackPanel> 
    </DataTemplate> 
    <DataTemplate x:Key="modelTwoTemplate" x:DataType="l:ModelTwo"> 
     <StackPanel> 
     <ContentControl Content="{Binding}" ContentTemplate="{StaticResource baseModelTemplate}"/> 
     <TextBlock Text="{Binding TwoValue}"/> 
     </StackPanel> 
    </DataTemplate> 
    </Page.Resources> 

    <Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"> 
    <StackPanel Orientation="Horizontal" 
       HorizontalAlignment="Center" VerticalAlignment="Center"> 
     <ContentControl Content="{Binding BaseModel}" Margin="5" 
        ContentTemplate="{StaticResource baseModelTemplate}"/> 
     <ContentControl Content="{Binding ModelOne}" Margin="5" 
        ContentTemplate="{StaticResource modelOneTemplate}"/> 
     <ContentControl Content="{Binding ModelTwo}" Margin="5" 
        ContentTemplate="{StaticResource modelTwoTemplate}"/> 
    </StackPanel> 
    </Grid> 
</Page> 

以上可能是矫枉过正看起来字面上就像班在你的问题中的例子。但对于更复杂的视图模型,这很有效。派生类可以重用基类模板,但可以控制该模板的呈现方式(可以将ContentControl放在模板所需的任何位置)。

除了允许在任何派生类模板中重用基类模板外,还可以避免需要包含具有所有可能视图模型绑定元素的单个模板。这种方法不仅会在运行时导致过重的视觉树,而且还会导致很多绑定错误,因为隐藏的元素仍然会尝试绑定到视图模型上的非现有属性。

在派生类模板中重用基类模板避免了这一切,对我来说更适合视图模型类继承的一般体系结构。


注意,这是它会在WPF做的方式有所不同:

<DataTemplate DataType="{x:Type lm:Model}"> 
    <!-- template definition here...for example: --> 
    <StackPanel> 
    <TextBlock Text="{Binding Id, StringFormat=Id: {0}}"/> 
    </StackPanel> 
</DataTemplate> 

<DataTemplate DataType="{x:Type lm:ModelOne}"> 
    <!-- template for ModelOne here; a ContentControl as shown below should be placed 
     in the as needed for your desired visual appearance. For example, 
     here is a template using a StackPanel as the top-level element, 
     with the base class template shown as the first item in the panel --> 
    <StackPanel> 
    <ContentControl Content="{Binding}" Focusable="False"> 
     <ContentControl.ContentTemplate> 
     <StaticResourceExtension> 
      <StaticResourceExtension.ResourceKey> 
      <DataTemplateKey DataType="{x:Type lm:Model}"/> 
      </StaticResourceExtension.ResourceKey> 
     </StaticResourceExtension> 
     </ContentControl.ContentTemplate> 
    </ContentControl> 
    <TextBlock Text="{Binding Tasks, StringFormat=Tasks: {0}}"/> 
    </StackPanel> 
</DataTemplate> 


<DataTemplate DataType="{x:Type lm:ModelTwo}"> 
    <!-- template for ModelTwo here; same as above --> 
    <StackPanel> 
    <ContentControl Content="{Binding}" Focusable="False"> 
     <ContentControl.ContentTemplate> 
     <StaticResourceExtension> 
      <StaticResourceExtension.ResourceKey> 
      <DataTemplateKey DataType="{x:Type lm:Model}"/> 
      </StaticResourceExtension.ResourceKey> 
     </StaticResourceExtension> 
     </ContentControl.ContentTemplate> 
    </ContentControl> 
    <TextBlock Text="{Binding date, StringFormat=date: {0}}"/> 
    </StackPanel> 
</DataTemplate> 

(其中,当然,lm:是你的模型类的类型无论你的实际的XML命名空间)

不幸的是,它看起来像许多其他有用的WPF功能一样,UWP(以及WinRT,Phone,Silverlight等)缺少自动数据模板资源键定义和查找。 WPF示例利用了这一点,即使使用模型类型作为基类数据模板资源引用的关键字。

在UWP,似乎所有的数据模板资源必须明确给出的密钥,并且可以显式引用,内联到一个模板性质(例如ContentTemplateItemTemplate),或经由资源引用(例如{Static Resource ...})。

文档诱人hints at the possibility of using automatic lookup [重点煤矿]:

所有资源都需要有一个关键。通常这个键是一个用x:Key =“myString”定义的字符串。但是,还有其他几种指定键的方法:

  • Style和ControlTemplate需要一个TargetType,并且如果未指定x:Key,将使用TargetType作为键。在这种情况下,键是实际的Type对象,而不是字符串。 (请参阅下面的示例)
  • 如果未指定x:Key,则具有TargetType的DataTemplate资源将使用TargetType作为键。在这种情况下,键是实际的Type对象,而不是字符串。
  • x:名称可以用来代替x:Key。但是,x:Name还会为该资源的字段生成代码。因此,x:Name比x:Key效率低,因为该页面在加载页面时需要初始化该字段。

但XAML编辑器和编译器不能识别DataTemplate.TargetType财产,有一个在the DataTemplate class documentation没有提到它,x:DataType没有避免需要仍然定义x:Key属性的资源,我不没有看到明确使用实际的Type引用作为资源键的方法。

我只能推测文档页面实际上是不正确的。也许一些懒惰的科技作家只是复制/粘贴WPF?我不知道。

所以上面的UWP例子只需要在简单的{StaticResource ...}引用编码。

当然,UWP中的另一个选项是使用DataTemplateSelector,这是WPF功能,似乎仍然在UWP中受支持。这里有一个相关的问题,其中包括使用选择器单向的示例:UWP DataTemplates for multiple item types in ListView。当然,还有许多其他合理的方式来初始化和使用DataTemplateSelector。当XAML自动支持的行为不足时,这基本上就是回退,并且在实施时,您可以做到这一点,但对您来说最有意义。

+0

这似乎不适用于UWP。 DataType不被识别为DataTemplate上的成员。 x:DataType似乎可以工作,但Type是“不支持在Windows通用项目中”。 –

+0

@Kristoffer:对不起,我没有太多的UWP,并没有意识到它不像WPF那样做数据模板资源。请参阅上面的修订答案,使用相同的技术,但使用UWP习语。 –

+0

谢谢,这是很多处理,但我设法创建模板,扩展基本模板,正如你在答案中所建议的。这提高了我的标记了很多。不过,我不知道我应该如何获得正确的模板。我最终没有使用x:Datatype,但我在代码隐藏中创建了一个TemplateSelector,它根据类返回了适当的模板。 –