2016-04-13 89 views
1

我想创建一个视图的DataTemplate,以显示一个特定的UserControl类型(如texbox,组合框,自定义控件或另一个视图)基于它绑定到的对象的类型。如何根据绑定对象的类型动态更改DataTemplate?

我有以下MVVM框架:

FieldView是联系在一起的FieldPresenter一个实例,应为“标签”属性显示<Textblock />,和用户控件或其他信息查看值(基于类型的值),将其DataSource设置为Presenter的Value属性。目前,我没有第二部分工作。我无法弄清楚如何根据需要编写WPF模板。

视图模型:

public class FieldPresenter : Observable<object>, IFieldPresenter, INotifyPropertyChanged 
{ 
    public FieldPresenter() { } 
    public FieldPresenter(object value) 
    { 
     Value = value; 
    } 
    object IFieldPresenter.Value 
    { 
     get 
     { 
      return base.Value; 
     } 

     set 
     { 
      base.Value = value; 
      OnPropertyChanged("Value"); 
     } 
    } 
    private string _label; 
    public virtual string Label 
    { 
     get 
     { 
      return _label; 
     } 
     private set 
     { 
      _label = value; 
      OnPropertyChanged("Label"); 
     } 
    } 
} 

查看:

<UserControl x:Class="My.Views.FieldView" 
      xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
      xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
      xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
      xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
      xmlns:ViewModels="clr-namespace:My.ViewModels" 
      mc:Ignorable="d" 
      d:DesignHeight="24" d:DesignWidth="100"> 
    <UserControl.DataContext> 
     <ViewModels:FieldPresenter/> 
    </UserControl.DataContext> 
     <UserControl.Template> 
      <ControlTemplate> 
       <Grid Margin="4"> 
        <Grid.ColumnDefinitions> 
         <ColumnDefinition Width="Auto" SharedSizeGroup="Key" /> 
        </Grid.ColumnDefinitions> 
        <StackPanel Margin="0,0,0,0" HorizontalAlignment="Stretch" Width="{Binding RelativeSource={RelativeSource AncestorType=Grid}, Path=ActualWidth}"> 
         <TextBlock Text="{Binding Label}" FontWeight="Bold" Height="32" HorizontalAlignment="Stretch"/> 
         <TextBox Text="{Binding Value}" Height="Auto" HorizontalAlignment="Stretch"/> 
        </StackPanel> 
       </Grid> 
      </ControlTemplate> 
     </UserControl.Template> 
</UserControl> 

我很好奇,如果我想要做的,甚至有可能,或者,如果我可以让我的演示视图模型回归变通办法一个UserControl而不是一个对象值,并让Presenter从对象类型中解析UserControl类型,但是我不觉得我的Presenter应该实例化Controls(或者技术上是一个未绑定的视图)。我应该制作一个界面,如IViewAs<controlType> { controlType View { get; } }

在基于数据绑定对象类型的UserControl的某种模板的上述脚本中,我还将如何替换<TextBox Text="{Binding Value}" />

回答

1

几乎可以确定你想要一个ContentTemplateSelector

代码:

using System.Windows; 
using System.Windows.Controls; 

namespace WpfApplication1 
{ 
    public partial class MainWindow : Window 
    { 
     public MainWindow() 
     { 
      InitializeComponent(); 

      Primitive primitive; 

      primitive = new Sphere(); 
      // primitive = new Cube(); 
      DataContext = primitive; 
     } 
    } 

    internal abstract class Primitive 
    { 
     public abstract string Description { get; } 
    } 

    internal class Cube : Primitive 
    { 
     public override string Description 
     { 
      get { return "Cube"; } 
     } 
    } 

    internal class Sphere : Primitive 
    { 
     public override string Description 
     { 
      get { return "Sphere"; } 
     } 
    } 

    public class MyTemplateSelector : DataTemplateSelector 
    { 
     public override DataTemplate SelectTemplate(object item, DependencyObject container) 
     { 
      var frameworkElement = container as FrameworkElement; 
      if (frameworkElement != null && item != null) 
      { 
       if (item is Cube) 
       { 
        return frameworkElement.FindResource("CubeTemplate") as DataTemplate; 
       } 
       if (item is Sphere) 
       { 
        return frameworkElement.FindResource("SphereTemplate") as DataTemplate; 
       } 
      } 

      return base.SelectTemplate(item, container); 
     } 
    } 
} 

XAML:

<Window x:Class="WpfApplication1.MainWindow" 
     xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
     xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
     xmlns:local="clr-namespace:WpfApplication1" 
     xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
     x:Name="Window" 
     Title="MainWindow" 
     Width="525" 
     Height="350" 
     mc:Ignorable="d"> 

    <Grid> 
     <Grid.Resources> 
      <local:MyTemplateSelector x:Key="myTemplateSelector" /> 
      <DataTemplate x:Key="CubeTemplate" DataType="local:Cube"> 
       <Border BorderBrush="Blue" 
         BorderThickness="1" 
         CornerRadius="5" /> 
      </DataTemplate> 
      <DataTemplate x:Key="SphereTemplate" DataType="local:Sphere"> 
       <Border BorderBrush="Red" 
         BorderThickness="1" 
         CornerRadius="50" /> 
      </DataTemplate> 
     </Grid.Resources> 
     <Grid.RowDefinitions> 
      <RowDefinition Height="Auto" /> 
      <RowDefinition Height="1*" /> 
      <RowDefinition /> 
     </Grid.RowDefinitions> 
     <Label Grid.Row="0" 
       Content="{Binding Description}" 
       d:DataContext="{d:DesignInstance local:Primitive}" /> 
     <ContentControl Grid.Row="1" 
         Content="{Binding}" 
         ContentTemplateSelector="{StaticResource myTemplateSelector}" /> 

    </Grid> 
</Window> 

结果:

enter image description here

enter image description here

更多请见文档:

https://msdn.microsoft.com/en-us/library/system.windows.controls.datatemplateselector(v=vs.110).aspx

+1

的'ContentTemplateSelector'是这个答案完全没有必要的,它需要在这里的唯一原因是因为'DataType'绑定尚未宣布正常。从资源块中删除'ContentTemplateSelector',移除'ContentTemplateSelector =“{StaticResource myTemplateSelector}”',将' DataType =“local:Cube”>“'DataTemplate DataType =”{ x:Type local:Cube}“>'并将 DataType =”local:Sphere“>''更改为':local local:Sphere}>>。代码将完全相同。 –

+1

你是对的,但这样做总是会为同一类型反复地分配相同的模板,例如OP可以通过某种机制指定特定的模板名称,他可以保持对同一模板分配不同模板的控制权键入(他是否需要这样的东西):) – Aybe

+0

现在你可以保持你的VM清洁:) – Aybe

相关问题