-->

How to use DataTemplateSelector with ContentContro

2019-02-21 03:03发布

问题:

I want to create a simple window that would display different controls (SpinEdit or TextEdit) based on the view-model that is selected.

I have the code and logic behind it done already, what is left is displaying the control (SpinEdit or TextEdit) itself.

XAML:

   <dx:DXWindow.Resources>

      <DataTemplate x:Key="DataTemplate_Value">
         <dxe:SpinEdit Height="23" MinWidth="200" Width="Auto"
                       Text="{Binding Path=Value, Mode=TwoWay}"
                       Mask="{Binding Mask, Mode=OneWay}" 
                       MaxLength="{Binding Path=InputLength}" />

      </DataTemplate>

      <DataTemplate x:Key="DataTemplate_Text">
         <dxe:TextEdit Height="23" MinWidth="200" Width="Auto"
                       Text="{Binding Path=Value, Mode=TwoWay}"
                       MaskType="RegEx" Mask="{Binding Mask, Mode=OneWay}"
                       MaxLength="{Binding Path=InputLength}"/>
      </DataTemplate>

      <local:PropertyDataTemplateSelector  x:Key="templateSelector"
         DataTemplate_Value="{StaticResource DataTemplate_Value}"
         DataTemplate_Text="{StaticResource DataTemplate_Text}" />

   </dx:DXWindow.Resources>



  <Grid>
      <Grid.RowDefinitions>
         <RowDefinition Height="Auto" />
         <RowDefinition Height="Auto" />
      </Grid.RowDefinitions>

      <StackPanel Grid.Row="0" >
         <Label x:Uid="Label" MinHeight="24" MinWidth="60" Content="Value" />
         <ContentControl ContentTemplateSelector="{StaticResource templateSelector}" />
      </StackPanel>

      <StackPanel Grid.Row="1" x:Uid="OKCancel_Buttons" Orientation="Horizontal" HorizontalAlignment="Right" VerticalAlignment="Bottom">
         <Button Height="23" x:Name="OK_Button" Click="OK_Click" Content="OK" IsDefault="True" HorizontalAlignment="Right" MinWidth="95" />
         <Button Height="23" x:Name="Cancel_Button" Click="Cancel_Click" Content="Cancel" HorizontalAlignment="Right" MinWidth="95" />
      </StackPanel>

Where in the <ContentControl> I want to select which control will be displayed (SpinEdit for numbers and TextEdit for names/letters)

C#:

   public class PropertyDataTemplateSelector : DataTemplateSelector
   {
      public DataTemplate DataTemplate_Value { get; set; }
      public DataTemplate DataTemplate_Text { get; set; }

      public override DataTemplate SelectTemplate(object item, DependencyObject container)
      {
         var selector = item as TInputBaseVM;

         if(selector is TInputValueVM)
            return DataTemplate_Value;
         return DataTemplate_Text;
      }
   }

Where I want to return a specific DataTemplate based on the view-model that is created in the c++/cli code.

C++/cli:

  TInputValueVM ^oExchange_Value;
  TInputTextVM ^oExchange_Text;

  int inputFormat = A_Attributes.GetInputFormat();

  if(inputFormat)
     oExchange_Text = gcnew TInputTextVM(gcnew System::String(A_Attributes.GetTitle()), gcnew System::String(A_Attributes.GetMask()),
        A_Attributes.GetInputLength(), gcnew System::String(A_Attributes.GetInitialText()));
  else
     oExchange_Value = gcnew TInputValueVM(gcnew System::String(A_Attributes.GetTitle()), gcnew System::String(A_Attributes.GetMask()),
        A_Attributes.GetInputLength(), A_Attributes.GetInitialValue());

  Dialogs::TSignalNumberPositionDialog^ dialog = gcnew Dialogs::TSignalNumberPositionDialog();

  if(inputFormat)
     dialog->DataContext = oExchange_Text;
  else
     dialog->DataContext = oExchange_Value;

  dialog->ShowDialog();

The point is, the item value in the overriden selector function always has the null value and I have no idea how to bind it in XAML since all the examples I've managed to find so far are ListBoxes etc. There's no example on how to display different controls based on the view-model.

EDIT:

As suggested, I added Content property in the ContentControl and passed it an argument which is now the 'item' argument in the selector. Works just fine!

回答1:

You do not need a DataTemplateSelector. WPF provides a mechanism that automatically selects a DataTemplate for the ContentTemplate of a ContentControl according to the type of a Content.

As explained in Data​Template.​Data​Type:

When you set this property to the data type without specifying an x:Key, the DataTemplate gets applied automatically to data objects of that type.

So drop the x:Key value and your DataTemplateSelector, set DataType

<dx:DXWindow.Resources>
    <DataTemplate DataType="{x:Type local:TInputValueVM}">
        <dxe:SpinEdit Height="23" MinWidth="200" Width="Auto"
                      Text="{Binding Path=Value, Mode=TwoWay}"
                      Mask="{Binding Mask, Mode=OneWay}" 
                      MaxLength="{Binding Path=InputLength}" />
    </DataTemplate>
    <DataTemplate DataType="{x:Type local:TInputTextVM}">
        <dxe:TextEdit Height="23" MinWidth="200" Width="Auto"
                      Text="{Binding Path=Value, Mode=TwoWay}"
                      MaskType="RegEx" Mask="{Binding Mask, Mode=OneWay}"
                      MaxLength="{Binding Path=InputLength}"/>
    </DataTemplate>
</dx:DXWindow.Resources>

and bind the ContentControl's Content to a property that returns either a TInputValueVM or a TInputTextVM:

<ContentControl Content="{Binding InputVM}" />

The appropriate DataTemplate will now be selected automatically.



回答2:

You have to add some value in the Content property of the ContentControl. That value will be passed to the SelectTemplate as the object item. You probably should bind to it some property in your ViewModel to be able to change that from there.