はじめに
前回の記事(その5)では、Select() と Button.OnSelect を「呼び出し可能な処理ブロック」の置き場所として整理しました。Part 4では、OnVisibleに「何を置くか」を整理しています。今回はナビゲーション(画面遷移)に目を向けます。
この記事で伝えたいこと
-
Back()ではなくNavigate(locFromScreen)でバックすると、「前の画面」を呼び出し側が選べる - 画面スタックに頼らない明示的なナビゲーションになる
前提
- Power Apps のCanvasアプリ
- 多少泥臭くても、画面遷移をわかりやすく意図が伝わる形で管理したい
- タブはタブコントロールではなく、あえて画面を分けて作成
- AIと協働しながら、より良い設計パターンを試行錯誤中
今回想定する画面構成はこちら。詳細画面を「基本」「詳細」の2タブで実装したケースです。
タブは見た目上1つの画面ですが、実態は2つの独立した画面をNavigateで切り替えています。
Back() だとどうなるか
Power Appsには、前の画面に戻る関数として Back() があります。シンプルで便利ですが、タブ切り替えが絡むと意図しない動作になります。
直線的な遷移では問題ない
MainScreen
↓ Navigate(DetailInfoScreen, ...)
DetailInfoScreen
↓ Back() → MainScreen ✅
この直線的な遷移なら Back() は問題ありません。
タブ切り替えが絡むと
MainScreen
↓ Navigate(DetailInfoScreen, ...)
DetailInfoScreen
↓ Navigate(DetailMoreScreen, ...) ← タブ切り替え
DetailMoreScreen
↓ Back() → DetailInfoScreen ❌(タブに戻ってしまう)
↓ Back() → MainScreen(もう一回押してようやく戻れる)
ユーザーは「詳細を閉じてメインに戻る」つもりで戻るボタンを押したのに、Back() はタブの切り替え履歴を1段巻き戻してしまいます。
Back() は暗黙のスタックに頼っているため、「タブ切り替えも履歴として積まれる」という事情がコードから読み取れません。
💡 Back()とNavigate()の動作の違い
Back() は Power Apps が内部で管理する「画面スタック」を1つ巻き戻します。どこから来たかはスタックが知っていて、コードには現れません。
タブ切り替えも Navigate() で実装している以上、スタックには「DetailInfoScreen → DetailMoreScreen」という履歴が積まれます。ユーザーには「タブを変えただけ」でも、Back() の視点では「画面遷移した」として扱われます。
一方、Navigate(locFromScreen) のように戻り先を明示すれば、「タブ切り替えではなくメインに戻る」という意図をコードに表現できます。
画面参照を渡して、戻り先を明示する
遷移元の画面を変数に乗せて次の画面に渡します。タブ切り替えのときはその変数をそのまま引き継ぎます。 戻るときは受け取った変数を使って Navigate() します。
ここで渡しているのは文字列ではなく「画面参照(Screen)」なので、画面名の文字列を自前で組み立てたりせずにシンプルに管理できます。
画面遷移時:遷移元を渡す
// ✅ MainScreen のリスト行タップ
Navigate(DetailInfoScreen, ScreenTransition.Fade, {locFromScreen: MainScreen})
// → DetailInfoScreen.locFromScreen = MainScreen
タブ切り替え時:locFromScreen をそのまま引き継ぐ
// ✅ DetailInfoScreen の詳細タブボタン
Navigate(DetailMoreScreen, ScreenTransition.None, {locFromScreen: locFromScreen})
// → DetailMoreScreen.locFromScreen = MainScreen(引き継がれる)
// ✅ DetailMoreScreen の基本タブボタン
Navigate(DetailInfoScreen, ScreenTransition.None, {locFromScreen: locFromScreen})
// → DetailInfoScreen.locFromScreen = MainScreen(引き継がれる)
戻るボタン:受け取った参照で戻る
// ✅ 戻るボタン(どちらのタブにも同じコードを置く)
Navigate(locFromScreen, ScreenTransition.Fade)
// DetailInfoScreen.locFromScreen = MainScreen → MainScreen ✅
// DetailMoreScreen.locFromScreen = MainScreen → MainScreen ✅
ポイント
-
タブ切り替えで効いているのは「引き継ぎ」:
{locFromScreen: locFromScreen}と書くことで、遷移先タブにも「元々どこから来たか」が伝わる -
戻るボタンは全画面で同じコード:遷移経路が変わっても
Navigate(locFromScreen, ...)は変えなくてよい -
戻り先がコードに明示される:
locFromScreenを追えば、どこに戻るかが読める
参考(公式:Microsoft Learn)
なぜこれで動くのか:コンテキスト変数のスコープ
「各画面に locFromScreen という同じ名前の変数があるのに、なぜ混ざらないのか?」
これが今回サンプルアプリを作って確かめたかったことです。
Power Apps の変数3種とスコープ
| 種類 | 関数 | スコープ | ライフタイム |
|---|---|---|---|
| グローバル変数 | Set(gblVar, value) |
アプリ全体 | アプリが起動している間 |
| コンテキスト変数 | UpdateContext({locVar: value}) |
画面単位 | 画面がメモリに存在する間 |
| コレクション変数 | ClearCollect(colData, ...) |
アプリ全体 | アプリが起動している間 |
コンテキスト変数は画面単位のスコープを持ちます。
MainScreen の locFromScreen と DetailScreen の locFromScreen は、名前が同じでもまったく別の変数です。
Navigateの第3引数とコンテキスト変数の関係
Navigate() の第3引数は遷移先画面のコンテキスト変数を初期化する仕組みになっています。
// MainScreen のリスト行タップ
Navigate(DetailInfoScreen, ScreenTransition.Fade, {locFromScreen: MainScreen})
// → DetailInfoScreen.locFromScreen = MainScreen が設定される
// DetailInfoScreen のタブ切り替えボタン
Navigate(DetailMoreScreen, ScreenTransition.None, {locFromScreen: locFromScreen})
// locFromScreen の値(= MainScreen)がそのまま渡される
// → DetailMoreScreen.locFromScreen = MainScreen が設定される
この状態で戻るボタンを押すと:
// DetailInfoScreen の戻るボタン
Navigate(locFromScreen, ScreenTransition.Fade)
// DetailInfoScreen.locFromScreen = MainScreen → MainScreen に戻る ✅
// DetailMoreScreen の戻るボタン
Navigate(locFromScreen, ScreenTransition.Fade)
// DetailMoreScreen.locFromScreen = MainScreen → MainScreen に戻る ✅
DetailInfoScreen と DetailMoreScreen それぞれが独立して locFromScreen を持っているため、タブを切り替えても値が混ざりません。
アプリ作って確かめたこと
サンプルアプリを作り、上記のパターンを実装して検証しました。
- MainScreen → DetailInfoScreen で入った場合:基本タブの戻るボタンで MainScreen に戻る ✅
- タブ切り替え(DetailInfoScreen → DetailMoreScreen)後:詳細タブの戻るボタンでも MainScreen に戻る ✅
コンテキスト変数が画面ごとに独立しているため、タブ間で locFromScreen が混ざらず、意図通りに動きました。
💡 変数スコープを意識すると設計はどう変わるか
コンテキスト変数のスコープを知ると、変数名の設計指針が変わります。
グローバル変数(gbl プレフィックス)
アプリ全体で使うデータ。ログインユーザー情報、アプリ全体の設定値など。どこからでも読み書きできるため、多用すると変数の変更元を追いにくくなる。
コンテキスト変数(loc プレフィックス)
その画面だけで使うデータ。画面の表示状態、入力フォームの一時保存、locFromScreen のような画面固有の制御変数。他の画面に影響しないため、名前が被っていても安全。
locFromScreen という命名を全画面で使い回せるのは、このスコープの特性があるからです。もしグローバル変数(gblFromScreen)で同じことをしようとすると、各画面で遷移のたびに上書きが必要になり、複数画面が連動するときに競合が起きます。
補足:この命名規則は Microsoft 公式ガイドラインでも定められている
loc / gbl のプレフィックス区別は、Power Apps コーディングガイドライン(Microsoft Learn) で推奨されている命名規則です。
Power Apps ではコンテキスト変数とグローバル変数に同じ名前を使えてしまい、数式では「コンテキスト変数が優先される」という仕様があります。混乱を避けるために、公式もプレフィックスによる区別を推奨しています。
| 種類 | プレフィックス | 例 |
|---|---|---|
| コンテキスト変数 | loc |
locSuccessMessage |
| グローバル変数 | gbl |
gblFocusedBorderColor |
| コレクション | col |
colMenuItems |
まとめ
| パターン | 戻り先 | コードの明示性 |
|---|---|---|
Back() |
暗黙のスタックに依存 | 低い(どこに戻るかコードに出ない) |
Navigate(locFromScreen) |
Navigate時に渡した画面 | 高い(遷移元がコードで追える) |
「前の画面に戻る」という操作を、Back() から Navigate(locFromScreen) に変えるだけで、ナビゲーションの制御がコードに現れるようになります。
これが成立するのは、コンテキスト変数が画面ごとに独立しているから。同じ locFromScreen でも画面が違えば別の値を持てる。この特性が、画面が増えても「戻る」のコードを使い回せる形にしています。
シリーズ一覧
- その1:画面のことは画面自身に任せる
- その2:Navigation Contextパターン
- その3:Execution Planパターン
- その4:OnVisibleに全部置かない。データのライフサイクルで配置を決める
- その5:Button.OnSelectを「呼び出し可能な処理ブロック」の置き場所として使う
- その6:ナビゲーションをコントロールする。コンテキスト変数のスコープをおさらい(本記事)
