2016-08-20 57 views
2

如何將DataTemplate設置爲PageResource中的ContentControl?我想在我的ContentControl中顯示一個UserControl,並且想要像導航區域一樣使用ContentControl。所以它可以改變UserControls顯示的內容。如何在Windows通用應用程序10的PageResource中將DataTemplate設置爲ContentControl?

我有一個Shell.xaml:

<Page 
    x:Class="MyProject.Shell" 
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"  
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"  
    xmlns:local="using:MyProject" 
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
    mc:Ignorable="d" 
    xmlns:View="using:MyProject.View" 
    xmlns:ViewModel="using:MyProject.ViewModel"> 

    <Page.DataContext> 
     <ViewModel:ShellViewModel /> 
    </Page.DataContext> 

    <Page.Resources> 
     <DataTemplate> 
      <View:MyUserControlViewModel1 /> 
     </DataTemplate> 

     <DataTemplate> 
      <View:MyUserControlViewModel2 /> 
     </DataTemplate> 
    </Page.Resources> 

    <StackPanel> 

     <ItemsControl ItemsSource="{Binding PageViewModels}"> 
      <ItemsControl.ItemTemplate> 
       <DataTemplate> 
        <Button Content="{Binding Name}"/> 
       </DataTemplate> 
      </ItemsControl.ItemTemplate> 
     </ItemsControl> 

     <ContentControl Content="{Binding CurrentPageViewModel}"> 
     </ContentControl> 
    </StackPanel> 
</Page> 

我殼牌的視圖模型是:

namespace MyProject.ShellViewModel 
{ 
    class ShellViewModel : ObservableObject 
    { 
     #region Fields 

     private ICommand _changePageCommand; 
     private IPageViewModel _currentPageViewModel; 
     private List<IPageViewModel> _pageViewModels; 

     #endregion 

     #region Properties/Commands 

     public List<IPageViewModel> PageViewModels 
     { 
      get 
      { 
       if (_pageViewModels == null) 
       { 
        _pageViewModels = new List<IPageViewModel>(); 
       } 
       return _pageViewModels; 
      } 
     } 

     public IPageViewModel CurrentPageViewModel 
     { 
      get { return _currentPageViewModel; } 
      set 
      { 
       if (_currentPageViewModel != value) 
       { 
        _currentPageViewModel = value; 
        OnPropertyChanged("CurrentPageViewModel"); 
       } 
      } 
     } 
     #endregion 

     #region Methods 

     public ShellViewModel() 
     { 
      PageViewModels.Add(new MyUserControlViewModel1()); 
      PageViewModels.Add(new MyUserControlViewModel2()); 

      CurrentPageViewModel = PageViewModels[0]; 
     } 

     #endregion 
    } 
} 

我試圖從這個鏈接設置Page.Resource象下面這樣:Window vs Page vs UserControl for WPF navigation?

<Window.Resources> 
     <DataTemplate DataType="{x:Type local:HomeViewModel}"> 
     <local:HomeView /> <!-- This is a UserControl --> 
     </DataTemplate> 
     <DataTemplate DataType="{x:Type local:ProductsViewModel}"> 
     <local:ProductsView /> <!-- This is a UserControl --> 
     </DataTemplate> 
    </Window.Resources> 

但這些使用其他名稱空間,它不適合我,因爲我的應用程序是Windows 10通用應用程序例如,DataTemplate沒有DataType屬性。

我想使我的應用程序使用MVVM模式(如果它不是從代碼片段不明顯)。

回答

2

您可以通過創建一個派生自DataTemplateSelector的類來實現。

在這個類中,您可以覆蓋SelectTemplateCore方法,該方法將根據數據類型返回您需要的DataTemplate。爲了使這一切變得更加自動化,您可以將每個模板的關鍵字設置爲與類的名稱相匹配,然後使用GetType().Name搜索具有該名稱的資源。

爲了能夠在不同的級別上提供特定的實現,您可以使用VisualTreeHelper.GetParent()直接找到樹,直到找到匹配的資源並使用Application.Current.Resources[ typeName ]作爲回退。

要使用您的自定義模板選擇器,請將其設置爲ContentControlContentTemplateSelector屬性。

這裏是一個AutoDataTemplateSelector

public class AutoDataTemplateSelector : DataTemplateSelector 
{ 
    protected override DataTemplate SelectTemplateCore(object item) => GetTemplateForItem(item, null); 

    protected override DataTemplate SelectTemplateCore(object item, DependencyObject container) => GetTemplateForItem(item, container); 

    private DataTemplate GetTemplateForItem(object item, DependencyObject container) 
    { 
     if (item != null) 
     { 
      var viewModelTypeName = item.GetType().Name; 
      var dataTemplateInTree = FindResourceKeyUpTree(viewModelTypeName, container); 
      //return or default to Application resource 
      return dataTemplateInTree ?? (DataTemplate)Application.Current.Resources[ viewModelTypeName ]; 
     } 
     return null; 
    } 

    /// <summary> 
    /// Tries to find the resources up the tree 
    /// </summary> 
    /// <param name="resourceKey">Key to find</param> 
    /// <param name="container">Current container</param> 
    /// <returns></returns> 
    private DataTemplate FindResourceKeyUpTree(string resourceKey, DependencyObject container) 
    { 
     var frameworkElement = container as FrameworkElement; 
     if (frameworkElement != null) 
     { 
      if (frameworkElement.Resources.ContainsKey(resourceKey)) 
      { 
       return frameworkElement.Resources[ resourceKey ] as DataTemplate; 
      } 
      else 
      { 
       return FindResourceKeyUpTree(resourceKey, VisualTreeHelper.GetParent(frameworkElement)); 
      } 
     } 
     return null; 
    } 
} 

樣本實現你現在可以實例化它作爲一種資源,併爲每種類型的視圖模型的創建資源使用

<Application.Resources> 
    <local:AutoDataTemplateSelector x:Key="AutoDataTemplateSelector" /> 

    <!-- sample viewmodel data templates --> 
    <DataTemplate x:Key="RedViewModel"> 
     <Rectangle Width="100" Height="100" Fill="Red" /> 
    </DataTemplate> 
    <DataTemplate x:Key="BlueViewModel"> 
     <Rectangle Width="100" Height="100" Fill="Blue" /> 
    </DataTemplate> 
</Application.Resources> 

現在你可以使用它與ContentControl如下:

<ContentControl ContentTemplateSelector="{StaticResource AutoDataTemplateSelector}" 
    Content="{x:Bind CurrentViewModel, Mode=OneWay}" /> 

我已經把sample solution on GitHub

相關問題