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