Help us understand the problem. What is going on with this article?

#PowerApps で TreeView Controlを作る

つくるもの

今回は、階層型のデータから、PowerAppsでTreeView Controlを作成する方法を紹介します。
データソースとしてはSharePoint Onlineのカスタムリストを利用しています。
tree構成.PNG
* 参考にしたビデオはこちらです。

最終的には下のように動くアプリを作成しました。


準備

SharePointカスタムリスト

(データソースがSharePointカスタムリストである必要は必ずしもありません。利用しやすいデータソースを使ってください。)
データソースのSharePointリストは下図のような構成で作っています。
SPO.PNG
List Name:
TreeViewElement
Columns:
- spElmntName : Title列。 各要素の名前を格納
- spElmntID : Single line text(必須&一意) 各要素の名前の重複を許可するために作成
- spParentID : Lookup このリストのspElmntIDを参照する列
- spPreantID:Title : Lookup列の結果のspElmntName
- spElmntLv : Choice 1~4までのレベル(階層)を指定する列

PowerApps側のCollection

データソースからTreeView Controlを作成するために、PowerApps側で中間の加工データ (Collection)を作成します。
image.png
元のデータに加えて、TreeViewの開閉のために"Show"という列を追加します。
また、正しくソートするために、"TreePath"という列を追加します。
今回のTreeViewはPowerAppsのGalleryコントロールを利用しますが、そのItemsにはSortByColumns(XXXX,"TreePath",Ascending)を指定します。
具体的にはCollectionを作成するためのアクション(ボタンのOnSelectやスクリーンのOnVisible)に以下のコードを設定します。

XXX.OnXXX
ForAll(
    TreeViewElement,
    Collect(
        colTreeElmnt,
        {
            ElmntName: Title,
            ElmntId: spElmntID,
            Lv: Value(spElmntLv.Value),
            ParentId: spParentID.Value,
          //Expand/collapseを制御するための列'Show'を追加  
            Show: If(
                Value(spElmntLv.Value) = 1,
                true,//初期状態ではspElmntLv=1の要素のみ表示
                false
            ),
            TreePath: ""//TreePathは次に定義

        }
    )
);
//TreePathを定義する
UpdateIf(
    colTreeElmnt,
    Lv = 1,
    {TreePath: ElmntId},
    Lv = 2,
    {TreePath: Concatenate(ParentId,">",ElmntId)},
    Lv = 3,
    {TreePath: 
    //Lv3のTreePathは、自分のParendIdと元のデータソースのspElmntIDが一致するレコード(自分の親レコード)のspParentIDを利用する
      Concatenate(LookUp(TreeViewElement,spElmntID = ParentId,spParentID.Value),">",ParentId,">",ElmntId)
    },
    Lv = 4,
    {TreePath:
    //Lv4のTreePathは、Lv3の手順で得られたspParentIDと、元のデータソースのspElmntIDが一致するレコード(自分の親の親のレコード)のspParentIDを利用する
      Concatenate(
          LookUp(TreeViewElement,
            spElmntID = LookUp(TreeViewElement,spElmntID = ParentId,spParentID.Value),
            spParentID.Value),
          ">",LookUp(TreeViewElement,spElmntID = ParentId,spParentID.Value),">",ParentId,">",ElmntId
      )
    }
)

TreePathの構成は少し複雑ですが、
自分のLevelが1なら、自分自身のID。Levelが2なら、自分と親要素のIDをConcatenateで結合。
Levelが3なら、自分の親要素(spParentID)を使ってリストのspElmntIDを検索し、一致したレコードのspParentIDを結合
というように、自分の要素のLevelに応じてTreeのたどり方を指定しています。

このようにして生成されたCollectionをGalleryに表示すると以下のようになります。
image.png
ここで、GalleryのItemsプロパティは以下のように設定しました。

Gallery.Items
SortByColumns(colTreeElmnt,"TreePath",Ascending)

ここまででほとんど完成です。あとは要素の開閉やインデントの調整をしていきます。

Gallery Control

事前に名前の前に付ける画像を用意しておきます。
image.png
子要素がある場合にはplus/minusを、子要素がない場合にはdotを表示させます。
image.png
あとは設定をしていくだけなので、各プロパティを順に記載します。

Gallery.Items
//Show=trueだけを表示する
SortByColumns(Filter(colTreeElmnt,Show=true),"TreePath",Ascending)
Image2.Image
If(
    CountRows(
        Filter(
            colTreeElmnt,
            ParentId = ThisItem.ElmntId
        )
    ) = 0,
    dot,//自分のIDがを親要素IDにセットされているレコードがなければdot
    LookUp(
        colTreeElmnt,
        ParentId = ThisItem.ElmntId
    ).Show = true,
    minus,//子要素が表示されていればminus
    plus//子要素が表示されていなければplus
)
Image2.OnSelect
If(
    //自分のIDを親IDとしてもつレコードが0でなく、それらが表示されているか判定
    CountRows(Filter(colTreeElmnt,ParentId=ThisItem.ElmntId))<>0&&LookUp(colTreeElmnt,ParentId=ThisItem.ElmntId).Show= true,
    //表示されていれば、自分のIDをTreePathに持っている列をすべて非表示にして、自分を再表示
    UpdateIf(colTreeElmnt,CountRows(Filter(Split(TreePath,">"),Result=ThisItem.ElmntId))<>0,{Show: false });UpdateIf(colTreeElmnt,ElmntId=ThisItem.ElmntId,{Show: true}),
    //表示されていなければ、自分を親とする要素(直下の要素)を表示
    UpdateIf(colTreeElmnt,ParentId=ThisItem.ElmntId,{Show:!Show})
)

ポイントは、Lv1やLv2の場合、自分の下にある要素全てを閉じるために、
UpdateIf(colTreeElmnt,CountRows(Filter(Split(TreePath,">"),Result=ThisItem.ElmntId))<>0,{Show: false })
このコードを入れていることです。

あとはインデントの調整だけですが、

Image2.X
ThisItem.Lv*40-30
Title3.X
30+ThisItem.Lv*40

このようにレベルに応じてずらしています。

最後に

何かわからないことがあればmofumofu_danceまでご連絡ください。

Why do not you register as a user and use Qiita more conveniently?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away