トップ回答者
TreeViewでかなり深い階層を開こうとすると発生するStackOverFlowExeptionの回避方法

質問
-
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
回答
-
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!)
- 回答の候補に設定 立花楓Microsoft employee, Moderator 2017年5月30日 0:11
- 回答としてマーク 立花楓Microsoft employee, Moderator 2017年6月6日 1:03
-
直接は関係ありませんが、MFC の CTreeCtrl クラスにおける階層数の制限についてというものがあります。Explorerでも256階層を表示することはできないそうです。
Tree構造で250階層を表示する意義があるのか再考されることをお勧めします。
- 回答の候補に設定 立花楓Microsoft employee, Moderator 2017年5月30日 0:11
- 回答としてマーク 立花楓Microsoft employee, Moderator 2017年6月6日 1:03
すべての返信
-
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!)
- 回答の候補に設定 立花楓Microsoft employee, Moderator 2017年5月30日 0:11
- 回答としてマーク 立花楓Microsoft employee, Moderator 2017年6月6日 1:03
-
直接は関係ありませんが、MFC の CTreeCtrl クラスにおける階層数の制限についてというものがあります。Explorerでも256階層を表示することはできないそうです。
Tree構造で250階層を表示する意義があるのか再考されることをお勧めします。
- 回答の候補に設定 立花楓Microsoft employee, Moderator 2017年5月30日 0:11
- 回答としてマーク 立花楓Microsoft employee, Moderator 2017年6月6日 1:03