WPF DataGrid用DataTemplate/DataTriggers数据不显示:TestField不在集合上下文
解决方法
核心思路
要解决这个问题,关键是在DataGrid的列/模板中同时获取两个上下文:集合项(TestCollection里的元素)和ViewModel的TestField。以下是几种实用的实现方式:
方法1:用DataTrigger结合RelativeSource切换模板
直接在XAML里通过RelativeSource找到DataGrid的DataContext(也就是ViewModel),再根据TestField的值动态切换CellTemplate:
<DataGrid ItemsSource="{Binding TestCollection}" AutoGenerateColumns="False"> <DataGrid.Columns> <DataGridTemplateColumn Header="动态列"> <DataGridTemplateColumn.CellTemplate> <DataTemplate> <!-- ContentControl的DataContext是集合项,同时通过RelativeSource绑定ViewModel的TestField --> <ContentControl> <ContentControl.Style> <Style TargetType="ContentControl"> <!-- 默认模板 --> <Setter Property="ContentTemplate"> <Setter.Value> <DataTemplate> <TextBlock Text="{Binding 默认属性}" /> </DataTemplate> </Setter.Value> </Setter> <!-- TestField为ABCD时的模板 --> <Style.Triggers> <DataTrigger Binding="{Binding DataContext.TestField, RelativeSource={RelativeSource AncestorType=DataGrid}}" Value="ABCD"> <Setter Property="ContentTemplate"> <Setter.Value> <DataTemplate> <!-- 绑定集合项的属性,比如Name --> <TextBlock Text="{Binding Name}" Foreground="Blue" /> </DataTemplate> </Setter.Value> </Setter> </DataTrigger> <!-- TestField为EFGH时的模板 --> <DataTrigger Binding="{Binding DataContext.TestField, RelativeSource={RelativeSource AncestorType=DataGrid}}" Value="EFGH"> <Setter Property="ContentTemplate"> <Setter.Value> <DataTemplate> <!-- 绑定集合项的属性,比如Id --> <Button Content="{Binding Id}" Width="80" /> </DataTemplate> </Setter.Value> </Setter> </DataTrigger> </Style.Triggers> </Style> </ContentControl.Style> </ContentControl> </DataTemplate> </DataGridTemplateColumn.CellTemplate> </DataGridTemplateColumn> </DataGrid.Columns> </DataGrid>
方法2:自定义DataTemplateSelector切换模板
如果模板逻辑复杂,写在XAML里太臃肿,可以自定义模板选择器,在代码里获取ViewModel的TestField来选择模板:
1. 编写模板选择器
using System.Windows; using System.Windows.Controls; using System.Windows.Media; public class TestTemplateSelector : DataTemplateSelector { // 可在XAML中绑定的模板属性 public DataTemplate AbcdTemplate { get; set; } public DataTemplate EfghTemplate { get; set; } public DataTemplate DefaultTemplate { get; set; } public override DataTemplate SelectTemplate(object item, DependencyObject container) { // 从VisualTree中找到父级DataGrid,获取ViewModel var dataGrid = FindAncestor<DataGrid>(container); if (dataGrid?.DataContext is ViewModel vm) { return vm.TestField switch { "ABCD" => AbcdTemplate, "EFGH" => EfghTemplate, _ => DefaultTemplate }; } return DefaultTemplate; } // 递归查找父级控件 private T FindAncestor<T>(DependencyObject obj) where T : DependencyObject { while (obj != null) { if (obj is T ancestor) return ancestor; obj = VisualTreeHelper.GetParent(obj); } return null; } }
2. 在XAML中使用
<Window.Resources> <!-- 定义不同的模板 --> <DataTemplate x:Key="AbcdTemplate"> <TextBlock Text="{Binding Name}" Foreground="Blue" /> </DataTemplate> <DataTemplate x:Key="EfghTemplate"> <Button Content="{Binding Id}" Width="80" /> </DataTemplate> <DataTemplate x:Key="DefaultTemplate"> <TextBlock Text="{Binding Description}" /> </DataTemplate> <!-- 实例化自定义选择器 --> <local:TestTemplateSelector x:Key="TestSelector" AbcdTemplate="{StaticResource AbcdTemplate}" EfghTemplate="{StaticResource EfghTemplate}" DefaultTemplate="{StaticResource DefaultTemplate}" /> </Window.Resources> <DataGrid ItemsSource="{Binding TestCollection}" AutoGenerateColumns="False"> <DataGrid.Columns> <DataGridTemplateColumn Header="动态列" CellTemplateSelector="{StaticResource TestSelector}" /> </DataGrid.Columns> </DataGrid>
方法3:ViewModel动态生成列集合
如果需要根据TestField显示完全不同的列(比如不同数量、不同类型的列),可以在ViewModel中生成列集合,直接绑定到DataGrid的Columns:
1. 修改ViewModel
using System.Collections.ObjectModel; using System.Windows.Controls; using System.ComponentModel; public class ViewModel : INotifyPropertyChanged { private string _testField; public string TestField { get => _testField; set { _testField = value; OnPropertyChanged(); // TestField变化时重新生成列 GenerateColumns(); } } public ObservableCollection<object> TestCollection { get; set; } public ObservableCollection<DataGridColumn> Columns { get; set; } public ViewModel() { TestField = "ABCD"; TestCollection = new ObservableCollection<object>(); Columns = new ObservableCollection<DataGridColumn>(); GenerateColumns(); } private void GenerateColumns() { Columns.Clear(); var app = System.Windows.Application.Current; if (TestField == "ABCD") { Columns.Add(new DataGridTemplateColumn { Header = "名称", CellTemplate = app.FindResource("AbcdTemplate") as DataTemplate }); // 可以添加更多列 Columns.Add(new DataGridTextColumn { Header = "编号", Binding = new System.Windows.Data.Binding("Id") }); } else if (TestField == "EFGH") { Columns.Add(new DataGridTemplateColumn { Header = "操作按钮", CellTemplate = app.FindResource("EfghTemplate") as DataTemplate }); } // 其他TestField值的列配置... } public event PropertyChangedEventHandler PropertyChanged; private void OnPropertyChanged(string propertyName = null) { PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); } }
2. XAML绑定
<DataGrid ItemsSource="{Binding TestCollection}" Columns="{Binding Columns}" CanUserAddColumns="False" CanUserDeleteColumns="False" AutoGenerateColumns="False" />
内容的提问来源于stack exchange,提问作者Павел Закриев
相关产品推荐
相关产品推荐

