none
TreeViewでかなり深い階層を開こうとすると発生するStackOverFlowExeptionの回避方法 RRS feed

  • 質問

  • TreeView内にTreeViewItemのかなり深いネスト構造を配置したツリーを作り、階層を順に開こうとすると250階層あたりでStackOverFlowExeptionが発生してしまいます。この例外を回避する方法があれば教えていただけないでしょうか。

    試したコードは下記になります。フレームワークは.Net 4.6.1になります。

    <Window x:Class="Tree.TreeWindow"
            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
            xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
            xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
            xmlns:local="clr-namespace:Tree"
            mc:Ignorable="d"
            Title="Tree" Height="300" Width="300">
        <TreeView x:Name="TreeView1"/>
    </Window>

    public partial class TreeWindow: Window
    {
        public TreeWindow()
        {
            InitializeComponent();
            TreeViewItem root = new TreeViewItem() ;
     
            TreeViewItem parent = new TreeViewItem() ;
            root.Items.Add(parent);
            TreeView1.Items.Add(root);
     
            for (int i = 1; i <= 300; i++)//300のネスト構造
                {
                    TreeViewItem childItem = new TreeViewItem();
                    parent.Items.Add(childItem);
                    parent = childItem;
                }
        }
    }

    よろしくお願いいたします。



    • 編集済み yj0529 2017年5月29日 9:00
    2017年5月29日 8:58

回答

  • Connectによるとフレームワークの設計上の限界っぽい?
    #x64だと149階層でエラーになったのでさらに厳しいです

    深すぎるツリー構造になってしまうならTreeViewは使わずに別の方法で表示したほうがいいのかも。

    <Window x:Class="WpfApplication1.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:app="clr-namespace:WpfApplication1"
            Title="MainWindow" Height="350" Width="525">
        <Window.Resources>
            <BooleanToVisibilityConverter x:Key="b2v"/>
            <app:LevelWidthConverter x:Key="levelWidthConverter"/>
    
            <ControlTemplate TargetType="ListBoxItem" x:Key="treeListBoxItemTemplate">
                <Grid >
                    <Grid.ColumnDefinitions>
                        <ColumnDefinition Width="{Binding Path=Level,Converter={StaticResource levelWidthConverter},ConverterParameter=10}"/>
                        <ColumnDefinition Width="Auto"/>
                        <ColumnDefinition Width="*"/>
                    </Grid.ColumnDefinitions>
                    <ToggleButton Grid.Column="1" IsChecked="{Binding Path=IsExpanded}">
                        <ToggleButton.Template>
                            <ControlTemplate TargetType="{x:Type ToggleButton}">
                                <Grid Background="Transparent">
                                    <Grid.LayoutTransform>
                                        <RotateTransform Angle="-90"/>
                                    </Grid.LayoutTransform>
                                    <TextBlock Text="▼"/>
                                </Grid>
                                <ControlTemplate.Triggers>
                                    <DataTrigger Binding="{Binding Path=Children.Count,FallbackValue=0}" Value="0" >
                                        <Setter  Property="Visibility" Value="Hidden" />
                                    </DataTrigger>
                                </ControlTemplate.Triggers>
                            </ControlTemplate>
    
                        </ToggleButton.Template>
                    </ToggleButton>
                    <ContentPresenter Grid.Column="2" Content="{Binding Path=Content.Item,RelativeSource={RelativeSource Mode=TemplatedParent}}" />
                </Grid>
            </ControlTemplate>
    
            <Style TargetType="{x:Type ListBoxItem}" x:Key="treeListBoxItemContainerStyle">
                <Setter Property="Visibility" Value="{Binding Path=Parent.IsExpanded,Converter={StaticResource b2v}}" />
                <Setter Property="Template" Value="{StaticResource treeListBoxItemTemplate}" />
            </Style>
        </Window.Resources>
    
    
        <ListBox x:Name="tree1" ItemContainerStyle="{StaticResource treeListBoxItemContainerStyle}"
                 ItemsSource="{Binding Path=All}">
    
        </ListBox>
    </Window>
    using System;
    using System.Linq;
    using System.Windows;
    using System.Windows.Data;
    
    namespace WpfApplication1
    {
        public partial class MainWindow : Window
        {
            public MainWindow()
            {
                InitializeComponent();
    
                this.DataContext = MakeDummy();
            }
    
    
            private static System.Collections.ObjectModel.ObservableCollection<TreeLike<string>> MakeDummy()
            {
                var col = new System.Collections.ObjectModel.ObservableCollection<TreeLike<string>>();
                TreeLike<string> root = new TreeLike<string>("ROOT") { IsExpanded = true };
                root.All = new System.Collections.ObjectModel.ObservableCollection<TreeLike<string>>();
                root.All.Add(root);
                col.Add(root);
    
                TreeLike<string> child = root;
                child = root;
                for (int i = 1; i < 5; i++)
                {
                    TreeLike<string> grandChild = new TreeLike<string>("A" + i.ToString()) { IsExpanded = true };
                    
                    child.Children.Add(grandChild);
                    child = grandChild;
                    child.IsExpanded = true;
    
                }
    
                child = root;
                for (int i = 1; i < 300; i++)
                {
                    TreeLike<string> grandChild = new TreeLike<string>("B" + i.ToString()) { IsExpanded = true };
                    child.Children.Add(grandChild);
                    child = grandChild;
                    child.IsExpanded = true;
    
                }
                return col;
            }
        }
    
    
    
        /// <summary>TreeItemのような構造を平坦に展開するクラス</summary>
        class TreeLike<T> : System.ComponentModel.INotifyPropertyChanged
        {
            public TreeLike(T item)
            {
                Children = new System.Collections.ObjectModel.ObservableCollection<TreeLike<T>>();
                Children.CollectionChanged += Children_CollectionChanged;
    
                this.Item = item;
    
            }
            public int Level { get { return this.Parent == null ? 0 : this.Parent.Level + 1; } }
            public int AllChildCount
            {
                get
                {
                    return this.Children.Sum(_ => _.AllChildCount);
                }
            }
            public T Item { get; private set; }
    
            public TreeLike<T> Parent { get; private set; }
    
            void Children_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
            {
                if (e.OldItems != null)
                {
                    foreach (TreeLike<T> child in e.OldItems)
                    {
                        child.Parent = null;
                        child.All = null;
                        this.All.Remove(child);
                    }
                }
    
                if (e.NewItems != null)
                {
                    int index = this.All.IndexOf(this) + 1;
    
                    foreach (TreeLike<T> child in e.NewItems)
                    {
                        child.Parent = this;
                        child.All = this.All;
    
                        this.All.Insert(child.GetPreviewIndexInALL() + 1, child);
                    }
                }
            }
    
            private int GetPreviewIndexInALL()
            {
                TreeLike<T> target = null;
                TreeLike<T> parent = this.Parent;
                TreeLike<T> child = this;
                if (this.Parent == null)
                {
                    return -1;
                }
    
                if (this.Parent.Children[0]==this)
                {
                    return this.All.IndexOf(this.Parent);
                }
                int index = this.Parent.Children.IndexOf(this) - 1;
                target = this.Parent.Children[index];
                while (target.Children.Count > 0)
                {
                    target = target.Children.Last();
                }
                return this.All.IndexOf(target);
            }
    
            public System.Collections.ObjectModel.ObservableCollection<TreeLike<T>> Children { get; private set; }
    
    
            public System.Collections.ObjectModel.ObservableCollection<TreeLike<T>> All { get; set; }
    
    
            public bool IsExpanded
            {
                get
                {
                    return _IsExpanded;
                }
                set
                {
                    if (_IsExpanded != value)
                    {
                        _IsExpanded = value;
                        OnPropertyChanged("IsExpanded");
    
                        if (!value)
                        {
                            foreach (TreeLike<T> child in Children)
                            {
                                child.IsExpanded = false;
                            }
                        }
                    }
                }
            }
            private bool _IsExpanded;
    
    
            #region INotifyPropertyChanged メンバ
    
            public event System.ComponentModel.PropertyChangedEventHandler PropertyChanged;
            protected virtual void OnPropertyChanged(string name)
            {
                var pc = PropertyChanged;
                if (pc != null)
                {
                    pc(this, new System.ComponentModel.PropertyChangedEventArgs(name));
                }
            }
            #endregion
        }
    
        /// <summary>TreeViewの左のスペース幅</summary>
        class LevelWidthConverter : IValueConverter
        {
            public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
            {
                var ic = value as IConvertible;
                if (ic != null)
                {
                    return ic.ToDouble(null) * 10;
                }
                return 0;
            }
    
            public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
            {
                throw new NotImplementedException();
            }
        }
    }

    個別に明示されていない限りgekkaがフォーラムに投稿したコードにはフォーラム使用条件に基づき「MICROSOFT LIMITED PUBLIC LICENSE」が適用されます。(かなり自由に使ってOK!)

    2017年5月29日 12:01
  • 直接は関係ありませんが、MFC の CTreeCtrl クラスにおける階層数の制限についてというものがあります。Explorerでも256階層を表示することはできないそうです。

    Tree構造で250階層を表示する意義があるのか再考されることをお勧めします。

    2017年5月29日 13:23

すべての返信

  • Connectによるとフレームワークの設計上の限界っぽい?
    #x64だと149階層でエラーになったのでさらに厳しいです

    深すぎるツリー構造になってしまうならTreeViewは使わずに別の方法で表示したほうがいいのかも。

    <Window x:Class="WpfApplication1.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:app="clr-namespace:WpfApplication1"
            Title="MainWindow" Height="350" Width="525">
        <Window.Resources>
            <BooleanToVisibilityConverter x:Key="b2v"/>
            <app:LevelWidthConverter x:Key="levelWidthConverter"/>
    
            <ControlTemplate TargetType="ListBoxItem" x:Key="treeListBoxItemTemplate">
                <Grid >
                    <Grid.ColumnDefinitions>
                        <ColumnDefinition Width="{Binding Path=Level,Converter={StaticResource levelWidthConverter},ConverterParameter=10}"/>
                        <ColumnDefinition Width="Auto"/>
                        <ColumnDefinition Width="*"/>
                    </Grid.ColumnDefinitions>
                    <ToggleButton Grid.Column="1" IsChecked="{Binding Path=IsExpanded}">
                        <ToggleButton.Template>
                            <ControlTemplate TargetType="{x:Type ToggleButton}">
                                <Grid Background="Transparent">
                                    <Grid.LayoutTransform>
                                        <RotateTransform Angle="-90"/>
                                    </Grid.LayoutTransform>
                                    <TextBlock Text="▼"/>
                                </Grid>
                                <ControlTemplate.Triggers>
                                    <DataTrigger Binding="{Binding Path=Children.Count,FallbackValue=0}" Value="0" >
                                        <Setter  Property="Visibility" Value="Hidden" />
                                    </DataTrigger>
                                </ControlTemplate.Triggers>
                            </ControlTemplate>
    
                        </ToggleButton.Template>
                    </ToggleButton>
                    <ContentPresenter Grid.Column="2" Content="{Binding Path=Content.Item,RelativeSource={RelativeSource Mode=TemplatedParent}}" />
                </Grid>
            </ControlTemplate>
    
            <Style TargetType="{x:Type ListBoxItem}" x:Key="treeListBoxItemContainerStyle">
                <Setter Property="Visibility" Value="{Binding Path=Parent.IsExpanded,Converter={StaticResource b2v}}" />
                <Setter Property="Template" Value="{StaticResource treeListBoxItemTemplate}" />
            </Style>
        </Window.Resources>
    
    
        <ListBox x:Name="tree1" ItemContainerStyle="{StaticResource treeListBoxItemContainerStyle}"
                 ItemsSource="{Binding Path=All}">
    
        </ListBox>
    </Window>
    using System;
    using System.Linq;
    using System.Windows;
    using System.Windows.Data;
    
    namespace WpfApplication1
    {
        public partial class MainWindow : Window
        {
            public MainWindow()
            {
                InitializeComponent();
    
                this.DataContext = MakeDummy();
            }
    
    
            private static System.Collections.ObjectModel.ObservableCollection<TreeLike<string>> MakeDummy()
            {
                var col = new System.Collections.ObjectModel.ObservableCollection<TreeLike<string>>();
                TreeLike<string> root = new TreeLike<string>("ROOT") { IsExpanded = true };
                root.All = new System.Collections.ObjectModel.ObservableCollection<TreeLike<string>>();
                root.All.Add(root);
                col.Add(root);
    
                TreeLike<string> child = root;
                child = root;
                for (int i = 1; i < 5; i++)
                {
                    TreeLike<string> grandChild = new TreeLike<string>("A" + i.ToString()) { IsExpanded = true };
                    
                    child.Children.Add(grandChild);
                    child = grandChild;
                    child.IsExpanded = true;
    
                }
    
                child = root;
                for (int i = 1; i < 300; i++)
                {
                    TreeLike<string> grandChild = new TreeLike<string>("B" + i.ToString()) { IsExpanded = true };
                    child.Children.Add(grandChild);
                    child = grandChild;
                    child.IsExpanded = true;
    
                }
                return col;
            }
        }
    
    
    
        /// <summary>TreeItemのような構造を平坦に展開するクラス</summary>
        class TreeLike<T> : System.ComponentModel.INotifyPropertyChanged
        {
            public TreeLike(T item)
            {
                Children = new System.Collections.ObjectModel.ObservableCollection<TreeLike<T>>();
                Children.CollectionChanged += Children_CollectionChanged;
    
                this.Item = item;
    
            }
            public int Level { get { return this.Parent == null ? 0 : this.Parent.Level + 1; } }
            public int AllChildCount
            {
                get
                {
                    return this.Children.Sum(_ => _.AllChildCount);
                }
            }
            public T Item { get; private set; }
    
            public TreeLike<T> Parent { get; private set; }
    
            void Children_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
            {
                if (e.OldItems != null)
                {
                    foreach (TreeLike<T> child in e.OldItems)
                    {
                        child.Parent = null;
                        child.All = null;
                        this.All.Remove(child);
                    }
                }
    
                if (e.NewItems != null)
                {
                    int index = this.All.IndexOf(this) + 1;
    
                    foreach (TreeLike<T> child in e.NewItems)
                    {
                        child.Parent = this;
                        child.All = this.All;
    
                        this.All.Insert(child.GetPreviewIndexInALL() + 1, child);
                    }
                }
            }
    
            private int GetPreviewIndexInALL()
            {
                TreeLike<T> target = null;
                TreeLike<T> parent = this.Parent;
                TreeLike<T> child = this;
                if (this.Parent == null)
                {
                    return -1;
                }
    
                if (this.Parent.Children[0]==this)
                {
                    return this.All.IndexOf(this.Parent);
                }
                int index = this.Parent.Children.IndexOf(this) - 1;
                target = this.Parent.Children[index];
                while (target.Children.Count > 0)
                {
                    target = target.Children.Last();
                }
                return this.All.IndexOf(target);
            }
    
            public System.Collections.ObjectModel.ObservableCollection<TreeLike<T>> Children { get; private set; }
    
    
            public System.Collections.ObjectModel.ObservableCollection<TreeLike<T>> All { get; set; }
    
    
            public bool IsExpanded
            {
                get
                {
                    return _IsExpanded;
                }
                set
                {
                    if (_IsExpanded != value)
                    {
                        _IsExpanded = value;
                        OnPropertyChanged("IsExpanded");
    
                        if (!value)
                        {
                            foreach (TreeLike<T> child in Children)
                            {
                                child.IsExpanded = false;
                            }
                        }
                    }
                }
            }
            private bool _IsExpanded;
    
    
            #region INotifyPropertyChanged メンバ
    
            public event System.ComponentModel.PropertyChangedEventHandler PropertyChanged;
            protected virtual void OnPropertyChanged(string name)
            {
                var pc = PropertyChanged;
                if (pc != null)
                {
                    pc(this, new System.ComponentModel.PropertyChangedEventArgs(name));
                }
            }
            #endregion
        }
    
        /// <summary>TreeViewの左のスペース幅</summary>
        class LevelWidthConverter : IValueConverter
        {
            public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
            {
                var ic = value as IConvertible;
                if (ic != null)
                {
                    return ic.ToDouble(null) * 10;
                }
                return 0;
            }
    
            public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
            {
                throw new NotImplementedException();
            }
        }
    }

    個別に明示されていない限りgekkaがフォーラムに投稿したコードにはフォーラム使用条件に基づき「MICROSOFT LIMITED PUBLIC LICENSE」が適用されます。(かなり自由に使ってOK!)

    2017年5月29日 12:01
  • 直接は関係ありませんが、MFC の CTreeCtrl クラスにおける階層数の制限についてというものがあります。Explorerでも256階層を表示することはできないそうです。

    Tree構造で250階層を表示する意義があるのか再考されることをお勧めします。

    2017年5月29日 13:23
  • gekkaさん
    ご回答に加えて代替案まで示していただきありがとうございます。よく確認してみます!
    2017年5月30日 8:44
  • 佐祐理さん
    Tree構造での表示する階層についても検討してみます。ありがとうございます。
    2017年5月30日 8:45