如何在递归期间更新 TreeView c#

How to update TreeView during recursion c#

我正在使用递归函数填充树视图,如下所示。树视图得到正确填充,但我无法在递归期间在 UI 中更新它。我仍然是线程的新手,但我尝试使用下面给出的 "updateTreeView" 函数......在递归函数中,但无法正确实现它。我怎样才能实现这个功能??请分享一些代码...因为我不知道线程。

为了简单起见,我修改了代码。但是,递归函数非常复杂并且处理 COM 对象。

private void CreateMyTree(List<string> RootNodes,  TreeViewItem ParentNode)
    {
        if(mycheck here....)
         {
            for (int i = 1; i <= RootNodes.Count; i++)
            {
               TreeViewItem NewTreeItem = new TreeViewItem() { Header = RootNodes[i], IsExpanded = false };
               ParentNode.Items.Add(NewTreeItem);    
            } 
         }
        else
        {            
           ///here some checks again and recursion again
           CreateMyTree(RootNodes, ParentNode)

        }
}

private void button1_Click(object sender, RoutedEventArgs e)
{
     //Create RootNode in TreeView
     TreeViewItem ParentNode = new TreeViewItem() { Header = "TopNode", IsExpanded = true };

     //Recursively add items to TreeView
     CreateMyTree(RootNode, ParentNode);

     //update TreeView GUI
     treeView1.Items.Add(ParentNode);

}

private void updateTreeView(TreeViewItem TreeItem)
{
  this.Dispatcher.Invoke(System.Windows.Threading.DispatcherPriority.Background, new Action(delegate()
   {
        treeView1.Items.Add(TreeItem);
   }));
}

我认为你应该采纳 HighCore 的建议并使用适当的 XAML 数据绑定,如果你坚持要 "old school" 你可以这样做:

private static Action EmptyDelegate = delegate() { };
private void CreateMyTree(List<string> RootNodes,  TreeViewItem ParentNode)
    {
        if(mycheck here....)
         {
            for (int i = 1; i <= RootNodes.Count; i++)
            {
               TreeViewItem NewTreeItem = new TreeViewItem() { Header = RootNodes[i], IsExpanded = false };
               ParentNode.Items.Add(NewTreeItem);  
               updateTreeView();
            } 
         }
        else
        {            
           ///here some checks again and recursion again
           CreateMyTree(RootNodes, ParentNode)

        }
}

private void button1_Click(object sender, RoutedEventArgs e)
{
     //Create RootNode in TreeView
     TreeViewItem ParentNode = new TreeViewItem() { Header = "TopNode", IsExpanded = true };

      //update TreeView GUI
     treeView1.Items.Add(ParentNode);

     //Recursively add items to TreeView
     CreateMyTree(RootNode, ParentNode);



}

private void updateTreeView()
{
  treeView1.Dispatcher.Invoke(DispatcherPriority.Background, EmptyDelegate);
}

你应该跟着做。 这与线程无关。如果您想要多个级别,仍然需要使用 DataTemplateSelector 进行更好的实现。

以下是跨越 3 个级别的层次结构示例:

CS:

     public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();
        this.DataContext = this;
    }

    private Entity _entity;
    public Entity Entity
    {
        get
        {
            if(_entity == null)
                _entity = new Entity();
            return _entity;
        }
    }

    public List<Entity> Entities
    {
        get { return CreateMyTree(); }
    }

    private List<Entity> CreateMyTree()
    {
        var list = new List<Entity>();

        var p1 = new Entity {Title = "Parent 1"};
        p1.Children.Add(new Entity{ Title = "Child 1"});
        p1.Children.Add(new Entity { Title = "Child 2" });

        var p2 = new Entity { Title = "Parent 2" };
        var c1 = new Entity { Title = "Child 1"};

        var g1 = new Entity {Title = "GrandChild 1"};
        c1.Children.Add(g1);

        var c2 = new Entity { Title = "Child 2" };
        p2.Children.Add(c1);
        p2.Children.Add(c2);

        list.Add(p1);
        list.Add(p2);

        return list;
    }

}

public class Entity : INotifyPropertyChanged
{
    private string _title;
    public string Title
    {
        get { return _title; }
        set
        {
            _title = value;
        }
    }

    private List<Entity> _children;
    public List<Entity> Children
    {
        get
        {
            if(_children == null)
                _children = new List<Entity>();
            return _children;
        }
    }

    public event PropertyChangedEventHandler PropertyChanged = delegate { };
}

}

xaml :

 <Window x:Class="WpfApplication7.MainWindow"
      xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
      xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
      xmlns:wpfApplication7="clr-namespace:WpfApplication7"
      Title="MainWindow" Height="350" Width="525">
   <Window.Resources>
      <DataTemplate x:Key="level3">
          <TextBlock Text="{Binding Title}" Foreground="Green" />
      </DataTemplate>

      <HierarchicalDataTemplate x:Key="rootTemplate" ItemsSource="{Binding Children}" >
           <TextBlock Text="{Binding Title}" Foreground="Red"  />
           <HierarchicalDataTemplate.ItemTemplate>
               <HierarchicalDataTemplate ItemsSource="{Binding Children}" ItemTemplate="{StaticResource level3}">
                   <TextBlock Text="{Binding Title}" Foreground="Blue" />
               </HierarchicalDataTemplate>
           </HierarchicalDataTemplate.ItemTemplate>

      </HierarchicalDataTemplate>

 </Window.Resources>
   <Grid>
       <TreeView ItemsSource="{Binding Entities}" ItemTemplate="{StaticResource rootTemplate}"/>
   </Grid>
</Window>

前面的帖子关于数据绑定的设计是完全正确的。此外,如果你想解决异步填充树的问题,你必须在另一个 thread/task 中执行此操作。使用 "ObservableCollection",您还可以在此任务 运行 和更改树时使您的树保持最新。但要小心 - ObservableCollections 不喜欢从其他线程然后 Ui 线程更改它们。此问题已讨论 here and here

在我编写的以下示例中,我只是为 ObservableCollection 的每次修改调用 Dispatcher,但这不是最佳解决方案。

在示例中,您可以同步或异步调用树创建 - 因此您可以理解 Ui 的实现和行为的差异。

namespace TreeViewExample
{
   public class MyNode
   {
    public ObservableCollection<MyNode> ChildNodes { get; set; }
    public int Number { get; set; }

    public MyNode()
    {
        ChildNodes = new ObservableCollection<MyNode>();
    }
   }

public class SimpleCommand : ICommand
{
    private readonly Action _action;

    public SimpleCommand(Action action)
    {
        _action = action;
    }

    public bool CanExecute(object parameter)
    {
        return true;
    }

    public void Execute(object parameter)
    {
        if (parameter is string && (string) parameter == "async")
        {
            new TaskFactory().StartNew(_action);
        }
        else _action();
    }

    public event EventHandler CanExecuteChanged;
}

public class NodeViewModel : INotifyPropertyChanged
{
    private ObservableCollection<MyNode> _rootNodes;

    public ObservableCollection<MyNode> RootNodes
    {
        set
        {
            _rootNodes = value;
            OnPropertyChanged();
        }
        get { return _rootNodes; }
    }

    private readonly ICommand _populateCommand;
    private readonly Random _random;
    private Dispatcher _dispatcher;
    public ICommand PopulateCommand { get { return _populateCommand; } }

    public NodeViewModel()
    {
        _dispatcher = Dispatcher.CurrentDispatcher;
        _random = new Random();
        _populateCommand = new SimpleCommand(PopulateTree);
        RootNodes = new ObservableCollection<MyNode>();
    }

    private void PopulateTree()
    {
        try
        {
            var node = new MyNode {Number = 0};
            if (_dispatcher.CheckAccess())
                RootNodes.Add(node);
            else _dispatcher.Invoke(() => RootNodes.Add(node));
            FillNodeRecursively(node, 1);
        }
        catch (Exception e)
        {
            Console.WriteLine(e);
        }
    }


    private void FillNodeRecursively(MyNode rootNode, int level)
    {
        int rand = _random.Next(0, 4);
        for (var i = 0; i <= rand; i++)
        {
            var subNode = new MyNode {Number = rand + i};
            Thread.Sleep(50); //simulating some workload
            if (_dispatcher.CheckAccess())
                rootNode.ChildNodes.Add(subNode);
            else _dispatcher.Invoke(() => rootNode.ChildNodes.Add(subNode));
            if (level < 4)
                FillNodeRecursively(subNode, level + 1);
        }
    }

    public event PropertyChangedEventHandler PropertyChanged;
    protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
    {
        PropertyChangedEventHandler handler = PropertyChanged;
        if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
    }
}

}

这是 WPF window 的 xaml:

<Window x:Class="TreeViewExample.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="MainWindow" Height="350" Width="525">

<Window.Resources>
    <HierarchicalDataTemplate x:Key="NodesTemplate" ItemsSource="{Binding Path=ChildNodes}" >
        <TextBlock Text="{Binding Path=Number}" />
    </HierarchicalDataTemplate>
</Window.Resources>

<DockPanel LastChildFill="True">

    <StackPanel DockPanel.Dock="Bottom" >
        <Button Command="{Binding PopulateCommand}" CommandParameter="sync">Sync</Button>
        <Button Command="{Binding PopulateCommand}" CommandParameter="async">Async</Button>
    </StackPanel>

    <TreeView DockPanel.Dock="Top" ItemsSource="{Binding RootNodes}" ItemTemplate="{StaticResource NodesTemplate}">
        <TreeView.ItemContainerStyle>
            <Style TargetType="TreeViewItem">
                <Setter Property="IsExpanded" Value="True"/>
            </Style>
        </TreeView.ItemContainerStyle>
    </TreeView>

</DockPanel>