DataContext 设置后的 DataTemplate 选择器

DataTemplate Selector after DataContext Set

我有一个包含 2 列的 DataGrid。基于绑定到 ParameterDataType 的第一列,我想在第二列中加载适当的模板。

此代码的问题是在加载 DataGrid 之前,模板选择器正在执行,结果项目为空。设置 ControlTemplate 的 DataContext 后,有没有办法执行模板选择器。请帮忙。

这是我的 xaml:

<uwpControls:DataGrid Grid.Row="4"
                              ItemsSource="{x:Bind ViewModel.ServiceMethodsData,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}"
                               AutoGenerateColumns="False">

<uwpControls:DataGrid.Resources>
                <DataTemplate x:Key="StringTemplate">
                    <TextBox Width="150" Height="30" VerticalAlignment="Center"></TextBox>
                </DataTemplate>
                <DataTemplate x:Key="IntegerTemplate">
                    <controls:TextBoxNumeric Width="150" Height="30" VerticalAlignment="Center"></controls:TextBoxNumeric>
                </DataTemplate>
                <DataTemplate x:Key="BooleanTemplate">
                    <CheckBox IsChecked="False"></CheckBox>
                </DataTemplate>
                <local:MethodValueDataTemplateSelector x:Key="MethodValueTemplateSelector"
                                               StringTemplate="{StaticResource StringTemplate}"
                                               IntegerTemplate="{StaticResource IntegerTemplate}"
                                               BooleanTemplate="{StaticResource BooleanTemplate}"/>
            </uwpControls:DataGrid.Resources>

 <uwpControls:DataGrid.Columns>

                <uwpControls:DataGridTextColumn Header="First Column"  
                                                Binding="{Binding ParameterDataType, Mode=OneWay}"/>

                <uwpControls:DataGridTemplateColumn Header="Second Column">
                    <uwpControls:DataGridTemplateColumn.CellTemplate>
                        <DataTemplate>
                            <ContentControl x:Name="MethodValueContentControl"
                                            Content="{Binding Path=.}"
                                            ContentTemplateSelector="{StaticResource MethodValueTemplateSelector}"></ContentControl>
                        </DataTemplate>
                    </uwpControls:DataGridTemplateColumn.CellTemplate>
                </uwpControls:DataGridTemplateColumn>
            </uwpControls:DataGrid.Columns>
        </uwpControls:DataGrid>

这是我的数据模板选择器

public class MethodValueDataTemplateSelector : DataTemplateSelector
    {
        public DataTemplate StringTemplate { get; set; }
        public DataTemplate IntegerTemplate { get; set; }
        public DataTemplate BooleanTemplate { get; set; }

        protected override DataTemplate SelectTemplateCore(object item, DependencyObject container)
        {
            //I want to do something like if(DataContext.ParameterDataType=="Int") return IntegerTemplate etc
            return StringTemplate;
        }
    }

这是我的 ViewModel

public class ServiceUtilityMethodsViewModel
{
        private ObservableCollection<VmServiceMethodsViewDataGridModel> _serviceMethodsData;
        public ObservableCollection<VmServiceMethodsViewDataGridModel> ServiceMethodsData
        {
            get => _serviceMethodsData;
            set => Set(ref _serviceMethodsData, value);
        }

        public ServiceUtilityMethodsViewModel(INavigationService navigationService) : base(navigationService)
        {
            PopulateServiceData();
        }

        private void PopulateServiceData()
        {
            ServiceMethodsData = new ObservableCollection<VmServiceMethodsViewDataGridModel>();
            ServiceMethodsData.Add(new VmServiceMethodsViewDataGridModel()
            {
                ParameterName = "Param1",
                ParameterDataType = "String"
            });
            ServiceMethodsData.Add(new VmServiceMethodsViewDataGridModel()
            {
                ParameterName = "Param2",
                ParameterDataType = "Int"
            });
            ServiceMethodsData.Add(new VmServiceMethodsViewDataGridModel()
            {
                ParameterName = "Param3",
                ParameterDataType = "bool"
            });
        }
    }
}

这是我的模型class

public class VmServiceMethodsViewDataGridModel : BindableBaseThreadSafe
    {
        private string _parameterName;
        private string _parameterDataType;

        public string ParameterName
        {
            get => _parameterName;
            set => Set(ref _parameterName, value);
        }
        public string ParameterDataType //I want the template selector to work based on this column.
        {
            get => _parameterDataType;
            set => Set(ref _parameterDataType, value);
        }
    }

您应该将 DataTemplateSelector 直接分配给 DataGridTemplateColumn.CellTemplateSelectorDataGridTemplateColumn.CellEditingTemplateSelector

我没有检查 UWP 版本。我认为 UWP DataGridTemplateColumn 没有此模板选择器属性。在这种情况下,您可以坚持使用当前的 XAML,但不要忘记也定义 CellEditingTemplate(例如,将 TextBlock 替换为 TextBox 16=] 版本 - 最好在默认 CellTemplate 中使用 TextBlock,因为它看起来更好)。 UWP 版本中肯定存在 CellTemplateCellEditingTemplate 属性。

XAML DataGrid定义

<uwpControls:DataGrid ItemsSource="{x:Bind ViewModel.ServiceMethodsData,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}"
                      AutoGenerateColumns="False">
  <uwpControls:DataGrid.Resources>
    <DataTemplate x:Key="StringTemplate">
      <TextBox Width="150" Height="30" VerticalAlignment="Center" />
    </DataTemplate>
    <DataTemplate x:Key="IntegerTemplate">
      <controls:TextBoxNumeric Width="150" Height="30" VerticalAlignment="Center" />
    </DataTemplate>
    <DataTemplate x:Key="BooleanTemplate">
      <CheckBox IsChecked="False" />
    </DataTemplate>

    <local:MethodValueDataTemplateSelector x:Key="MethodValueTemplateSelector"
                                           StringTemplate="{StaticResource StringTemplate}"
                                           IntegerTemplate="{StaticResource IntegerTemplate}"
                                           BooleanTemplate="{StaticResource BooleanTemplate}" />
  </uwpControls:DataGrid.Resources>

  <uwpControls:DataGrid.Columns>
    <uwpControls:DataGridTextColumn Header="First Column"  
                                    Binding="{Binding ParameterDataType, Mode=OneWay}" />
    <uwpControls:DataGridTemplateColumn Header="Second Column"
                                        CellTemplateSelector="{StaticResource MethodValueTemplateSelector}"
                                        CellEditingTemplateSelector="{StaticResource MethodValueTemplateSelector}">
    </uwpControls:DataGridTemplateColumn>
  </uwpControls:DataGrid.Columns>
</uwpControls:DataGrid>

DataTemplateSelector也很简单
SelectTemplateCore 覆盖的参数是项目和项目的容器(大部分时间是 FrameWorkElementContentControl)。
该项目始终是数据模型和当前行的 DataContext。在您的情况下,该项目的类型为 VmServiceMethodsViewDataGridModel

容器是 FrameWorkElement 包装用于渲染的模型,例如ListBoxItem。在您的情况下,容器应为 DataGridRow.

类型

只需将项目参数转换为适当的类型并对其求值。

MethodValueDataTemplateSelector.cs

public class MethodValueDataTemplateSelector : DataTemplateSelector
{
  public DataTemplate StringTemplate { get; set; }
  public DataTemplate IntegerTemplate { get; set; }
  public DataTemplate BooleanTemplate { get; set; }

  protected override DataTemplate SelectTemplateCore(object item, DependencyObject container)
  {
    // Return if the control is not loaded yet and the item is therefore null    
    // or the item is of the wrong type
    if (!(item is VmServiceMethodsViewDataGridModel dataModel))
    {
      return null;
    }

    // I want to do something like: 
    // if(DataContext.ParameterDataType=="Int") return IntegerTemplate etc
    switch (dataModel.ParameterDataType)
    {
      case string value when value.Equals("Int", StringComparison.OrdinalIgnoreCase): 
        return IntegerTemplate;
      case string value when value.Equals("String", StringComparison.OrdinalIgnoreCase): 
        return StringTemplate;
      case string value when value.Equals("Bool", StringComparison.OrdinalIgnoreCase): 
        return BooleanTemplate;
      default: 
        return null;
    }
  }
}