WPF TreeView将更改还原到选定项

izj3ouym  于 12个月前  发布在  其他
关注(0)|答案(1)|浏览(104)

我试图实现一个带有TextBox的TreeView,它使用路径获取/设置TreeView的SelectedItem。
x1c 0d1x的数据
当我在一个分支中输入一个节点的路径时,它每次都能工作,但是当我试图输入一个已经被选择的分支或节点的路径时,TreeView立即重新选择前一个项目,似乎没有任何原因。
我已经尝试了我在MSDN & SO上能找到的所有可能的解决方案,但没有任何效果!这是我尝试的第三个项目 (使用.NET Core 6),每次都从头重写整个项目,看看我是否遗漏了什么。我的示例项目中有4个文件,但对于SO来说仍然很长,所以here's the link to full solution
我试过手动聚焦/取消聚焦TreeViewItem,因为它们是(联合国)选择在类似的SO职位,但没有任何效果。
为了弄清楚发生了什么,我在MainWindowVM.TreeView_SelectedItemChanged中添加了断点& TreeNode.IsSelected设置器,用于写入输出日志。
这是我在TreeView中点击项目时看到的:

"Root/Node0".IsSelected = true
SelectedItemChanged: null => "Root/Node0"   {System.Windows.Controls.TreeView Items.Count:4}
//                   ^^^^    ^^^^^^^^^^^^   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
//              prev. path     new path        e.OriginalSource

//   clicking another item:
"Root/Node0".IsSelected = false
"Root/Node1".IsSelected = true
SelectedItemChanged: "Root/Node0" => "Root/Node1"   {System.Windows.Controls.TreeView Items.Count:4}

字符串
一切都如预期的那样工作,日志显示的正是您期望看到的内容。前一项 (如果有) 被取消选择,新项被选择,SelectedItemChanged事件被触发。
这是当我使用TextBox设置所选项目时发生的情况:

// Continuing from the previous log, "Root/Node1" is selected & expanded.
// I then append "/Node2" to the path. It's visible but hasn't been selected before:  
"Root/Node1/Node2".IsSelected = true
"Root/Node1".IsSelected = false
SelectedItemChanged: "Root/Node1" => "Root/Node1/Node2"   {System.Windows.Controls.TreeView Items.Count:4}
// Success. Nothing abnormal occurred.
// And then I try to remove "/Node2" from the path...
"Root/Node1".IsSelected = true
"Root/Node1/Node2".IsSelected = false
"Root/Node1".IsSelected = false
"Root/Node1/Node2".IsSelected = true
SelectedItemChanged: "Root/Node1" => "Root/Node1/Node2"   {System.Windows.Controls.TreeView Items.Count:4}
// ...and it gets instantly reverted.


树视图只是恢复选择更改,然后触发SelectedItemChanged事件。当我尝试选择一个已经可见的子节点时,也会发生同样的事情:

// Again continuing from the previous log, I click on "Node1" with the mouse
"Root/Node1/Node2".IsSelected = false
"Root/Node1".IsSelected = true
SelectedItemChanged: "Root/Node1/Node2" => "Root/Node1"   {System.Windows.Controls.TreeView Items.Count:4}
// And again, nothing abnormal occurs.
// Then I try appending "/Node2" to the path:
"Root/Node1/Node2".IsSelected = true
"Root/Node1".IsSelected = false
"Root/Node1/Node2".IsSelected = false
"Root/Node1".IsSelected = true
SelectedItemChanged: "Root/Node1/Node2" => "Root/Node1"   {System.Windows.Controls.TreeView Items.Count:4}
// and it happens again!


我怀疑这与已经存在的控件有关,因为TreeView会根据需要创建子控件,但我不明白这有什么关系。我不明白为什么会发生这种情况或导致这种情况的原因,我正在失去理智试图弄清楚这一点。任何帮助都将不胜感激!
要使用提供的解决方案文件重现问题,请执行以下操作:
1.在顶部的文本框中键入Root/Node0/Node1/Node2并按Tab键。

  • (将选择指定的节点)*

1.将文本更改为Root/Node0并按Tab键。

  • (所选项目将立即更改回来)*

1.将文本更改为Root/Node1/Node1/Node2并按Tab键。

  • (将选择指定的节点)*

1.将文本更改为Root/Node1/Node1/Node3并按Tab键。

  • (所选项目将立即更改回来)*
cclgggtu

cclgggtu1#

问题不在于搜索和选择节点,而在于布局。我甚至没有注意到这一点,因为我认为您在创建最小示例时错过了一些东西。
为了修复布局,我添加了另一个TextBox(可以是任何其他元素),它可以在带有路径的TextBox之后获得焦点。

<Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto" />
            <RowDefinition />
        </Grid.RowDefinitions>

        <TextBox Text="{Binding VM.CurrentPath}" />
        <TextBox MinWidth="20" HorizontalAlignment="Right"/>

        <TreeView
            x:Name="TreeView"
            Grid.Row="1"
            ItemsSource="{Binding VM.RootNode.Children}">
            <TreeView.ItemContainerStyle>

字符串
这是因为在VM中获取Focus和选择节点的处理发生在主线程处理的一个“量子”中,并且它们相互干扰。
因此,解决此问题的第二种方法是将VM中的节点选择执行与执行TreeView焦点获取的量程分离。
例如,这可以通过在异步方法中使用延迟来完成。

public string CurrentPath
        {
            get => _currentPath;
            set
            {
                _currentPath = value;
                NotifyPropertyChanged();
                SelectNode();

                async void SelectNode()
                {
                    await Task.Delay(10);
                    if (FindNode(_currentPath) is TreeNode targetNode)
                    {
                        _mainWindow.TreeView.SelectedItemChanged -= this.TreeView_SelectedItemChanged;
                        targetNode.IsSelected = true;
                        _mainWindow.TreeView.SelectedItemChanged += this.TreeView_SelectedItemChanged;
                        targetNode.ForEachBranchNode(node => node.IsExpanded = true);
                    }
                }
            }
        }

**P.S.**但更好的实现是为TreeView创建一个Attached Property,它将采用路径并选择一个节点。此属性需要绑定到VM.CurrentPath。

相关问题