20201019のC#に関する記事は7件です。

IAM認証のAWS API GatewayにC#からIAMロールでSigV4署名してアクセスするには

IAM認証を使っているAWSのAPI Gatewayは、APIリクエスト時にSigV4署名が必要です。

前回の記事では、IAMユーザで認証するAPI Gateway呼び出しをC#で書きました。

今回は、EC2インスタンスにアタッチされているIAMロールでの認証です。

前提

IAMロールのアタッチされているEC2インスタンスでC#のコードを実行します。~/.aws/config に以下のようにIAMロールが指定されているものとします。

[profile default]
role_arn = arn:aws:iam::999999999999:role/ROLENAME
credential_source = Ec2InstanceMetadata

API GatewayのリソースポリシーにはこのIAMロールからのAPIアクセスを許可してあるものとします。

動作確認した環境はUbuntu 20.04です。

C#の環境は以下の通り。

$ dotnet --version
3.1.402

以下は私の記事でして、このとおりC#をほとんど始めて触っています。C#の流儀と違うところがあったらごめんなさい。

サンプルコードダウンロード

SigV4署名するC#のサンプルコードはAWS公式サイトにありますので、それをダウンロードし、必要なディレクトリのみ残します。

ここの手順の詳細は前回の記事を参照。

$ mkdir sample
$ cd sample
$ mkdir tmp
$ cd tmp
$ wget https://docs.aws.amazon.com/AmazonS3/latest/API/samples/AmazonS3SigV4_Samples_CSharp.zip
$ unzip AmazonS3SigV4_Samples_CSharp.zip
$ cd ..
$ mv tmp/AWSSignatureV4-S3-Sample/Signers ./
$ mv tmp/AWSSignatureV4-S3-Sample/Util ./
$ rm -r tmp

サンプルソースコードのnamespaceを全置換しておきます。(このコマンドの説明は sedコマンドでディレクトリ内の全ファイルをテキスト全置換するには)

$ grep -rl AWSSignatureV4_S3_Sample Signers | xargs sed -i 's/AWSSignatureV4_S3_Sample/Sample/g'
$ grep -rl AWSSignatureV4_S3_Sample Util | xargs sed -i 's/AWSSignatureV4_S3_Sample/Sample/g'

C#のプロジェクト作成

dotnetコマンドでプロジェクトを作成します。

$ dotnet new console

以下のようなディレクトリ構成になります。

$ tree
.
├── obj
│   ├── project.assets.json
│   ├── project.nuget.cache
│   ├── sample.csproj.nuget.dgspec.json
│   ├── sample.csproj.nuget.g.props
│   └── sample.csproj.nuget.g.targets
├── Program.cs
├── sample.csproj
├── Signers
│   ├── AWS4SignerBase.cs
│   ├── AWS4SignerForAuthorizationHeader.cs
│   ├── AWS4SignerForChunkedUpload.cs
│   ├── AWS4SignerForPOST.cs
│   └── AWS4SignerForQueryParameterAuth.cs
└── Util
    └── HttpHelpers.cs

3 directories, 13 files

sample.csprojに以下のように RootNamespace の項目を追加します。サンプルダウンロード後に全置換したnamespaceを指定します。

<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>netcoreapp3.1</TargetFramework>
    <RootNamespace>Sample</RootNamespace>
  </PropertyGroup>

</Project>

必要なパッケージをダウンロードします。

$ dotnet add package AWSSDK.SecurityToken

C#のソースコード

Program.cs は以下です。

using System;
using System.Collections.Generic;
using System.Threading.Tasks;

using Amazon.Runtime;
using Amazon.Runtime.CredentialManagement;
using Amazon.SecurityToken;
using Amazon.SecurityToken.Model;

using Sample.Signers;
using Sample.Util;

namespace Sample
{
    class Program
    {
        private static async Task Run()
        {
            // ~/.aws/credentials からRoleArnを読み取る
            SharedCredentialsFile sharedFile = new SharedCredentialsFile();
            sharedFile.TryGetProfile("default", out CredentialProfile credentialProfile);
            string roleArn = credentialProfile.Options.RoleArn;

            // IAMロールにassumeする
            InstanceProfileAWSCredentials instanceCredentials = new InstanceProfileAWSCredentials();
            AmazonSecurityTokenServiceClient stsClient = new AmazonSecurityTokenServiceClient(instanceCredentials);
            AssumeRoleRequest assumeRoleRequest = new AssumeRoleRequest
            {
                RoleArn = roleArn,
                RoleSessionName = "test_session",
            };
            var assumeRoleResponse = await stsClient.AssumeRoleAsync(assumeRoleRequest);
            var credentials = assumeRoleResponse.Credentials;

            var uri = new Uri("https://xxxxxxxxxx.execute-api.ap-northeast-1.amazonaws.com/Prod/hello");

            // 署名するためのソースとなるヘッダ情報
            var headers = new Dictionary<string, string>
            {
                {AWS4SignerBase.X_Amz_Content_SHA256, AWS4SignerBase.EMPTY_BODY_SHA256},
                {"content-type", "text/plain"},
                {"x-amz-security-token", credentials.SessionToken}, // IAMロールではこれが必要
            };

            // 署名を作成
            var signer = new AWS4SignerForAuthorizationHeader
            {
                EndpointUri = uri,
                HttpMethod = "GET",
                Service = "execute-api",
                Region = "ap-northeast-1"
            };
            var authorization = signer.ComputeSignature(headers,
                                                        "",   // no query parameters
                                                        AWS4SignerBase.EMPTY_BODY_SHA256,
                                                        credentials.AccessKeyId,
                                                        credentials.SecretAccessKey);

            // リクエストヘッダに署名を追加
            headers.Add("Authorization", authorization);

            // リクエスト実行
            // HttpHelpers はUtilで定義
            HttpHelpers.InvokeHttpRequest(uri, "GET", headers, null);
        }

        static void Main(string[] args)
        {
            Run().Wait();
        }
    }
}

uriはAPI GatewayのAPIのURLを入れます。

実行

以下のコマンドで実行できます。

$ dotnet run

ダウンロードしたサンプルコードのSignersUtilにデバッグ用出力があるので、いろいろ表示されますが、最後にAPI Gatewayからのレスポンスが表示されます。

情報源

C#でIAMロールにAssumeする方法がわからず、今回のテーマは難儀でした。わかってしまえば大したことないのですが。

参考にした情報は、前回の記事に加えて、AWS SDK for .NETのAPIレファレンスとPython boto3のソースコードと、参考としてawscurlというコマンドのソースコードです。

AWS SDK for .NETのドキュメントは、説明が少なく、メソッドのシグニチャはわかってもどう使ったらいいのかがわかりませんでした。IAMロールでの署名をしているはずのawscurlのソースコードを読み、awscurlが呼び出しているboto3のソースコードも読むことで、AWSのAPIをどう呼び出しているのかを把握し、同じことをC#で書くことで実装できました。

boto3はAWSのAPIを呼び出しているだけだと思っていましたが、使いやすいように多くの機能をPythonで実装していることを実感しました。boto3とは違って、AWS SDK for .NETは単にAWS API呼び出しをそのままメソッドにしているだけのように見えます。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Unity】Randomまとめ

はじめに

ゲーム製作においてRandomを使用する場面は多いと思います。
備忘録としてRandomクラスをまとめてみました。
なお、本記事ではUnityEngine.Randomについて記載しています(System.Randomではない)。

Static Properties

insideUnitCircle

半径1の円の内部の点をランダムに返す。
円形上にランダムに敵を出現させたい場合などに使用。
image.png

insideUnitSphere

半径1の球体の内部の点をランダムに返す。
球体内部全てなので、浮いてるオブジェクトなどにも使用可能。
スクリーンショット 2020-10-19 22.12.18.png

onUnitSphere

半径1の球体の表面上の点をランダムで返す。
スクリーンショット 2020-10-19 22.14.24.png

rotation

ランダムなQuaternionを返す。
スクリーンショット 2020-10-19 22.16.37.png

rotationUniform

一様な分布でランダムにQuaternionを返す。
Random.rotationでは完全なランダムではないみたいです。ただ計算コストはこちらの方が高い。

state

乱数生成器の内部状態のすべてを取得、または、設定する。
適当なタイミングでアプリを落としても、再度起動したときに続きからできるようにするために乱数を保存する。

value

0.0と 1.0の範囲のランダムな数を返す。
0.0と1.0も含むことに注意。汎用性が高く、Randomクラスのプロパティで基本的に使うのはこれ。

Static Methods

ColorHSV

HSVでアルファ値(透明度)をもつランダムなColorを返す。
パラメータで色相(hueMin,hueMax)、彩度(saturationMin,saturationMax)、明度(valueMin,valueMax)、アルファ値(透明度:alphaMin,alphaMax)を設定可能。
スクリーンショット 2020-10-19 22.51.41.png

InitState

シード値を指定し、ランダム数生成器の状態を初期化する。
システム時刻等で決定されるシード値を指定することで、同じ乱数の値を生成することができる。
シード値による擬似乱数については、以下の記事がわかりやすいです。
コンピューターの「ランダム」は本当にランダムなのか?

Range

範囲を指定できる、Random.value
intで設定するか、floatで設定するかによって、戻り値の範囲に差がある点に注意。

int設定
\text{min}\leqq \text{return} < max
float設定
\text{min}\leqq \text{return} \leqq max

まとめ

  • Randomクラスは様々な乱数を生成するクラス。
  • Random.valueRandom.Rangeの使用頻度が圧倒的に高い。
  • Random.rotationRandom.rotationUniformの違いは後日検証したい...
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Kafka Trigger のロギングを改善する

自分がメンテをしている Azure Functions Kafka Extensionというライブラリがある。実際に運用してみると、ロギングが不十分であることに気が付いた。

Azure Functions Kafka Extensions は、 Confluent.Kafka の C#ライブラリを使っている、さらにその内部でネイティブライブラリである Libkafkaが使用されている。

通常の実行ログは実行されるが、Libkafkaの方で何か問題が出たとき、例えば Maximum application poll interval exceeded が出たあと、Auto-commit をかけようとして失敗しているメッセージが LibKafka から出ているが、ゴリゴリに Standard Output から出ている。運用もしている側からするとこれはいただけない。

しかも、先の Libkafka のライブラリの issue を見ていると、問題が発生したら作者が Debug オプションを On にしてとのたまうことが多い。これは、Debug を有効にしたい。条件としてはこの通り。

  • Libkafka のデバッグの有効化設定をできるようにしたい
  • Libkafka 由来の Standard Output に出ているログをちゃんとしたログに流したい

上記をやってみた。

デバッグの有効化

Libkafka のデバッグの有効化は簡単だ。こちらのとおり、debug という設定をしてあげると良い。

Consumer

debug = broker,topic,msg

Producer

debug = consumer,cgrp,topic,fetch

全オプション

all という凶悪なオプションまである。

generic, broker, topic, metadata, feature, queue, msg, protocol, cgrp, security, fetch, interceptor, plugin, consumer, admin, eos, mock, all

Confluent.Kafka での実装

Debug の追加

さて、こちらのConfluent.Kafka ではどうするか?

KafkaOptions というクラスがあり、これが Kafka の host.json の項目になる。ここに書けば host.jsonから自動で読んでくれる。Default は空文字ではうまくいかなかったので、null にした。

        /// <summary>
        /// Gets or sets the debug option for librdkafka library.
        /// Default = "" (disable)
        /// 
        /// Librdkafka: debug
        /// </summary>
        public string LibkafkaDebug { get; set; } = null; 

こちらを、ConsumerConfig と ProducerConfig に足すだけでよい。これらのクラスのスーパークラスが ClientConfig であり、そこに、単純に Debug に代入するだけでよい。簡単や。これが、Libkafka のライブラリに設定を渡してくれる。

KafkaListener L152

            ConsumerConfig conf = new ConsumerConfig()
            {
                // enable auto-commit 
                EnableAutoCommit = true,

                // disable auto storing read offsets since we need to store them after calling the trigger function
                EnableAutoOffsetStore = false,

                // Interval in which commits stored in memory will be saved
                AutoCommitIntervalMs = this.options.AutoCommitIntervalMs,

                // Librdkafka debug options               
                Debug = this.options.LibkafkaDebug,

Producer の方は KafkaProducerFactory L131 だけど同じようなものだから省略

Log のリダイレクト

さて、現在 Standard Output に垂れ流されている libkafka のログどうやって盗んだらええねん、、、と思ってたけど、欲しいの自分だけやないよねということで、あっさりとライブラリにHookが存在した。こんな感じ。

KafkaProducerFactory L76

            var builder = new ProducerBuilder<byte[], byte[]>(producerConfig);
            ILogger logger = this.loggerProvider.CreateLogger("Kafka");
            builder.SetLogHandler((_, m) =>
            {
                logger.LogInformation($"Libkafka: {m?.Message}");
            });

            return builder.Build();

Consumer も似たようなもので、KafkaListener L107 だ。省略

この SetLogHandler で、Action を渡す。
image.png
IConsumer と、LogMessage が渡ってくる。IConsumer には様々なメタ情報が入っているが、私が今欲しいのはログのメッセージだけなので、
一旦ここでは、LogMessage の中身だけ見てみる。ここでは、レベルとかも出てくるが、よくよく考えたらログレベルに合わせてInfomationだけではなく違うものに変えるようにしてもいいかもしれない。一旦ここではInfoにしておく。クエリーできるだけでも様様だ。レビューをお願いしているので、気に食わんかったらアドバイスがもらえるだろう。

image.png

気になるログの追加

さて、運用していると、ユーザーさんがFunctionをどちらのモードで使っているか?とか知りたいときがある。例えばKafka Trigger は、Function に定義されたパラメータが Array か否かでパラメータがシングルか、複数か決まり実行されるロジックが異なってくる。ただ、頻繁にはログを出したくない。であるので、コンストラクターのところに入れてみたのだが、実際は、Function名をとりたいところ。実際には、executor オブジェクトがFunction名を持っているのだが、そのライブラリの作者が実装クラスを internal にしているし、インターフェイスなので、リフレクションで取得するのは危険だろう。今はあきらめたがいい方法があれば教えてほしい。

SingleItemFunctionExecutor L22

        public SingleItemFunctionExecutor(ITriggeredFunctionExecutor executor, IConsumer<TKey, TValue> consumer, int channelCapacity, int channelFullRetryIntervalInMs, ICommitStrategy<TKey, TValue> commitStrategy, ILogger logger)
            : base(executor, consumer, channelCapacity, channelFullRetryIntervalInMs, commitStrategy, logger)
        {
            logger.LogInformation($"FunctionExecutor Loaded: {nameof(MultipleItemFunctionExecutor<TKey, TValue>)}");
        }

まとめ

これで一旦当初予定していたロギングの強化が完了した。ついでにライブラリもバージョンアップしておいた。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

ReactiveProperty v7.5.0 をリリースしました

最近、ビルド・単体テスト・パッケージング・NuGet への公開・GitHub Releases へのひな型作成まで GitHub Actions で自動化できたのでリリースが楽になりました。リリースノートに追加した機能や変更の一覧を自動で書いてくれるようなのも作りたいけど、どうにかできるんだろうか…

ということで v7.5.0 をリリースしました。

リリースノート
NuGet Gallery | ReactiveProperty

今回は 2 つ機能を追加しています。

ObserveProperty, ToReactivePropertyAsSynchronized, FromObject の機能強化

今までは ObservePropertyToReactivePropertyAsSynchronized でプロパティを指定するときに x => x.Name のような指定した変数の直接のプロパティしか対象にできませんでした。今回の更新で x => x.Child.Name のようなネストしたプロパティも指定可能になりました。

これを書いてて ReactiveProperty.FromObject だけ、これに対応するのを忘れていたので近いうちに v7.5.1 で対応します orz

2020/10/20 にリリースした v7.5.1 で ReactiveProperty.FromObject にも対応しました。

IFilteredReadOnlyObservableCollection に ObserveElementPropertyChanged などを追加

コレクションの要素のプロパティの変更を監視するための ObserveElementPropertyChangedObserveElementPropertyObserveElementObservablePropertyIFilteredReadOnlyObservableCollection<T> にも対応させました。

まとめ

ReactiveProperty.FromObject は、ネストしたプロパティにまだ対応していないので気を付けてください orz

ネストしたプロパティへの対応は久しぶりの割とメンドクサイ系の機能追加でしたが、無事実装出来たのでぜひ使ってみてください。問題点等ありましたら GitHub の issues までお願いします。日本語でも OK です。

https://github.com/runceel/ReactiveProperty

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

C#_メソッド

プログラミングを進める上で、何度も似たような処理を書くのは非効率的であり、ソースコードの可読性を下げる要因にもなります。
その為、何度も必要とされる処理は1度だけまとめて書いておくことで、それを複数の箇所から呼び出せるようにすることができます。これを "メソッド" と呼びます。

※他の言語では「関数」「サブルーチン」と呼ばれるものです。

メソッド

基本的なメソッドの書き方は次のようになります

Program.cs
// [修飾子] 戻り値 メソッド名(型 パラメータ1, 型 パラメータ2, ...)
int Add(int x, int y)
{
    // return 戻り値;
    return x + y;
}

メソッドは "パラメータ" として値を受け取ることができ、反対に "戻り値" として値を返すことができます。
また、 "return文" で戻り値として定義した型の値を返すことができます。

上記Addメソッドはint型の値を2つ受け取り、その和を戻します。

Program.cs
static void Main(string[] args)
{
    // 呼出し方は メソッド名(パラメータ, パラメータ, ...)
    Console.WriteLine(Add(1, 2));
    Console.WriteLine(Add(3, 5));
}

static int Add(int x, int y)
{
    return x + y;
}
出力
> 3
> 8

エントリポイントもメソッドであり、戻り値はなし(void)かつパラメータとしてstring型配列のargsを受け取っています。
※戻り値の前に付いている "static""修飾子" のひとつですが、これは "クラス" の話をするときにまとめて触れます。

値渡しと参照渡し

メソッドのパラメータとして値を指定する時、定義上のパラメータを "仮引数" 。メソッドの呼出し側が関数に渡す引数を "実引数" と呼びます。
※ただの用語で、あまり重要ではありません。

Program.cs
static void Main(string[] args)
{
    // 3, 5が実引数
    Console.WriteLine(Add(3, 5));
}
// x, yは仮引数
static int Add(int x, int y)
{
    return x + y;
}

上記のように単純にパラメータを指定した場合、 "値渡し" と言います。
この時、メソッド内部で仮引数の内容を変更したとしても、当然それは実引数側に反映されることはありません。

Program.cs
static void Main(string[] args)
{
    int x = 5;
    Temp(x);
    Console.WriteLine(x);
}
static void Temp(int x)
{
    x = 10;
}
出力
> 5

上記に対してパラメータの前にin/out/refを付けることで "参照渡し" を使用することができます。
このin/out/refなどは "パラメータ修飾子" と呼ばれます。

Program.cs
static void Main(string[] args)
{
    int x = 0;
    InMethod(in x);

    OutMethod(out x);
    Console.WriteLine(x);
    RefMethod(ref x);
    Console.WriteLine(x);
}
static void InMethod(in int x)
{
    // xへの代入はエラー
    // x = 1;
    Console.WriteLine(x);
}
static void OutMethod(out int x)
{
    // xが未割当てのままメソッドを抜けようとするとエラー
    // return;
    x = 2;
}
static void RefMethod(ref int x)
{
    x = 3;
}
出力
> 0
> 2
> 3

それぞれ少しずつ動作が異なるので表にまとめると以下のようになります。

修飾子 呼出し前の初期化 メソッド内での変更・代入
in 必須 不可
out 不要 必須
ref 不要 可能
  • in修飾子

    in修飾子は呼出し側で最初に値を代入し、初期化しておかなければ使用することができません。
    またメソッド内部ではin修飾子の付いたパラメータを変更することはできません。
    これはメソッドの呼出し側に「値が書き換わらないこと」を保証することになりますが、これは先述の値渡しと同じ動作になります。
    値渡しと違い、実引数→仮引数へのデータのコピーが発生しないので、巨大なデータを取り扱いやすいというメリットがあります。

  • out修飾子

    out修飾子は呼出し側でのパラメータの初期化を必要としません(初期化しても問題ではありませんが、メソッド内部での代入を強制するので意味がありません)。
    メソッド内部ではout修飾子のついたパラメータは何らかの値を代入する必要があり、その値をメソッドの呼出し側へ返却することになります。
    つまり、return文による戻り値とは別に値を返したい場合などに使用されます。

  • ref修飾子

    ref修飾子はメソッド呼出し側およびメソッド内部のどちらでも初期化や代入を強制することなく、自由に使用することができます。
    使い勝手は一番いいですが、反対にメソッド内部と呼出し側でやり取りされるデータの管理が煩雑になりやすいことが挙げられます。

上記は値型についての話でしたが、参照型についても同様に値渡しと参照渡しを行うことができます。
代表的な参照型のひとつである配列でその動きを見てみましょう。

Program.cs
static void Main(string[] args)
{
    int[] x = new int[] { 1, 2, 3 };
    int[] y = new int[] { 4, 5, 6 };
    Update(x, ref y);
    Console.WriteLine(x[0]);
    Console.WriteLine(y[0]);
}
static void Update(int[] x, ref int[] y)
{
    x[0] = 10;
    y[0] = 10;
}
出力
> 10
> 10

参照型は値型と違い、持っているデータはメモリ上の実体を指す参照アドレスである為、値渡しでも「アドレスのコピー」を渡すことになり、そのアドレスが指している実体は同じものになります。
なので、参照先の実体に対する変更は、値渡しと参照渡しのどちらでも同じような結果になります。

1.PNG

しかし一方で、メソッド内部で新たに参照を割当てた場合、値型と似たような挙動になります。

Program.cs
static void Main(string[] args)
{
    int[] x = new int[] { 1, 2, 3 };
    int[] y = new int[] { 4, 5, 6 };
    Update(x, ref y);
    Console.WriteLine(x[0]);
    Console.WriteLine(y[0]);
}
static void Update(int[] x, ref int[] y)
{
    x = new int[] { 10, 20, 30 };
    y = new int[] { 40, 50, 60 };
}
出力
> 1
> 40

値渡しの時に参照アドレスをコピーしましたが、そこに新たに別の参照先を割当てることで、メソッド内部での仮引数には別の実体2が割当てられました。
しかし実引数の方は変わらず元の実体1を指したままになります。

参照渡ししている方は値型と同様に、元の実引数に対して新たに参照先を割当てるため、メソッドの呼び出し側でも値が変更されていることが確認できます。

2.PNG

デフォルト引数

メソッドに渡すパラメータはデフォルトの値を設定しておくことが可能です。

これを "デフォルト引数" と呼びます

Program.cs
static void Main(string[] args)
{
    Output(3);
    Output(3, 7);
}
static void Output(int x, int y = 5)
{
    Console.WriteLine(x + y);
}
出力
> 8
> 10

デフォルト引数は呼出し側から指定しなかった場合にデフォルトの値となり、指定した場合にはその指定された値となります。
また、デフォルト引数はメソッド定義上、通常のパラメータよりも後ろにのみ設定することが可能です。

オーバーロード

基本的に同名のメソッドを複数定義することはできませんが、パラメータが異なれば別のメソッドとして認識されます。
これをメソッドの "オーバーロード" と呼びます。

Program.cs
static void Main(string[] args)
{
    Console.WriteLine(Add(2, 3));
    Console.WriteLine(Add(2.5, 3.2));
}
static int Add(int x, int y)
{
    return x + y;
}
static double Add(double x, double y)
{
    return x + y;
}
出力
> 5
> 5.7

パラメータの型や個数が異なればオーバーロードが可能です。
ただし、戻り値のみ異なる同名のメソッドではオーバーロードとしては認識されません。

メソッドについてはここまで。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

UWPでメッセージボックスを表示したいとき

UWPでMessageBox.Show()したいとき

各ダイアログの表示確認ようにUWPアプリ作りました。
ソースコード

MessageDialog

シンプルなメッセージ表示ダイアログです。

MessageDialogSample.cs
var dialog = new MessageDialog("コンテンツ", "タイトル");
_= dialog.ShowAsync();

こんな感じでモーダルで表示されます。
image.png
アラートなどユーザーに何かを伝えたいときに便利です。

ContentDialog

  • ボタン1つの場合
ContentDialogSample1.cs
var dialog = new ContentDialog()
{
    Title = "OK Dialog Here!!",
    Content = "Click Ok Button Dialog",
    CloseButtonText = "Ok"
};
 var result = await dialog.ShowAsync();

こんな感じ
image.png

  • ボタン2つの場合
ContentDialogSample2.cs
    var dialog = new ContentDialog()
    {
        Title = "Yes No Dialog Here!!",
        Content = "Click Yes No Button Dialog",
        PrimaryButtonText = "Yes",
        CloseButtonText = "No"
    };
    var result = await dialog.ShowAsync();

こんな感じ
image.png

  • ボタン3つの場合
ContentDialogSample3.cs
    var dialog = new ContentDialog()
    {
        Title = "3 Button Dialog Here!!",
        Content = "Click 3 Button Dialog",
        PrimaryButtonText = "Allow",
        SecondaryButtonText = "Delete",
        CloseButtonText = "Cancel"
    };
    var result = await dialog.ShowAsync();

こんな感じ
image.png

ContentDialogのShowAsync()の戻り値について

namespace Windows.UI.Xaml.Controls
{
    [ContractVersion(typeof(UniversalApiContract), 65536)]
    [WebHostHidden]
    public enum ContentDialogResult
    {
        None = 0,
        Primary = 1,
        Secondary = 2
    }
}

ContentDialogのプロパティに設定した値に対応するボタンが押された際、下記の戻り値を返却します。

プロパティ コマンド 戻り値
CloseButtonText CloseButtonCommand None
PrimaryButtonText PrimaryButtonCommand Primary
SecondaryButtonText SecondaryButtonCommand Secondary

また、それぞれコマンドプロパティも実装されているので、コマンドを作成してプロパティに設定することも可能です。

MessageDialogクラスがsealedクラスであるのに対してContentDialogクラスは継承できるのでレイアウトを拡張したり、好きなコントロールを配置可能なので使いやすいと思います。

応用 InputDialog

ContentDailogクラスを拡張して1つのテキストボックスを持つダイアログクラスを作成しました。
image.png

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

UWPでMessageBoxを表示したいとき

各ダイアログの表示確認ようにUWPアプリ作りました。
ソースコード

MessageDialog

シンプルなメッセージ表示ダイアログです。

MessageDialogSample.cs
var dialog = new MessageDialog("コンテンツ", "タイトル");
_= dialog.ShowAsync();

こんな感じでモーダルで表示されます。
image.png
アラートなどユーザーに何かを伝えたいときに便利です。

ContentDialog

  • ボタン1つの場合
ContentDialogSample1.cs
var dialog = new ContentDialog()
{
    Title = "OK Dialog Here!!",
    Content = "Click Ok Button Dialog",
    CloseButtonText = "Ok"
};
 var result = await dialog.ShowAsync();

こんな感じ
image.png

  • ボタン2つの場合
ContentDialogSample2.cs
    var dialog = new ContentDialog()
    {
        Title = "Yes No Dialog Here!!",
        Content = "Click Yes No Button Dialog",
        PrimaryButtonText = "Yes",
        CloseButtonText = "No"
    };
    var result = await dialog.ShowAsync();

こんな感じ
image.png

  • ボタン3つの場合
ContentDialogSample3.cs
    var dialog = new ContentDialog()
    {
        Title = "3 Button Dialog Here!!",
        Content = "Click 3 Button Dialog",
        PrimaryButtonText = "Allow",
        SecondaryButtonText = "Delete",
        CloseButtonText = "Cancel"
    };
    var result = await dialog.ShowAsync();

こんな感じ
image.png

ContentDialogのShowAsync()の戻り値について

namespace Windows.UI.Xaml.Controls
{
    [ContractVersion(typeof(UniversalApiContract), 65536)]
    [WebHostHidden]
    public enum ContentDialogResult
    {
        None = 0,
        Primary = 1,
        Secondary = 2
    }
}

ContentDialogのプロパティに設定した値に対応するボタンが押された際、下記の戻り値を返却します。

プロパティ コマンド 戻り値
CloseButtonText CloseButtonCommand None
PrimaryButtonText PrimaryButtonCommand Primary
SecondaryButtonText SecondaryButtonCommand Secondary

また、それぞれコマンドプロパティも実装されているので、コマンドを作成してプロパティに設定することも可能です。

MessageDialogクラスがsealedクラスであるのに対してContentDialogクラスは継承できるのでレイアウトを拡張したり、好きなコントロールを配置可能なので使いやすいと思います。

応用 InputDialog

ContentDailogクラスを拡張して1つのテキストボックスを持つダイアログクラスを作成しました。
image.png

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む