2016-11-06 46 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,它根據類返回了適當的模板。 –