[power apps]コンポーネントのカスタムプロパティをC#に置き換えて理解する

やりたいこと

Power Appsにはカスタムプロパティがあるが、なかなか直感的に理解できないためC#のソースコードに置き換えてイメージをしやすくしてみる。
また、C#自体久しく触ってないので、もしかしたら文法に誤りがあるかもしれないが、雰囲気だけでも伝わってもらえれば幸いだ。

Power Appsのコンポーネントと、それを呼び出すアプリをそれぞれ「Component」クラスと「App」クラスで表現するものとする。

カスタムプロパティの種類と事前準備

カスタムプロパティは初期状態では種類を選択することができない。
デフォルトでデータ型が選ばれている状態である。

そのため、Power Apps > 左メニュ > 近日公開の機能 > 試験段階 > 拡張コンポーネントのプロパティをONにする必要がある。

ONにすると以下のようにプロパティの型から「データ」「関数」「イベント」「アクション」を選択できるようになる。

プロパティの型:1.データ

データプロパティは文字通り数値や文字列などのデータを保持するプロパティである。

イメージ ソースコード

/** コンポーネント */
public class Component
{
    // スライダーオブジェクト
    private Slider mSliderA;

    // 「プロパティの定義:出力」
    public string mSliderValue {get { return mSliderA.value; }};
    
    // 「プロパティの定義:入力」
    public int mSliderWidth {set; get;}
  
    // コンストラクタ ・・・3
    public Component(int width = 200) {
        mSliderWidth = width;
    }
}
/** アプリケーション */
public class App
{
    public void Run()
    {
        var comp = new Component(300);

        // 「プロパティの定義:出力」
        // アプリからプロパティを参照する(GET)・・・1
        string sliderValue = comp.mSliderValue;
       
        // 「プロパティの定義:入力」
        // アプリからプロパティを参照する (GET)・・・2
        int sliderWidth = comp.mSliderWidth;
     // アプリからプロパティの設定をする (SET)・・・3
        comp.mSliderWidth = 300;
        
    }
}

Power Apps 画面上での定義

解説

  1. プロパティの定義が「出力」の場合、アプリケーションからデータプロパティの値を取得することができる。つまり、「出力」データプロパティはgetterであると考えることができる。また、アプリケーションからコンポーネント上のオブジェクト(今回の例ではSliderA)へ直接アクセスをすることができない(Component1_1.mSliderAは参照不可)。ゆえにコンポーネント上のオブジェクトの情報を取得は必ずデータプロパティを経由して取得しなければいけない。
    一方でアプリケーションから「出力」データプロパティを書き換えることはできないのでsetterは定義されていないということになる。
  2. プロパティの定義が「入力」のでも、同様にgetterを持つ。
  3. プロパティの定義が「入力」の場合、アプリケーションから「入力」プロパティを設定することができる。
    これはsetterを持つと考えることもできるし、コンストラクタで初期値を設定できるとみなすこともできる。ただし、設定は必須ではないため、引数はデフォルト引数であり、引数が省略されればコンポーネント側で設定していた初期値が採用されることになる。

活用場面

コンポーネント上の値を取得(Get)したいとき、コンポーネントに初期値を設定(Set)したいとき。
データプロパティについては関してはそんなに難しくないと思う。

プロパティの型:2.関数(出力)

イメージ ソースコード

/** コンポーネント */
public class Component
{
    // 現在時刻テキスト
    private string mTextHour;
    private string mTextMinute;
    private string mTextSecond;

    // 「プロパティの定義:出力」
  // メソッドには引数を渡すことができる。・・・1
  // デフォルト引数でも可        ・・・2
    public string GetCurrentTime(boolean addColon, string endWord = "だニャン") {
        if (addColon) {
            return mTextHour & ":" & mTextMinute & ":" & mTextSecond & endWord;
        } 
        return mTextHour & mTextMinute & mTextSecond & endWord;
    }
}
/** アプリケーション */
public class App
{
    public void Run()
    {
        var comp = new Component(300);
        // コンポーネントのメソッドを呼び出す。
        // ・・・3
        string currentTime1 = comp.GetCurrentTime(true);
        // ・・・4
     string currentTime2 = comp.GetCurrentTime(false, "でござるよ");        
    }
}

Power Apps 画面上での定義

コンポーネント定義

アプリでの参照

解説

  1. コンポーネントで関数プロパティの定義を行っている。
  2. 関数に渡される引数「AddColon」「EndWord」に初期値を持たせることができる。とはいえ、AddColonは必須の引数であるため、その初期値が利用されることはない。
  3. アプリケーションに設けたラベルオブジェクトから関数を呼んでいる。第2引数が省略されているので「EndWord」の初期値である”だニャン”が利用される。
  4. アプリケーションに設けたラベルオブジェクトから関数を呼んでいる。第2引数を渡しているので「EndWord」の初期値である”だニャン”は利用されない。

定義プロパティが「出力」の関数は、C#における関数とほとんど同じである。
違いとして、動的プロパティ(Set()など)呼び出しをすることはできないという制約がある。

また、データプロパティとの違いは引数を設けられるかどうかである。引数がないのであれば、その関数プロパティはデータプロパティと同じである。

活用方法

動的プロパティの呼び出しができない = C#でいうところの変数の更新ができないとなるとできることはかなり限られてしまう。

実用例としては、自作の汎用関数ライブラリの作成あたりだろうか(それしかぱっと出てこない)

例えば、全角を半角に変換する「ConvertWideToHalf(str)」や文字列の空白の数をカウントする「CountSpace(str)」など

プロパティの型:2.関数(入力)

イメージ ソースコード

/** コンポーネント */
public class Component
{
    // コールバック関数
    public delegate string GetRank(int score);
    private GetRank mGetRankMethod {set;}

    // 評価ボタン
    private Button mJudgeButton;
    private Text mScoreText;

    // コンストラクタ
    public void Component(GetRank method = null) {
        // イベントの登録
     // ・・・4
        mJudgeButton.OnSelect = JudgeEvent;

        if (method != null) {
            mGetRankMethod = method;
        } else {
            // コールバックが渡されなかったときの
            // デフォルトの挙動を定義する
            // ・・・1
            mGetRankMethod = (score) => {
                if (score => 95) 
                {
                    return "A";
                } else if (score >= 80) {
                    return "B";
                } else if (score >= 70) {
                    return "C";
                } else {
                    return "D";
                }
            };
        }
    }

    // 評価イベント
    // ボタンがクリックされたときに実行されるメソッド
    // ・・・2
    private JudgeEvent() {
        // テキストの文字列をスコアとする
        int score = mScoreText.Text;
        // スコアによってランク付けをする
        string rank = mGetRankMethod (score);
        switch (rank) {
            case "A":
                Console.WriteLine("すごいね");
                break;
            case "B":
                Console.WriteLine("まずまずだね");
                break;
            case "C":
                Console.WriteLine("ギリ...なんとか...");
                break;
            case "D":
                Console.WriteLine("単位は...あげません!!!!");
                break;
        }
    }
}
/** アプリケーション */
public class App
{
    public void Run()
    {
        // アプリ側で評価方法を定義する
     // ・・・3
        var comp = new Component((score) => {
            if (score => 80) 
            {
                return "A";
            } else if (score >= 60) {
                return "B";
            } else if (score >= 40) {
                return "C";
            } else {
                return "D";
            }
        });
    }
}

Power Apps 画面上での定義

コンポーネント定義

アプリでの参照

解説

  1. コンポーネント内で関数プロパティのデフォルトの挙動を定義している。関数プロパティは必ずしもアプリ側から定義する必要はなく、定義されない場合コンポーネントの定義でそのまま動作する。
  2. コンポーネント内であれば、どこでも関数プロパティを呼び出すことができる。
  3. アプリケーション側で関数プロパティの再定義を行っている。
  4. 50点はデフォルトの定義ではランクDだが、アプリケーション側の定義に則ってランクCのメッセージが表示されている。80点の時も同様。

定義プロパティが「入力」の関数は、一言でいえばコールバック関数である。データプロパティと同様でタイプが「入力」であれば、アプリ側から定義することできる。

データプロパティが「値」の定義に対して関数プロパティは「メソッド」の定義ができるため、アプリごとに「挙動」を変えたい場合に利用をすることができる。

こう書くとテクニカルな実装が出来そうな気がするが、やはり動的プロパティの呼び出しができないために出来ることがかなり限られるため、正直使いどころが難しい。

活用方法

う~ん…

 

プロパティの型:3.アクション

 

プロパティの型:4.イベント

 

 

 

 

コンポーネントの関数プロパティまたはパラメータには、コンポーネント内または
コンポーネント出力プロパティからのみアクセスできます。