我有一个具有继承的数据模型,我希望为我的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中有什么特性可以帮助我实现这一点吗?
发布于 2016-11-06 03:24:20
有各种各样的方法来解决这个问题。但对我来说,最简单、最符合逻辑的方法是像往常一样创建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中,所有数据模板资源似乎都必须显式地给出一个键,并显式地引用,或者内联到模板属性(例如ContentTemplate或ItemTemplate),或者通过资源引用(例如{Static Resource ...})引用。
令人着迷的提示使用自动查找的可能性文档强调了地雷
所有的资源都需要一把钥匙。通常,该键是用x:Key=“myString”定义的字符串。但是,还有其他几种方法可以指定密钥:
但是XAML编辑器和编译器不识别DataTemplate.TargetType属性,在类文档中没有提到它,x:DataType没有避免仍然需要为资源定义x:Key属性,而且我也没有看到一种显式使用实际Type引用作为资源键的方法。
我只能猜测文档页实际上是不正确的。也许某个懒惰的科技作家只是从WPF复制/粘贴?我不知道。
因此,上面的UWP示例适用于在需要时显式编码的简单{StaticResource ...}引用。
当然,UWP中的另一个选项是使用DataTemplateSelector,这是一个在UWP中似乎仍然支持的WPF特性。下面是一个相关的问题,其中包括一个使用选择器的方法的示例:用于DataTemplates中多个项目类型的UWP ListView。当然,还有许多其他合理的方法来初始化和使用DataTemplateSelector。当XAML中自动支持的行为还不够时,这基本上就是退步,在实现XAML时,您可以这样做,但这对您来说是最有意义的。
https://stackoverflow.com/questions/40445037
复制相似问题