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

WPF TreeView实现鼠标悬停节点时添加项功能(附XAML代码)

实现TreeView节点悬停显示添加按钮(类似OneNote)

我帮你完善了TreeView的实现,刚好能满足你要的类似OneNote的悬停显示添加按钮功能,核心是通过自定义TreeViewItem的模板,配合触发器来控制按钮的显示隐藏,同时适配你的递归类绑定需求。

完整XAML实现

你可以直接把这段代码替换/补充到你的现有XAML中,关键是添加了TreeView.ItemContainerStyle来定义节点的模板和悬停逻辑:

<TreeView Grid.Row="1" BorderBrush="{x:Null}" ItemsSource="{Binding Law.RootGroup.Groups}">
    <i:Interaction.Behaviors>
        <functions:BindableSelectedItemBehavior SelectedItem="{Binding SelectedGroup, Mode=TwoWay}" />
    </i:Interaction.Behaviors>
    
    <!-- 自定义TreeViewItem样式,实现悬停显示添加按钮 -->
    <TreeView.ItemContainerStyle>
        <Style TargetType="TreeViewItem">
            <Setter Property="Padding" Value="4,2"/>
            <Setter Property="Template">
                <Setter.Value>
                    <ControlTemplate TargetType="TreeViewItem">
                        <StackPanel Orientation="Horizontal" VerticalAlignment="Center">
                            <!-- 节点展开/折叠按钮(保留默认逻辑) -->
                            <ToggleButton x:Name="Expander" 
                                          IsChecked="{Binding IsExpanded, RelativeSource={RelativeSource TemplatedParent}}"
                                          Visibility="{Binding HasItems, RelativeSource={RelativeSource TemplatedParent}, Converter={StaticResource BooleanToVisibilityConverter}}"
                                          Margin="0,0,4,0">
                                <ToggleButton.Style>
                                    <Style TargetType="ToggleButton">
                                        <Setter Property="Width" Value="16"/>
                                        <Setter Property="Height" Value="16"/>
                                        <Setter Property="Background" Value="Transparent"/>
                                        <Setter Property="BorderThickness" Value="0"/>
                                        <Setter Property="Template">
                                            <Setter.Value>
                                                <ControlTemplate TargetType="ToggleButton">
                                                    <Path x:Name="ExpandPath" 
                                                          Data="M0,0 L0,6 L6,0 Z" 
                                                          Fill="#888" 
                                                          HorizontalAlignment="Center" 
                                                          VerticalAlignment="Center"/>
                                                    <ControlTemplate.Triggers>
                                                        <Trigger Property="IsChecked" Value="True">
                                                            <Setter TargetName="ExpandPath" Property="Data" Value="M0,0 L6,0 L3,6 Z"/>
                                                        </Trigger>
                                                    </ControlTemplate.Triggers>
                                                </ControlTemplate>
                                            </Setter.Value>
                                        </Setter>
                                    </Style>
                                </ToggleButton.Style>
                            </ToggleButton>
                            
                            <!-- 节点内容显示 -->
                            <ContentPresenter x:Name="PART_Header" 
                                              ContentSource="Header"
                                              HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"/>
                            
                            <!-- 悬停显示的添加按钮 -->
                            <Button x:Name="AddButton"
                                    Content="+"
                                    Width="20" Height="20"
                                    Margin="8,0,0,0"
                                    Visibility="Collapsed"
                                    Style="{StaticResource CircleButtonStyle}"
                                    Command="{Binding DataContext.AddNewGroupCommand, RelativeSource={RelativeSource AncestorType=TreeView}}"
                                    CommandParameter="{Binding}"/>
                        </StackPanel>
                        
                        <ControlTemplate.Triggers>
                            <!-- 鼠标悬停节点时显示添加按钮 -->
                            <Trigger Property="IsMouseOver" Value="True">
                                <Setter TargetName="AddButton" Property="Visibility" Value="Visible"/>
                            </Trigger>
                            <!-- 可选:节点选中时也保持按钮显示 -->
                            <Trigger Property="IsSelected" Value="True">
                                <Setter TargetName="AddButton" Property="Visibility" Value="Visible"/>
                            </Trigger>
                        </ControlTemplate.Triggers>
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
        </Style>
    </TreeView.ItemContainerStyle>
    
    <!-- 递归类的层级数据模板 -->
    <TreeView.ItemTemplate>
        <HierarchicalDataTemplate ItemsSource="{Binding Groups}">
            <TextBlock Text="{Binding Name}" Padding="2,0"/>
        </HierarchicalDataTemplate>
    </TreeView.ItemTemplate>
</TreeView>

配套ViewModel与递归类实现

你的递归类(比如Group)需要包含子项集合,同时ViewModel要提供添加子项的命令:

递归类示例

public class Group : INotifyPropertyChanged
{
    private string _name;
    public string Name
    {
        get => _name;
        set { _name = value; OnPropertyChanged(); }
    }

    private ObservableCollection<Group> _groups;
    public ObservableCollection<Group> Groups
    {
        get => _groups;
        set { _groups = value; OnPropertyChanged(); }
    }

    public Group()
    {
        Groups = new ObservableCollection<Group>();
    }

    public event PropertyChangedEventHandler PropertyChanged;
    protected void OnPropertyChanged([CallerMemberName] string propertyName = null)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }
}

ViewModel中的命令实现

public class YourViewModel : INotifyPropertyChanged
{
    public Law Law { get; set; } // 你的Law对象,包含RootGroup
    public Group SelectedGroup { get; set; }
    public ICommand AddNewGroupCommand { get; }

    public YourViewModel()
    {
        // 初始化数据(示例)
        Law = new Law { RootGroup = new Group { Name = "Root" } };
        AddNewGroupCommand = new RelayCommand<Group>(AddNewGroup);
    }

    private void AddNewGroup(Group parentGroup)
    {
        if (parentGroup == null) return;
        // 添加新的子分组
        parentGroup.Groups.Add(new Group { Name = $"New Group {parentGroup.Groups.Count + 1}" });
    }

    public event PropertyChangedEventHandler PropertyChanged;
    protected void OnPropertyChanged([CallerMemberName] string propertyName = null)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }
}

可选:自定义圆形按钮样式

如果想让添加按钮更贴近OneNote的风格,可以添加这个样式到资源字典中:

<Style x:Key="CircleButtonStyle" TargetType="Button">
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="Button">
                <Ellipse x:Name="ellipse" Fill="{TemplateBinding Background}" Stroke="{TemplateBinding BorderBrush}" StrokeThickness="1">
                    <VisualStateManager.VisualStateGroups>
                        <VisualStateGroup x:Name="CommonStates">
                            <VisualState x:Name="Normal">
                                <Storyboard>
                                    <ColorAnimation To="#E0E0E0" Storyboard.TargetProperty="(Ellipse.Fill).(SolidColorBrush.Color)" Storyboard.TargetName="ellipse" Duration="0:0:0.1"/>
                                </Storyboard>
                            </VisualState>
                            <VisualState x:Name="MouseOver">
                                <Storyboard>
                                    <ColorAnimation To="#C0C0C0" Storyboard.TargetProperty="(Ellipse.Fill).(SolidColorBrush.Color)" Storyboard.TargetName="ellipse" Duration="0:0:0.1"/>
                                </Storyboard>
                            </VisualState>
                        </VisualStateGroup>
                    </VisualStateManager.VisualStateGroups>
                </Ellipse>
                <ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center"/>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
    <Setter Property="Background" Value="#F0F0F0"/>
    <Setter Property="BorderBrush" Value="#CCC"/>
    <Setter Property="Foreground" Value="#333"/>
</Style>

关键细节说明

  • 按钮命令绑定:因为TreeViewItem的DataContext是单个Group对象,所以通过RelativeSource找到TreeView的DataContext(也就是ViewModel)来绑定命令,同时把当前Group作为参数传递,方便添加子项。
  • 悬停触发器:通过IsMouseOver触发器控制按钮显示,你也可以根据需求调整逻辑,比如只有选中节点悬停才显示。
  • 层级模板:HierarchicalDataTemplate自动绑定递归类的Groups集合,实现无限层级的节点展示。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.25 08:30:47