使用DataTemplateSelector

时间:2018-11-13 07:28:27

标签: c# wpf c++-cli

我正在尝试编写一个简单的对话框,该对话框将接受SpinEdit中的值或TextEdit中的文本。我正在使用多个VM,并选择了一个选择器,该选择器应基于c ++ / cli文件中的逻辑查看适当的控件。

XAML:

       xmlns:local="clr-namespace:asd"
       Title="{Binding Path=Title, Mode=OneTime}"

       <dx:DXWindow.Resources>

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

          </DataTemplate>

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

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

       </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 Content="{Binding Path=Whoami}" 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>

       </Grid>

在c#中,我有一个基本VM和两个对其进行扩展的VMS,一个用于值,一个用于文本。其他属性保持不变。

C#

namespace asd
{
   public class TInputBaseVM : ViewModelBase
   {
      private string m_sTitle;
      private string m_sMask;
      private int m_nInputLenght;
      private string m_sWhoami;

      public TInputBaseVM(string A_sTitle, string A_sMask, int A_nInputLength)
      {
         m_sTitle = A_sTitle;
         m_sMask = A_sMask;
         m_nInputLenght = A_nInputLength;
      }

      protected string Title
      {
         get { return m_sTitle; }
         set { SetProperty(ref m_sTitle, value, () => Title); }
      }
      protected string Mask
      {
         get { return m_sMask; }
         set { SetProperty(ref m_sMask, value, () => Mask); }
      }

      protected int InputLength
      {
         get { return m_nInputLenght; }
         set { SetProperty(ref m_nInputLenght, value, () => InputLength); }
      }

      protected string Whoami
      {
         get { return m_sWhoami; }
         set { SetProperty(ref m_sWhoami, value, () => Whoami); }
      }
   }

   public class TInputValueVM : TInputBaseVM
   {
      public TInputValueVM(string A_sTitle, string A_sMask, int A_nInputLength, double A_nValue) : base(A_sTitle, A_sMask, A_nInputLength)
      {
         Value = A_nValue;
         Whoami = "Value";
      }

      private double m_nValue;
      public double Value
      {
         get { return m_nValue; }
         set { SetProperty(ref m_nValue, value, () => Value); }
      }
   }

   public class TInputTextVM : TInputBaseVM
   {
      public TInputTextVM(string A_sTitle, string A_sMask, int A_nInputLength, string A_sValue) : base(A_sTitle, A_sMask, A_nInputLength)
      {
         Value = A_sValue;
         Whoami = "Text";
      }

      private string m_sValue;
      public string Value
      {
         get { return m_sValue; }
         set { SetProperty(ref m_sValue, value, () => Value); }
      }
   }

   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 string;

         if(selector == "Value")
            return DataTemplate_Value;
         return DataTemplate_Text;
      }
   }

}

在c ++ / cli中,我创建了一个正确VM的对象,我希望WPF自动将视图更新为spinedit或textedit,但是我不确定如何正确地绑定C#的属性。如果我在ContentControl的Content属性中明确键入“ Value”,则它将显示spinEdit,但我不知道如何绑定它,因此它将自动采用正确的属性。

编辑:我正在添加c ++ / cli代码以显示如何选择不同的虚拟机

C ++ / cli:

bool TSignalNumberPositionDialogCLR::StartDialog(TSignalNumberPositionSupport& A_Attributes, HWND A_hwndParent, LPTSTR String)
{
   try
   {
      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();

      if(dialog->DialogResult)
      {
         CString nValue;

         if(inputFormat)
            nValue = oExchange_Text->Value;
         else
            nValue = ((Decimal)oExchange_Value->Value).ToString("F2", CultureInfo::InvariantCulture);

         A_Attributes.UpdateValue(nValue, String, A_Attributes.GetInputLength());
         return true;
      }

      return false;
   }
   catch(Exception^ e)
   {
      e;
   }
}

基于“ inputFormat”变量,我想在对话框中显示不同的控件。

编辑:基于@Clemens注释,我摆脱了选择器部分和DataTemplates中的x:Key属性。我将Content属性的opf内容更改为Content =“ {Binding}”,它以某种方式起作用。创建虚拟机后,它会选择正确的虚拟机。

1 个答案:

答案 0 :(得分:1)

根据您的评论。让我改善答案。当您面临虚拟机选择问题时。因此plesae专注于如何将VM分配给datatemplate。尽管它是通过非常基本的方式完成的,但是如果您使用的是MVVM软件包,则可以进行处理。 我创建了2个数据模板和2个虚拟机,每个虚拟机都绑定到datatemplate。为了进行验证,我有一个组合框,它将根据所选值选择数据模板。

这是示例VM

public class VM : System.ComponentModel.INotifyPropertyChanged
{
    private string title;
    private SolidColorBrush background;

    public string Title { get => title; set { title = value; RaisePropertyChanged(); } }
    public SolidColorBrush Background { get => background; set { background = value; RaisePropertyChanged(); } }

    public event PropertyChangedEventHandler PropertyChanged;
    private void RaisePropertyChanged([System.Runtime.CompilerServices.CallerMemberName] string name = "")
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
    }
}

public class VM1: VM
{
    public VM1()
    {
        Title = "This is VM1";
        Background = Brushes.Yellow;
    }
}

public class VM2: VM
{
    public VM2()
    {
        Title = "This is VM2";
        Background = Brushes.Orange;
    }
}

现在检查资源

<local:VM1 x:Key="VM1"/>
    <local:VM2 x:Key="VM2"/>

    <DataTemplate x:Key="DT1">
        <Grid DataContext="{StaticResource VM1}">
            <TextBlock Text="{Binding Title}" Background="{Binding Background}"/>
        </Grid>
    </DataTemplate>
    <DataTemplate x:Key="DT2">
        <Grid DataContext="{StaticResource VM2}">
            <TextBlock Text="{Binding Title}" Background="{Binding Background}"/>
        </Grid>
    </DataTemplate>

    <Style TargetType="ContentControl" x:Key="contentStyle">
        <Style.Triggers>
            <DataTrigger Binding="{Binding ElementName=cmbo, Path=SelectedValue}" Value="Template1">
                <Setter Property="ContentTemplate" Value="{StaticResource DT1}" />
            </DataTrigger>
            <DataTrigger Binding="{Binding ElementName=cmbo, Path=SelectedValue}" Value="Template2">
                <Setter Property="ContentTemplate" Value="{StaticResource DT2}" />
            </DataTrigger>
        </Style.Triggers>
    </Style>

最后我有组合框和内容控件只是为了验证

    <ComboBox Name="cmbo"/>

        <ContentControl  Style="{StaticResource contentStyle}"/>

其中cmbo.ItemsSource =新列表{“ Template1”,“ Template2”};

希望你明白了