You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

WPF代码后台创建DataTemplate时IsMouseOver触发器无效求助

解决动态GridViewColumn中DataTemplate的IsMouseOver触发器问题

你的问题主要源于两个核心误区:

  1. 触发器的IsMouseOver属性指向错误:之前的触发器默认绑定到了DataContext(也就是DataTable的行对象),但DataRowView并没有IsMouseOver属性,导致触发器永远不会触发。
  2. 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();
}

关键注意事项

  1. 必须设置根元素Background为Transparent:GridViewCell默认Background为null,不会接收鼠标事件,所以模板的根容器必须设置Background为透明,这样IsMouseOver才能正确触发。
  2. 避免同时设置DisplayMemberBinding和CellTemplate:这两个属性互斥,设置CellTemplate后DisplayMemberBinding会被忽略,模板内的绑定需要直接指向列名。
  3. 触发器要正确监听GridViewCell的IsMouseOver:通过RelativeSource指定监听父级GridViewCell的属性,而不是DataContext的属性。

内容的提问来源于stack exchange,提问作者Arli Chokoev

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.06 22:02:42