如何禁止UWP TreeView指定根节点被拖拽及参与重排序
UWP TreeView 文件夹视图拖拽重排限制实现方案
需求规则
实现的拖拽逻辑需满足以下要求:
- 根文件夹节点不可被拖拽,位置完全固定
- 禁止任何其他项放置到根节点之前
- 仅根节点下的一级子节点(示例中的
childNode One、childNode Two、childNode Three)支持自由重排序
效果参考
- 期望效果:根节点位置固定,三个子节点可自由拖拽排序

- 需规避异常场景:根节点被拖拽挪动位置

当前实现代码
MainPage.xaml
<Page x:Class="TreeView.MainPage" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="using:TreeView" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d" Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"> <Grid> <TreeView Name="KeywordTreeView" ItemsSource="{x:Bind Nodes}" CanDragItems="True" CanReorderItems="True"> <TreeView.ItemTemplate> <DataTemplate x:DataType="local:Node"> <TreeViewItem ItemsSource="{x:Bind ChildNodes}" IsExpanded="True" AllowDrop="True" CanDrag="True" Content="{x:Bind Name}"/> </DataTemplate> </TreeView.ItemTemplate> </TreeView> </Grid> </Page>
MainPage.xaml.cs
namespace TreeView { public sealed partial class MainPage : Page { public MainPage() { this.InitializeComponent(); this.Nodes = new ObservableCollection<Node>(); Node root = new Node("Root"); Node childnodeone = new Node("ChildNode One"); Node childnodetwo = new Node("ChildNode Two"); Node childnodethree = new Node("ChildNode Three"); root.ChildNodes.Add(childnodeone); root.ChildNodes.Add(childnodetwo); root.ChildNodes.Add(childnodethree); this.Nodes.Add(root); } public ObservableCollection<Node> Nodes { get; } } public class Node { public Node(string name) { this.Name = name; } public string Name { get; } public ObservableCollection<Node> ChildNodes { get; } = new ObservableCollection<Node>(); } }
修改实现方案
现有代码的问题是给所有TreeViewItem统一硬编码开启了CanDrag="True",同时没有对顶层集合的拖拽放置做校验,才会出现根节点被拖动的异常。按以下步骤修改即可:
- 给节点类增加根节点标记,区分根节点和普通子节点
- 禁用根节点的拖拽能力,仅子节点允许拖拽
- 拦截顶层集合的非法放置操作,禁止任何项放到根节点前后位置
第一步:扩展Node类
给Node类增加IsRoot属性,标记节点是否为固定根节点:
public class Node { public Node(string name, bool isRoot = false) { this.Name = name; this.IsRoot = isRoot; } public string Name { get; } public bool IsRoot { get; } public ObservableCollection<Node> ChildNodes { get; } = new ObservableCollection<Node>(); }
创建根节点时传入根节点标记:
Node root = new Node("Root", isRoot: true);
第二步:添加布尔取反转换器
实现通用布尔取反转换器,用于XAML中绑定控制节点的CanDrag属性:
public class InvertBoolConverter : IValueConverter { public object Convert(object value, Type targetType, object parameter, string language) { return value is bool flag ? !flag : false; } public object ConvertBack(object value, Type targetType, object parameter, string language) { return value is bool flag ? !flag : false; } }
在XAML页面资源中注册转换器:
<Page.Resources> <local:InvertBoolConverter x:Key="InvertBoolConverter"/> </Page.Resources>
第三步:修改XAML配置
移除TreeViewItem上硬编码的CanDrag="True",改为通过绑定自动控制:根节点CanDrag为False不可拖动,子节点CanDrag为True可拖动。同时给TreeView添加DragOver事件用于拦截非法放置:
<TreeView Name="KeywordTreeView" ItemsSource="{x:Bind Nodes}" CanDragItems="True" CanReorderItems="True" AllowDrop="True" DragOver="KeywordTreeView_DragOver"> <TreeView.ItemTemplate> <DataTemplate x:DataType="local:Node"> <TreeViewItem ItemsSource="{x:Bind ChildNodes}" IsExpanded="True" AllowDrop="True" CanDrag="{x:Bind IsRoot, Converter={StaticResource InvertBoolConverter}}" Content="{x:Bind Name}"/> </DataTemplate> </TreeView.ItemTemplate> </TreeView>
第四步:添加拖拽拦截逻辑
在后台代码中实现DragOver事件处理,禁止拖拽项放置到根节点的前后位置:
private void KeywordTreeView_DragOver(object sender, DragEventArgs e) { if (e.OriginalSource is not DependencyObject element) return; var targetItem = element.FindAscendant<TreeViewItem>(); if (targetItem?.DataContext is not Node targetNode) return; // 仅当拖拽目标是根节点,且插入位置不是根节点内部时,禁止放置 if (targetNode.IsRoot) { var insertPos = targetItem.GetInsertPosition(e.GetPosition(targetItem)); if (insertPos != ItemInsertPosition.Inside) { e.AcceptedOperation = DataPackageOperation.None; e.Handled = true; } } }
修改完成后,根节点完全无法被选中拖拽,任何拖拽项都不能放到根节点的前后位置,只有根节点内部的子节点可以自由拖拽重排,完全符合需求。
内容的提问来源于stack exchange,提问作者Badhan Sen
相关产品推荐
相关产品推荐

