Quantcast
Channel: かずきのBlog@hatena
Viewing all articles
Browse latest Browse all 1387

UWP の TreeView でデータバインディングを使ってデータを表示する(プレビュー)

$
0
0

Windows 10 April 2018 Update で追加された TreeView ですがデータバインディングでデータを表示することが出来なくて不満でしたが、今見たらプレビューで出来るようです。

ちょっと試してみました。使い方は簡単です。ItemsSource にコレクションをバインドするのと、ItemTemplate 内で TreeViewItem の ItemsSource に子要素をバインドすることです。

因みに TreeView は仮想化されてないみたいなので、アプリ開発者側で頑張っていらないものは消してねってスタンスみたいです。じゃぁやってみましょう。

表示するデータとして以下のような Item クラスを準備しました。

using Prism.Mvvm;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace App1
{
    publicclass Item : BindableBase
    {
        private CancellationTokenSource _cancellationTokenSource;
        privatebool _hasUnrealizedChildren;
        publicbool HasUnrealizedChildren
        {
            get { return _hasUnrealizedChildren; }
            set { SetProperty(ref _hasUnrealizedChildren, value); }
        }

        privatebool _isLoading;
        publicbool IsLoading
        {
            get { return _isLoading; }
            set { SetProperty(ref _isLoading, value); }
        }

        privatestring _text;
        publicstring Text
        {
            get { return _text; }
            set { SetProperty(ref _text, value); }
        }

        public ObservableCollection<Item> Children { get; } = new ObservableCollection<Item>();

        privatestaticint _counter;

        publicstatic Item Create() => new Item
        {
            Text = $"Item {_counter++}",
            HasUnrealizedChildren = true,
        };

        public async Task FillChildrenAsync()
        {
            if (IsLoading)
            {
                return;
            }

            IsLoading = true;
            _cancellationTokenSource = new CancellationTokenSource();
            for (int i = 0; i < 5; i++)
            {
                await Task.Delay(1000, _cancellationTokenSource.Token);
                if (_cancellationTokenSource.IsCancellationRequested)
                {
                    break;
                }

                Children.Add(Create());
            }

            IsLoading = false;
            HasUnrealizedChildren = false;
        }

        public Task ClearChildrenAsync()
        {
            _cancellationTokenSource?.Cancel();
            Children.Clear();
            IsLoading = false;
            HasUnrealizedChildren = true;
            return Task.CompletedTask;
        }
    }
}

BindableBase クラスは Prism.Core のパッケージのものです。自分で INotifyPropertyChanged を実装するのがめんどくさかったので使ってます。FillChildrenAsync で子要素を順次読み込んで、ClearChildrenAsync で子要素を綺麗にするようにしてます。

あとは、MainPage.xaml.cs のプロパティとして定義して、TreeView のノードを展開したときと閉じたときのイベントをハンドリングして対象ノードのデータの子要素を読み込んだりクリアーしたりしてます。

<Pagex:Class="App1.MainPage"xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"xmlns:local="using:App1"xmlns:d="http://schemas.microsoft.com/expression/blend/2008"xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"mc:Ignorable="d"Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"><Grid><TreeView x:Name="treeView"ItemsSource="{x:Bind Items}"Expanding="{x:Bind TreeView_Expanding}"Collapsed="{x:Bind TreeView_Collapsed}"><TreeView.ItemTemplate><DataTemplate x:DataType="local:Item"><TreeViewItem ItemsSource="{x:Bind Children}"HasUnrealizedChildren="{x:Bind HasUnrealizedChildren, Mode=OneWay}"><StackPanel Orientation="Horizontal"><ProgressRing Visibility="{x:Bind local:BindingUtilities.BoolToVisibility(IsLoading), Mode=OneWay}"IsActive="True" /><TextBlock Text="{x:Bind Text}"Style="{ThemeResource BodyTextBlockStyle}" /></StackPanel></TreeViewItem></DataTemplate></TreeView.ItemTemplate></TreeView></Grid></Page>
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices.WindowsRuntime;
using Windows.Foundation;
using Windows.Foundation.Collections;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Controls.Primitives;
using Windows.UI.Xaml.Data;
using Windows.UI.Xaml.Input;
using Windows.UI.Xaml.Media;
using Windows.UI.Xaml.Navigation;

// 空白ページの項目テンプレートについては、https://go.microsoft.com/fwlink/?LinkId=402352&clcid=0x411 を参照してくださいnamespace App1
{
    /// <summary>/// それ自体で使用できる空白ページまたはフレーム内に移動できる空白ページ。/// </summary>publicsealedpartialclass MainPage : Page
    {
        private IEnumerable<Item> Items { get; } = Enumerable.Range(1, 10).Select(_ => Item.Create()).ToArray();
        public MainPage()
        {
            this.InitializeComponent();
        }

        privatevoid TreeView_Expanding(TreeView sender, TreeViewExpandingEventArgs args)
        {
            (args.Item as Item)?.FillChildrenAsync();
        }

        privatevoid TreeView_Collapsed(TreeView sender, TreeViewCollapsedEventArgs args)
        {
            (args.Item as Item)?.ClearChildrenAsync();
        }
    }
}

あとは、bool から Visibility への変換用の関数も用意しました。x:Bind 楽ですね。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Windows.UI.Xaml;

namespace App1
{
    publicstaticclass BindingUtilities
    {
        publicstatic Visibility BoolToVisibility(boolvalue) => BoolToVisibility(value, true);

        publicstatic Visibility BoolToVisibility(boolvalue, bool trueIsVisible)
        {
            if (trueIsVisible)
            {
                returnvalue ? Visibility.Visible : Visibility.Collapsed;
            }

            returnvalue ? Visibility.Collapsed : Visibility.Visible;
        }
    }
}

実行するとこんな感じで動きます。

f:id:okazuki:20180927164436g:plain

よきかな。


Viewing all articles
Browse latest Browse all 1387

Trending Articles



<script src="https://jsc.adskeeper.com/r/s/rssing.com.1596347.js" async> </script>