DataTemplate Selector after DataContext Set

限于喜欢 提交于 2020-07-10 03:17:38

问题


I have a DataGrid with 2 columns. Based on the first column which is bound to ParameterDataType I want to load the appropriate template in the second column.

Problem with this code is before the DataGrid has been loaded, the template selector is executing as a result the item is null. Is there a way to execute the Template Selector after the ControlTemplate's DataContext is set. Please help.

Here is my 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>

Here is my DataTemplate selector

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

Here is my 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"
            });
        }
    }
}

Here is my Model 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);
        }
    }

回答1:


You should assign the DataTemplateSelector to DataGridTemplateColumn.CellTemplateSelector and DataGridTemplateColumn.CellEditingTemplateSelector directly.

I didn't check the UWP version. I think the UWP DataGridTemplateColumn doesn't have this template selector properties. In this case you can stick to your current XAML, but don't forget to define a CellEditingTemplate too (e.g., replace the TextBlock with a TextBox for the CellEditingTemplate version - better use a TextBlock in your default CellTemplate as it looks better). The properties CellTemplate and CellEditingTemplate exist for sure in the UWP version.

XAML DataGrid definition

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

The DataTemplateSelector is also quite simple.
The parameters of the SelectTemplateCore override are the item and the item's container (which is a FrameWorkElement and a ContentControl most of the time).
The item is always the data model and the DataContext of the current row. In your case the item is of type VmServiceMethodsViewDataGridModel.

The container is the FrameWorkElement that wraps the model for rendering e.g. ListBoxItem. In your case the container should be of type DataGridRow.

Simply cast the item parameter to the appropriate type and evaluate it.

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


来源:https://stackoverflow.com/questions/61373596/datatemplate-selector-after-datacontext-set

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!