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.CellTemplateSelector
和 DataGridTemplateColumn.CellEditingTemplateSelector
。
我没有检查 UWP 版本。我认为 UWP DataGridTemplateColumn
没有此模板选择器属性。在这种情况下,您可以坚持使用当前的 XAML,但不要忘记也定义 CellEditingTemplate
(例如,将 TextBlock
替换为 TextBox
16=] 版本 - 最好在默认 CellTemplate
中使用 TextBlock
,因为它看起来更好)。 UWP 版本中肯定存在 CellTemplate
和 CellEditingTemplate
属性。
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
覆盖的参数是项目和项目的容器(大部分时间是 FrameWorkElement
和 ContentControl
)。
该项目始终是数据模型和当前行的 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;
}
}
}
我有一个包含 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.CellTemplateSelector
和 DataGridTemplateColumn.CellEditingTemplateSelector
。
我没有检查 UWP 版本。我认为 UWP DataGridTemplateColumn
没有此模板选择器属性。在这种情况下,您可以坚持使用当前的 XAML,但不要忘记也定义 CellEditingTemplate
(例如,将 TextBlock
替换为 TextBox
16=] 版本 - 最好在默认 CellTemplate
中使用 TextBlock
,因为它看起来更好)。 UWP 版本中肯定存在 CellTemplate
和 CellEditingTemplate
属性。
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
覆盖的参数是项目和项目的容器(大部分时间是 FrameWorkElement
和 ContentControl
)。
该项目始终是数据模型和当前行的 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;
}
}
}