WPF代码后台创建DataTemplate时IsMouseOver触发器无效求助
解决动态GridViewColumn中DataTemplate的IsMouseOver触发器问题
你的问题主要源于两个核心误区:
- 触发器的IsMouseOver属性指向错误:之前的触发器默认绑定到了DataContext(也就是DataTable的行对象),但DataRowView并没有IsMouseOver属性,导致触发器永远不会触发。
- DisplayMemberBinding与CellTemplate冲突:你同时设置了这两个属性,WPF会优先使用CellTemplate,但模板里的绑定没有指向正确的列值,导致内容显示异常,甚至看起来模板没生效。
下面是具体的解决步骤:
方案一:XAML定义模板框架,后台动态修改绑定(推荐)
先在XAML中定义包含触发器的模板框架,然后后台为每个列创建模板副本并修改绑定:
1. XAML中的模板资源
<UserControl.Resources> <!-- 基础模板,后台会复制并修改绑定 --> <DataTemplate x:Key="baseCellTemplate"> <!-- 必须设置Background为Transparent,否则无法接收鼠标事件 --> <Grid Background="Transparent"> <TextBlock x:Name="cellTextBlock" /> </Grid> <DataTemplate.Triggers> <!-- 指定触发器监听GridViewCell的IsMouseOver属性 --> <Trigger Property="IsMouseOver" Value="True" RelativeSource="{RelativeSource AncestorType={x:Type GridViewCell}}"> <Setter TargetName="cellTextBlock" Property="Background" Value="Black"/> <Setter TargetName="cellTextBlock" Property="Foreground" Value="White"/> </Trigger> <Trigger Property="IsMouseOver" Value="False" RelativeSource="{RelativeSource AncestorType={x:Type GridViewCell}}"> <Setter TargetName="cellTextBlock" Property="Background" Value="White"/> <Setter TargetName="cellTextBlock" Property="Foreground" Value="Black"/> </Trigger> </DataTemplate.Triggers> </DataTemplate> </UserControl.Resources> <ListView x:Name="lvMain" VirtualizingStackPanel.VirtualizationMode="Recycling"> <ListView.View> <GridView /> </ListView.View> </ListView>
2. 后台动态创建列和模板
这里使用XamlReader来克隆并修改模板,避免直接修改共享的模板实例:
private void FillListView(DataTable table) { GridView grid = (GridView)lvMain.View; grid.Columns.Clear(); // 先清空原有列 foreach (DataColumn col in table.Columns) { // 生成对应列的模板XAML,替换绑定路径 string templateXaml = $@" <DataTemplate xmlns=""http://schemas.microsoft.com/winfx/2006/xaml/presentation""> <Grid Background=""Transparent""> <TextBlock x:Name=""cellTextBlock"" Text=""{{Binding {col.ColumnName}}}""/> </Grid> <DataTemplate.Triggers> <Trigger Property=""IsMouseOver"" Value=""True"" RelativeSource=""{{RelativeSource AncestorType=GridViewCell}}""> <Setter TargetName=""cellTextBlock"" Property=""Background"" Value=""Black""/> <Setter TargetName=""cellTextBlock"" Property=""Foreground"" Value=""White""/> </Trigger> <Trigger Property=""IsMouseOver"" Value=""False"" RelativeSource=""{{RelativeSource AncestorType=GridViewCell}}""> <Setter TargetName=""cellTextBlock"" Property=""Background"" Value=""White""/> <Setter TargetName=""cellTextBlock"" Property=""Foreground"" Value=""Black""/> </Trigger> </DataTemplate.Triggers> </DataTemplate>"; // 解析XAML为DataTemplate实例 var columnTemplate = (DataTemplate)System.Windows.Markup.XamlReader.Parse(templateXaml); // 创建GridViewColumn并添加到GridView var gridColumn = new GridViewColumn { Header = col.ColumnName, CellTemplate = columnTemplate }; grid.Columns.Add(gridColumn); } // 设置数据源 lvMain.ItemsSource = ((IListSource)table).GetList(); }
方案二:完全后台创建DataTemplate(使用FrameworkElementFactory)
如果你不想依赖XAML模板,也可以完全在后台构建模板和触发器:
private void FillListView(DataTable table) { GridView grid = (GridView)lvMain.View; grid.Columns.Clear(); foreach (DataColumn col in table.Columns) { // 创建模板的可视化树 var gridFactory = new FrameworkElementFactory(typeof(Grid)); gridFactory.SetValue(Grid.BackgroundProperty, Brushes.Transparent); // 必须设置,接收鼠标事件 var textBlockFactory = new FrameworkElementFactory(typeof(TextBlock)); textBlockFactory.Name = "cellTextBlock"; // 绑定到当前列的列名 textBlockFactory.SetBinding(TextBlock.TextProperty, new Binding(col.ColumnName)); gridFactory.AppendChild(textBlockFactory); // 创建DataTemplate var columnTemplate = new DataTemplate(); columnTemplate.VisualTree = gridFactory; // 创建IsMouseOver为True的触发器 var mouseOverTrigger = new Trigger { Property = GridViewCell.IsMouseOverProperty, Value = true }; mouseOverTrigger.Setters.Add(new Setter(TextBlock.BackgroundProperty, Brushes.Black, "cellTextBlock")); mouseOverTrigger.Setters.Add(new Setter(TextBlock.ForegroundProperty, Brushes.White, "cellTextBlock")); // 创建IsMouseOver为False的触发器 var mouseNotOverTrigger = new Trigger { Property = GridViewCell.IsMouseOverProperty, Value = false }; mouseNotOverTrigger.Setters.Add(new Setter(TextBlock.BackgroundProperty, Brushes.White, "cellTextBlock")); mouseNotOverTrigger.Setters.Add(new Setter(TextBlock.ForegroundProperty, Brushes.Black, "cellTextBlock")); // 添加触发器到模板 columnTemplate.Triggers.Add(mouseOverTrigger); columnTemplate.Triggers.Add(mouseNotOverTrigger); // 创建并添加列 var gridColumn = new GridViewColumn { Header = col.ColumnName, CellTemplate = columnTemplate }; grid.Columns.Add(gridColumn); } lvMain.ItemsSource = ((IListSource)table).GetList(); }
关键注意事项
- 必须设置根元素Background为Transparent:GridViewCell默认Background为
null,不会接收鼠标事件,所以模板的根容器必须设置Background为透明,这样IsMouseOver才能正确触发。 - 避免同时设置DisplayMemberBinding和CellTemplate:这两个属性互斥,设置CellTemplate后DisplayMemberBinding会被忽略,模板内的绑定需要直接指向列名。
- 触发器要正确监听GridViewCell的IsMouseOver:通过
RelativeSource指定监听父级GridViewCell的属性,而不是DataContext的属性。
内容的提问来源于stack exchange,提问作者Arli Chokoev
相关产品推荐
相关产品推荐

