none
TreeViewを折り返して表示させることは可能でしょうか? RRS feed

  • 質問

  • いつもお世話になっています。

    あまり見慣れないコントロールだとは思うのですが、TreeViewを折り返して表示できるようにしたいと考えています。

    ひとまず、TreeView、TreeViewItem両方のItemsPanelを

    <ItemPanelTemplate><WrapPanel Orientation="Vertical"/></ItemPanelTemplate>

    とし、

    TreeViewに ScrollViewer.VerticalScrollBarVisibility="Auto" ScrollViewer.HorizontalScrollBarVisibility="Disabled"

    を定義することで、それらしい動きをするようになったのですが、一つ問題がありました。

    下記のようなデータをTreeViewに表示するとします。

    <items>  <item value="1">   <item value="1.1">    <item value="1.1.1"/>    <item value="1.1.2"/>    <item value="1.1.3"/>   </item>   <item value="1.2">    <item value="1.2.1"/>    <item value="1.2.2"/>    <item value="1.2.3"/>   </item>  </item>  <item value="2">   <item value="2.1">    <item value="2.1.1"/>   </item>  </item>  <item value="3">   <item value="3.1">    <item value="3.1.1"/>    <item value="3.1.2"/>   </item>  </item> </items>

    8項目までレイアウトできる高さだとした場合、”1.2.3”は1列目にはレイアウトできないので、"1.2"が折り返され2列目に、"1.1"と同じ高さからレイアウトされます。

    ただし、そうした場合に、1列目には3項目分の空きがあるため、"2"から"2.1.1"が1列目にレイアウトされてしまいます。(下図の現状をご参照ください)

    WrapPanelの動作を考えるといたしかたないことなのですが、その様にレイアウトされてしまうと、"2.1.1"の続きが"1.2"であるかのように見えてしまい困っています。

    下図Aのように"2"は3列目に表示されてほしいのですが、何か方法はありますでしょうか。

    また、TreeViewでは実現できそうにないとは思うのですが、下図Bのように"1.2.2"まで1列目にレイアウトされ、"1.2.3"が2列目に、"1"と同じ高さからレイアウトされ、その続きに"2"~"3.1.2"までがレイアウトされる、というコントロールがあれば、理想的です。

    

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

    2012年2月14日 4:42

回答

  • Bのように表示するだけなら左にマージンと展開用のボタンを付けるだけですけどね。

    <ItemsControl ItemsSource="{Binding Collection}" >
        <ItemsControl.Resources>
            <app:LevelMarginConverter x:Key="conv" xmlns:app="clr-namespace:WpfApplication1"/>
        </ItemsControl.Resources>
        <ItemsControl.ItemsPanel>
            <ItemsPanelTemplate>
                <WrapPanel Orientation="Vertical" />
            </ItemsPanelTemplate>
        </ItemsControl.ItemsPanel>
        <ItemsControl.ItemTemplate>
            <DataTemplate>
                <StackPanel Orientation="Horizontal"
                    Margin="{Binding Path=Level,Converter={StaticResource conv},ConverterParameter=10}">
                    <ToggleButton IsChecked="{Binding Path=IsExpand}" x:Name="tgl"
                          Width="10" Height="10"  IsEnabled="{Binding Path=CanExpand}" >
                    </ToggleButton>
                    <TextBlock Text="{Binding Item.Value}" />
                </StackPanel>
    
                <DataTemplate.Triggers>
                    <DataTrigger  Binding="{Binding CanExpand}" Value="false" >
                        <Setter TargetName="tgl" Property="Visibility" Value="Hidden" />
                    </DataTrigger>
                </DataTemplate.Triggers>
            </DataTemplate>
        </ItemsControl.ItemTemplate>
    </ItemsControl>

    public partial class Window1 : Window
    {
        public Window1()
        {
            InitializeComponent();
    
            var flat = FlatItem.Create(TestValue.CreateTestData());
    
            this.DataContext = flat;
        }
    }
    
    class TestValue
    {
        public TestValue()
        {
            Items = new List<TestValue>();
        }
    
        public string Value { get; set; }
    
        public List<TestValue> Items { get; private set; }
    
        public static TestValue CreateTestData()
        {
            TestValue lv0 = new TestValue();
            lv0.Value = "ルート";
    
            for (int i = 1; i <= 5; i++)
            {
                TestValue lv1 = new TestValue();
                lv1.Value = i.ToString();
    
                for (int j = 1; j <= 2; j++)
                {
                    TestValue lv2 = new TestValue();
                    lv2.Value = lv1.Value + "." + j.ToString();
    
                    for (int k = 1; k <= 10; k++)
                    {
                        TestValue lv3 = new TestValue();
                        lv3.Value = lv2.Value + "." + k.ToString();
    
                        lv2.Items.Add(lv3);
                    }
    
                    lv1.Items.Add(lv2);
                }
                lv0.Items.Add(lv1);
            }
    
            return lv0;
        }
    }
    
    abstract class FratWrapperItem<T,U> : INotifyPropertyChanged
    {
         public FratWrapperItem(int level , T item, ObservableCollection<object> collection)
        {
            this.Items = new List<U>();
    
            this.Level = level;
            this.Item = item;
            this._Collection = collection;
        }
    
        public ObservableCollection<object> Collection { get { return _Collection; } }
        private ObservableCollection<object> _Collection;
    
        public int Level { get; set; }
        public T Item { get; set; }
        public List<U> Items { get; private set; }
    
        public bool IsExpand
        {
            get
            {
                return _IsExpand;
            }
            set
            {
                if (_IsExpand != value)
                {
                    _IsExpand = value;
                    OnPropertyChanged("IsExpand");
    
                    if (value)
                    {
                        OnExpand();
                    }
                    else
                    {
                        OnShrink();
                    }
                }
            }
        }
        private bool _IsExpand;
    
        public abstract bool CanExpand { get; }
    
        protected abstract void OnExpand();
        protected abstract void OnShrink();
    
    
        #region INotifyPropertyChanged メンバ
    
        public event PropertyChangedEventHandler PropertyChanged;
        protected virtual void OnPropertyChanged(string name)
        {
            if (PropertyChanged != null)
            {
                PropertyChanged(this, new PropertyChangedEventArgs(name));
            }
        }
        #endregion
    }
    
    class FlatItem : FratWrapperItem<TestValue, FlatItem>
    {
        public static FlatItem Create(TestValue item)
        {
            FlatItem root = new FlatItem(0, item, new ObservableCollection<object>());
            root.Collection.Add(root);
            return root;
        }
    
        public FlatItem(int level, TestValue item, ObservableCollection<object> collection)
            : base(level,item,collection){}
    
        protected override void OnExpand()
        {
            int index = Collection.IndexOf(this) ;
            foreach (TestValue child in this.Item.Items)
            {
                FlatItem childFlat = new FlatItem(this.Level+1 ,child, Collection);
    
                Collection.Insert(++index, childFlat);
                this.Items.Add(childFlat);
            }
        }
    
        protected override void OnShrink()
        {
            foreach (FlatItem flatChild in this.Items)
            {
                flatChild.IsExpand = false;
                Collection.Remove(flatChild );
            }
        }
    
        public override bool CanExpand
        {
            get { return this.Item.Items.Count > 0; }
        }
    }
    
    public class LevelMarginConverter : IValueConverter
    {
        public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
        {
            double span = 20;
            if (parameter != null)
            {
                double.TryParse(parameter.ToString(),out span);
            }
            return  new Thickness((int)value * span, 0, 0, 0);
        }
    
        public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
        {
            throw new NotImplementedException();
        }
    
    #編集したらコードが壊れたので再投稿

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

    • 回答の候補に設定 山本春海 2012年2月23日 9:01
    • 回答としてマーク 山本春海 2012年2月27日 8:47
    2012年2月14日 10:48

すべての返信

  • Bのように表示するだけなら左にマージンと展開用のボタンを付けるだけですけどね。

    <ItemsControl ItemsSource="{Binding Collection}" >
        <ItemsControl.Resources>
            <app:LevelMarginConverter x:Key="conv" xmlns:app="clr-namespace:WpfApplication1"/>
        </ItemsControl.Resources>
        <ItemsControl.ItemsPanel>
            <ItemsPanelTemplate>
                <WrapPanel Orientation="Vertical" />
            </ItemsPanelTemplate>
        </ItemsControl.ItemsPanel>
        <ItemsControl.ItemTemplate>
            <DataTemplate>
                <StackPanel Orientation="Horizontal"
                    Margin="{Binding Path=Level,Converter={StaticResource conv},ConverterParameter=10}">
                    <ToggleButton IsChecked="{Binding Path=IsExpand}" x:Name="tgl"
                          Width="10" Height="10"  IsEnabled="{Binding Path=CanExpand}" >
                    </ToggleButton>
                    <TextBlock Text="{Binding Item.Value}" />
                </StackPanel>
    
                <DataTemplate.Triggers>
                    <DataTrigger  Binding="{Binding CanExpand}" Value="false" >
                        <Setter TargetName="tgl" Property="Visibility" Value="Hidden" />
                    </DataTrigger>
                </DataTemplate.Triggers>
            </DataTemplate>
        </ItemsControl.ItemTemplate>
    </ItemsControl>

    public partial class Window1 : Window
    {
        public Window1()
        {
            InitializeComponent();
    
            var flat = FlatItem.Create(TestValue.CreateTestData());
    
            this.DataContext = flat;
        }
    }
    
    class TestValue
    {
        public TestValue()
        {
            Items = new List<TestValue>();
        }
    
        public string Value { get; set; }
    
        public List<TestValue> Items { get; private set; }
    
        public static TestValue CreateTestData()
        {
            TestValue lv0 = new TestValue();
            lv0.Value = "ルート";
    
            for (int i = 1; i <= 5; i++)
            {
                TestValue lv1 = new TestValue();
                lv1.Value = i.ToString();
    
                for (int j = 1; j <= 2; j++)
                {
                    TestValue lv2 = new TestValue();
                    lv2.Value = lv1.Value + "." + j.ToString();
    
                    for (int k = 1; k <= 10; k++)
                    {
                        TestValue lv3 = new TestValue();
                        lv3.Value = lv2.Value + "." + k.ToString();
    
                        lv2.Items.Add(lv3);
                    }
    
                    lv1.Items.Add(lv2);
                }
                lv0.Items.Add(lv1);
            }
    
            return lv0;
        }
    }
    
    abstract class FratWrapperItem<T,U> : INotifyPropertyChanged
    {
         public FratWrapperItem(int level , T item, ObservableCollection<object> collection)
        {
            this.Items = new List<U>();
    
            this.Level = level;
            this.Item = item;
            this._Collection = collection;
        }
    
        public ObservableCollection<object> Collection { get { return _Collection; } }
        private ObservableCollection<object> _Collection;
    
        public int Level { get; set; }
        public T Item { get; set; }
        public List<U> Items { get; private set; }
    
        public bool IsExpand
        {
            get
            {
                return _IsExpand;
            }
            set
            {
                if (_IsExpand != value)
                {
                    _IsExpand = value;
                    OnPropertyChanged("IsExpand");
    
                    if (value)
                    {
                        OnExpand();
                    }
                    else
                    {
                        OnShrink();
                    }
                }
            }
        }
        private bool _IsExpand;
    
        public abstract bool CanExpand { get; }
    
        protected abstract void OnExpand();
        protected abstract void OnShrink();
    
    
        #region INotifyPropertyChanged メンバ
    
        public event PropertyChangedEventHandler PropertyChanged;
        protected virtual void OnPropertyChanged(string name)
        {
            if (PropertyChanged != null)
            {
                PropertyChanged(this, new PropertyChangedEventArgs(name));
            }
        }
        #endregion
    }
    
    class FlatItem : FratWrapperItem<TestValue, FlatItem>
    {
        public static FlatItem Create(TestValue item)
        {
            FlatItem root = new FlatItem(0, item, new ObservableCollection<object>());
            root.Collection.Add(root);
            return root;
        }
    
        public FlatItem(int level, TestValue item, ObservableCollection<object> collection)
            : base(level,item,collection){}
    
        protected override void OnExpand()
        {
            int index = Collection.IndexOf(this) ;
            foreach (TestValue child in this.Item.Items)
            {
                FlatItem childFlat = new FlatItem(this.Level+1 ,child, Collection);
    
                Collection.Insert(++index, childFlat);
                this.Items.Add(childFlat);
            }
        }
    
        protected override void OnShrink()
        {
            foreach (FlatItem flatChild in this.Items)
            {
                flatChild.IsExpand = false;
                Collection.Remove(flatChild );
            }
        }
    
        public override bool CanExpand
        {
            get { return this.Item.Items.Count > 0; }
        }
    }
    
    public class LevelMarginConverter : IValueConverter
    {
        public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
        {
            double span = 20;
            if (parameter != null)
            {
                double.TryParse(parameter.ToString(),out span);
            }
            return  new Thickness((int)value * span, 0, 0, 0);
        }
    
        public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
        {
            throw new NotImplementedException();
        }
    
    #編集したらコードが壊れたので再投稿

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

    • 回答の候補に設定 山本春海 2012年2月23日 9:01
    • 回答としてマーク 山本春海 2012年2月27日 8:47
    2012年2月14日 10:48
  • お礼が遅くなってしまってすみません。

    すごいですね。こんな風にできるんですね。

    上記ソースで動かしてみたところ、理想的な動きでした。

    ただ、現在作成中のクラスとうまく合せることができなくて、取り入れるのが難しい状況です。

    せっかくご教授いただいたのに本当に申し訳ないです。

    結果をご報告できるのはものすごく遅くなってしまいそうなので、取り急ぎ(と言ってもすでに十分遅くなってしまっていますが)お礼まで。

    ありがとうございました。


    2012年3月6日 7:23