- 投稿日:2019-10-28T22:05:37+09:00
【Xamarin】C#でAndroidアプリを作ってみる 6 (簡単なアニメーション)
概要
前回:C#でAndroidアプリを作ってみる 5 (見栄えの調整)
今回は、干支×星座算出結果表示に簡単なアニメーションを追加します。
生年月日選択→待機アニメーション→算出結果表示という流れです。アニメーションさせる画像を追加する
「Resources\drawable」に画像を追加します。
この画像ファイル名をstar.pngとします。
activity_main.xmlに、以下のImageViewを追加します。activity_main.xml<ImageView android:layout_height="wrap_content" android:layout_width="wrap_content" android:id="@+id/star_image" android:src="@drawable/star" android:visibility="gone"/>「visibility="gone"」で表示領域を確保せずに非表示にしています。
アニメーションを定義する
ソリューションエクスプローラーの「Resources」で右クリックし、追加→新しいフォルダを選択します。
フォルダ名を「anim」とします。
更に、「Resources\anim」で右クリックし、追加→新しい項目を選択します。
XMLファイルを選択します。
ファイル名は「wait_animation.xml」としました。追加ボタンを押下します。
コードは以下の通りです。wait_animation.xml<?xml version="1.0" encoding="utf-8" ?> <set xmlns:android="http://schemas.android.com/apk/res/android" android:interpolator="@android:anim/accelerate_interpolator"> <translate android:duration="800" android:fromXDelta="0%p" android:toXDelta="100%p" android:fromYDelta="0%p" android:toYDelta="100%p"/> </set>左上から右下に移動するアニメーションです。
アニメーションを開始する
アニメーションを開始するために、MainActivityに以下のコードを追加します。
MainActivity.csusing Android.Views.Animations; ・・・省略・・・ // 星の画像 ImageView starImage; ・・・省略・・・ protected override void OnCreate(Bundle savedInstanceState) { ・・・省略・・・ starImage = FindViewById<ImageView>(Resource.Id.star_image); } /// <summary> /// 結果を表示する /// </summary> private void DisplayResults(DateTime time) { // 星画像を表示する starImage.Visibility = Android.Views.ViewStates.Visible; // アニメーション開始 var waitAnimation = AnimationUtils.LoadAnimation(this, Resource.Animation.wait_animation); starImage.StartAnimation(waitAnimation); // アニメーション完了イベント starImage.Animation.AnimationEnd += (sender, e) => { // 星画像を非表示にする starImage.Visibility = Android.Views.ViewStates.Gone; selectedDateLabel.Text = time.ToString("yyyy年MM月dd日") + $"\r\nあなたは、{GetEto(time.Year)}年 {GetSeiza(time.Month, time.Day)}です。"; }; }日付選択処理(DateSelectButton_Click)を以下のように変更します。
MainActivity.cs/// <summary> /// SELECT DATEボタン押下時の処理 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void DateSelectButton_Click(object sender, System.EventArgs e) { var frag = DatePickerFragment.NewInstance(DisplayResults); frag.Show(FragmentManager, DatePickerFragment.TAG); }実行してみる
まとめ
今回は、簡単なアニメーションを追加するところまでを行いました。
次回は、未定です。
- 投稿日:2019-10-28T21:05:20+09:00
C#プログラム向きのPC
なるべくいいスペックにしましょう
Visual Studio はそれなりに重いですCPU intel製がお勧め、i7かi9せめてi5、コスパが良くないのでi3はだめ、だったら廉価なセレロンとか最近あまり見かけなくなったAMDとかの方がまし
4コア以上
HDDでは無理です
容量少なめでもSDDですメモリーは多い方がいいです
純正は高いので、メモリー交換が簡単な機種を選びあとで交換した方が安くつくかも
32GBできれば64Gb間違っても、Appleは買わないよう
できるという意見もありますが、いろいろ手間だし、ダメなこともあります
- 投稿日:2019-10-28T21:01:11+09:00
【Xamarin】C#でAndroidアプリを作ってみる 5 (見栄えの調整)
概要
前回:C#でAndroidアプリを作ってみる 4 (画面作成2)
今回は、デフォルトのままのコントロールの見栄えを整えます。Buttonに画像を表示する
SELECT DATEボタンの背景に画像を表示します。
ソリューションエクスプローラーの「Resources\drawable」を選択します。
右クリックでメニューを表示し、追加⇒既存の項目を選択します。
画像ファイルを選択して追加します。以降、ファイル名を仮にbutton.pngとします。Buttonのtextを、Resources\values\strings.xmlに定義します。
ついでに、app_nameも変更してタイトルを変えます。strings.xml<resources> <string name="app_name">干支×星座算出アプリ</string> <string name="action_settings">Settings</string> <string name="date_select_button">誕生日を選択してね!</string> </resources>activity_main.xmlのSELECT DATEボタンを以下のように変更します。
activity_main.xml<Button android:text="@string/date_select_button" android:layout_width="wrap_content" android:layout_height="wrap_content" android:minWidth="25px" android:minHeight="25px" android:id="@+id/date_select_button" android:background="@drawable/button" android:layout_centerHorizontal="true" android:layout_marginTop="10px" android:gravity="top"/>「layout_centerHorizontal="true"」で水平方向の真ん中にします。
「layout_marginTop="10px"」で上に余白を作ります。
「gravity="top"」で文字を上に移動します。算出結果のラベルを見やすくする
算出結果の文字を大きくして、真ん中に表示します。
TextViewを以下のように変更します。activity_main.xml<TextView android:id="@+id/selected_date_label" android:layout_height="match_parent" android:layout_width="match_parent" android:layout_below="@id/date_select_button" android:gravity="center" android:textSize="24sp" />「gravity="center"」で文字を真ん中に移動します。
「textSize="24sp"」で文字サイズを設定します。まとめ
今回は、見栄えを改善するところまでを行いました。
次回は、算出結果を表示する際に、簡単なアニメーションを追加します。
次回分はこちら⇒C#でAndroidアプリを作ってみる 6 (簡単なアニメーション)
- 投稿日:2019-10-28T20:29:28+09:00
初めてのVisualStudioでプロジェクト作る人に
プロジェクト
プログラムを作る一式を収める箱
もう少し大きい箱にソリューションがあります詳しくは公式で(https://docs.microsoft.com/ja-jp/visualstudio/get-started/tutorial-projects-solutions?view=vs-2019)
Visual Studio を起動します
インストールが済んでいればメニューから起動できます
まずは、コンソールアプリケーションを作ってみましょう
「コンソールアプリ(>NET Core)」をクリックして選んでから
次へのボタンをクリック
- 投稿日:2019-10-28T19:50:13+09:00
【Unity】UnityにVivox導入してみた【Sound】
Vivox ってな~に?
https://www.vivox.com/partners
PUBG, Fortnite, LoL, R6S, WoT, など世界的に売れている超有名ゲームで用いられている
テキスト/ボイスチャット提供ミドルウェアです。
2018年9月頃よりインディーデベロッパーでも無償で使えるようになりました(一部条件有)日本国内ではほとんど導入事例を聞きませんが、現在ボイスチャットを導入するなら
間違いなくこれという状態なので知っておいて損は無いでしょう。Vivox 導入フロー
アカウント作成まで
- 公式サイト(https://developer.vivox.com/) に行ってSDKを手に入れる心構えをする(ここ大事)
- 氏名とメールアドレスを登録 (ちゃんと確認用メールが届いているかチェックしよう)
- 組織を入力
- 組織の審査を待つ
審査通過後~SDK 取得まで
アプリケーションの詳細設定
最悪間違えてもあとでアプリの編集から再設定できるので必要最低限で大丈夫です
今回はUnityでの開発なのでUnityを選択してリリースに必要なプラットフォームを選択しましょう
左側メニューのDownloadからSDKを選択
Android を選択していますが、UnitySDKを選択すると中にStandalone用とAndroid用とiOS 用のSDKがUnityPackageとして入っていました。とても優しいですね。DLしてきたzip ファイルを解凍するとUnityCustomPackageがあるのでこれをUnityProject側にImport
ドキュメントについて
ReadMeには「ナレッジベースの~」という説明がありますが、日本語化UIだと場所がよくわからないので説明します。
1. Developperページのところのドキュメントを選択
2. バージョンを選択
3. General Unity を選択
4. ドキュメントページへ移動
5. Vivox Core Unity: Developer First Stepsを見ながら初期設定を行うUnityプロジェクト側の初期設定
カスタムパッケージをImport後からの手順です。
1. Assets/Vivox/Samples/TanksUnityGameSample/Scenes/MainScene.unity を開く
2. VivoxのDevelopperPortalからDashBoard 内にある先ほど作ったアプリを選択
3. InspectorにあるVivoxVoiceManager にWebページに乗っているパラメータをセット
(画像はセキュリティの都合上値を一部非表示しています)
4. Unity側のBuildSettingsにSceneを設定してビルド
まとめ
だらだらやって2~3時間でボイスチャット/テキストチャットが動きました。
Pixel3, Oculus Go, UnityEditor(PC)間で簡単にボイチャが出来るのは驚くべきことで、
謳い文句の36時間以内にボイスチャットが動くはあながち間違いじゃないなと思いました。今回は導入フローまでですが、製品版で使うには別途審査があったりしますが、
導入ハードルの低さや性能部分に関しては文句なしといったところでしょうか。
- 投稿日:2019-10-28T17:28:59+09:00
はじめます
もうすぐ、義務教育でもプログラムが始まるんだよ!
プログラム言語って、C#だけじゃないけど?
プログラムを始めるには、一番いいと思うよC#たんの生みの親の岩さんも言ってるし
https://ufcpp.net/study/csharp/getstarted.htmlまあ、色々なプログラム言語のことは、丸さんに聞いてね。
https://ncode.syosetu.com/n1081fh/
- 投稿日:2019-10-28T17:28:59+09:00
C#でのプログラム初学者向けのチュートリアルを始めます
もうすぐ、義務教育でもプログラムが始まるんだよ!
プログラム言語って、C#だけじゃないけど?
プログラムを始めるには、一番いいと思うよC#たんの生みの親の岩さんも言ってるし
https://ufcpp.net/study/csharp/getstarted.htmlまあ、色々なプログラム言語のことは、丸さんに聞いてね。
https://ncode.syosetu.com/n1081fh/第0章 C#でプログラムをする前準備(https://qiita.com/CsharpTan/items/3db4373b924b7d1ab9ec)
- 投稿日:2019-10-28T17:26:10+09:00
脱出ゲームの作り方 7 特定の条件でアイテムを使用
取得した電球を使うと電気が点灯する処理が作成されたが、
LightstandPanel以外の場所でも使えてしまうので、LightstandPanelの場面で電気を使うようにする各パネルを表示するときに、現在のパネルが何かをチェックする
GameManagerで下記を追加する
◇変数currentPanelを列挙型で作成し、ROOMパネルで初期化する
public enum PANEL { ROOM, LIGHT_STAND, DRAWER, PC } // 現在表示しているパネル public PANEL currentPanel = PANEL.ROOM;◇ボタンを押したら、現在のパネルをcurrentPanelに取得し、該当するパネルを表示する
バックボタンが押されたら、currentPanelをROOMに戻し、他のパネルを非表示にする
// ボタンを押したら該当するパネルを表示 public void OnClickLightStandTrigger() { currentPanel = PANEL.LIGHT_STAND; lightStandPanel.SetActive(true); } public void OnClickDrawerTrigger() { currentPanel = PANEL.DRAWER; drawerPanel.SetActive(true); } public void OnClickPCTrigger() { currentPanel = PANEL.PC; pcPanel.SetActive(true); } // ボタンを押したらパネルを全て非表示 public void OnClickBackTrigger() { currentPanel = PANEL.ROOM; lightStandPanel.SetActive(false); drawerPanel.SetActive(false); pcPanel.SetActive(false); }アイテムを電気スタンドが表示されている時だけ使用する
◇現在のパネルが電気スタンドのときに、アイテムを使う
ItemBoxManagerでGameManagerを取得し、UseItem()を書き換える[SerializeField] GameManager gameManager; // アイテムの使用 public void UseItem(int index) { if ( gameManager.currentPanel == PANEL.LIGHT_STAND && itemList[index] == ITEM.LIGHT_BULB) //現在のパネルが電気スタンドで、かつ使用するアイテムが電球だったら { lightStandManager.LightSwitch(true); //電気をつける itemList[index] = ITEM.NONE; // アイテムを使用したので空にする; itemBoxImages[index].sprite = null; } }InspecterでItemBoxPnelオブジェクトのItemBoxManagerスクリプトにGameManagerが出ているので、GameManagerを設定する
UseItem(int index)関数は、OnClick()ではなく実行できる?教材
Unityゲームスタジオ スタジオしまづ
【Unity】初心者からの脱出!? 脱出ゲームの作り方 その7 特定の条件でアイテムを使用
https://youtu.be/ualq_a3rnQc
- 投稿日:2019-10-28T16:47:43+09:00
値オブジェクトのnull対応ってNullObjectパターンで良いのでは?
はじめに
値オブジェクトの悩みポイントとどう付き合っていくかを見させていただいてのですが、
悩みがちな項目に
- Nullableな値を取り扱う場合
がありました。
NullObjectパターンじゃないの?と自分は思っていたのですが、ネットで言及している情報があまりなかったのでここが良いよっていう点を投稿してみます。業務知識
以下のようなドメインで考えます。
- Nullを許容しない値オブジェクト(ユーザーID)
- nullと空文字は別扱いしたい値オブジェクト(名前)
- DateTimeとかNullにできない値オブジェクト(生年月日)
- 選択肢系の値オブジェクト(性別種別)
を用意してみました。
前提
値オブジェクトの抽象クラスには値オブジェクトを実装するを使用しています。値オブジェクトの特性を満たせていればなんでも良いです。
値オブジェクトの生成はstaticファクトリメソッド派です。Nullを許容しない値オブジェクト
例えばユーザーIDのようなNullを許容しない値オブジェクトは以下のようにガチガチに作れば良いと思ってます。
public class ユーザーID : ValueObject { private Guid _id { get; } public string Guid文字列 => _id.ToString(); public static ユーザーID CreateNew() => new ユーザーID(Guid.NewGuid()); public static ユーザーID Create(object o) => o switch { null => throw new ArgumentException("Guidに変換出来ません。Nullです。", "_ユーザーID"), string s => new ユーザーID(文字列をGuidに変換(s)), Guid g => new ユーザーID(g), _ => throw new ArgumentException("Guidに変換出来ません。" + o.ToString(), "_ユーザーID"), }; private static Guid 文字列をGuidに変換(string s) { Guid g; var result = Guid.TryParse(s, out g); return (result) ? g : throw new ArgumentException("Guidに変換出来ません。" + s, "_ユーザーID"); } private ユーザーID(Guid _id) => this._id = _id; protected override IEnumerable<object> GetAtomicValues() { yield return _id; } }nullと空文字は別扱いしたい値オブジェクト
例えば名前のように(今回想定しているシステムでは)必須ではない項目に関しては別途Null名前のように名前を継承したNullObjectを用意すれば使う側は動作の違いを意識する必要がなくなるはず。
public class 名前 : ValueObject { protected string _名前 { get; } public virtual string 名前文字列 => _名前; public virtual string 名前様付 => _名前 + "様"; public static 名前 Create(object _名前) => _名前 switch { string s when string.IsNullOrEmpty(s) => new Null名前(), string s when s.Length > 60 => throw new ArgumentException("名前は60文字以下", nameof(_名前)), string s => new 名前(s), _ => throw new ArgumentException("文字列以外です。" + _名前.ToString(), "_名前") }; private 名前(string _名前) => this._名前 = _名前; protected 名前() => this._名前 = null; protected override IEnumerable<object> GetAtomicValues() { yield return _名前; } } public class Null名前 : 名前 { public override string 名前文字列 => ""; public override string 名前様付 => ""; }DateTimeとかNullにできない値オブジェクト
例えば生年月日のように(今回想定しているシステムでは)必須ではない項目に関しては別途Null生年月日のように生年月日を継承したNullObjectを用意すれば使う側は動作の違いを意識する必要がなくなるはず。
今回はDateTime.MinValueを有り得ない値として表現しました。DateTimeをNull許容型にして操作するのも有りかと。public class 生年月日 : ValueObject { protected DateTime _生年月日 { get; } public virtual string 生年月日スラッシュ => _生年月日.ToString("yyyy/MM/dd"); public virtual string 生年月日ハイフン => _生年月日.ToString("yyyy-MM-dd"); public static 生年月日 Create(object o) => DateTimeに変換(o) switch { null => new Null生年月日(), DateTime d when d.Equals(DateTime.MinValue) => new Null生年月日(), DateTime d when d > DateTime.Now => throw new ArgumentException("今日より未来の生年月日は不可", "_生年月日"), var d => new 生年月日(d.Value), }; private static DateTime? DateTimeに変換(object o) => o switch { string s when string.IsNullOrEmpty(s) => null, string s => 文字列をDateTimeに変換(s), DateTime d => d, _ => throw new ArgumentException("DateTimeに変換出来ません。" + o.ToString(), "_生年月日"), }; private static DateTime? 文字列をDateTimeに変換(string s) { DateTime? d = null; DateTime d2; var result = DateTime.TryParse(s, out d2); if (result) d = d2; return (result) ? d : throw new ArgumentException("DateTimeに変換出来ません。" + s, "_生年月日"); } private 生年月日(DateTime _生年月日) => this._生年月日 = _生年月日.Date; protected 生年月日() => _生年月日 = DateTime.MinValue; protected override IEnumerable<object> GetAtomicValues() { yield return _生年月日; } } public class Null生年月日 : 生年月日 { public override string 生年月日スラッシュ => ""; public override string 生年月日ハイフン => ""; }選択肢系の値オブジェクト
例えば性別種別のように(今回想定しているシステムでは)必須ではない項目で、画面上の都合で選択していないという状態を表現するのにもNullObjectで良いかと。
public enum 性別 { 男 = 1, 女 = 2 } public class 性別種別 : ValueObject { protected 性別? _性別 { get; } public virtual string 文字列 => Enum.GetName(typeof(性別), _性別.Value); public virtual int 値 => (int)(_性別.Value); public static 性別種別 Create(object o) => o switch { null => new 性別種別選択外(), int i when i == 0 => new 性別種別選択外(), var v => new 性別種別(性別に変換(v)) }; public static 性別 性別に変換(object o) => o switch { int i => (性別)Enum.ToObject(typeof(性別), i), string s => (性別)Enum.Parse(typeof(性別), s), _ => throw new ArgumentException("性別種別に変換できません。" + o.ToString(), "_性別") }; public static 性別種別 Create男() => Create(1); public static 性別種別 Create女() => Create(2); public static 性別種別 Create選択外() => Create(null); private 性別種別(性別 _性別) => this._性別 = _性別; protected 性別種別() => _性別 = null; protected override IEnumerable<object> GetAtomicValues() { yield return _性別; } } public class 性別種別選択外 : 性別種別 { public override string 文字列 => "選択外"; public override int 値 => 0; } public class 性別種別一覧 { private static List<性別種別> _一覧 { get; } = new List<性別種別>{ 性別種別.Create選択外(), 性別種別.Create男(), 性別種別.Create女(), }; private static List<性別種別> _選択外無一覧 { get; } = new List<性別種別>{ 性別種別.Create男(), 性別種別.Create女(), }; public static List<性別種別> 一覧 => _一覧; public static List<性別種別> 選択外無一覧 => _選択外無一覧; public static List<(string, int)> 名称と値一覧 => _一覧.Select(x => (x.文字列, x.値)).ToList(); }業務知識として選択なしというのを扱うのならば素直にenumに追加して上げても良いはず。
例えばプリミティブな値から復元するとき
プリミティブな値からユーザーを復元する際の例を書いてみます。
public class ユーザー { public ユーザーID ID { get; } public 名前 名前 { get; } public 生年月日 生年月日 { get; } public 性別種別 性別種別 { get; } public ユーザー(ユーザーID _ID, 名前 _名前, 生年月日 _生年月日, 性別種別 _性別種別) { ID = _ID; 名前 = _名前; 生年月日 = _生年月日; 性別種別 = _性別種別; } } public interface Iユーザーファクトリ { ユーザー Create(object _ID, object _名前, object _生年月日, object _性別種別); } public class ユーザーファクトリ : Iユーザーファクトリ { public ユーザー Create(object _ID, object _名前, object _生年月日, object _性別種別) => new ユーザー( ユーザーID.Create(_ID), 名前.Create(_名前), 生年月日.Create(_生年月日), 性別種別.Create(_性別種別) ); }こんな感じでnullか否かを意識することなくすっきり書けます。許容外の値が来た時のエラーハンドリングは必要ですけど。
例えばDBに保存するとき
ユーザーをリポジトリを介して保存する例を書いてみます。
public class ユーザーDAO { public string ID { get; set; } public string 名前 { get; set; } public string 生年月日 { get; set; } public int 性別種別 { get; set; } }というようなDAOがあったとして
public interface Iユーザーリポジトリ { void Save(ユーザー _ユーザー); } public static class なんかのフレームワーク向け拡張メソッド { public static string なんかのフレームワーク向け格納(this 名前 _名前) => _名前.名前文字列; public static string なんかのフレームワーク向け格納(this 生年月日 _生年月日) => _生年月日.生年月日ハイフン; public static int なんかのフレームワーク向け格納(this 性別種別 _性別種別) => _性別種別.値; public static string なんかのフレームワーク向け格納(this ユーザーID _id) => _id.Guid文字列; } public class ユーザーリポジトリ : Iユーザーリポジトリ { private なんかのフレームワーク なんかのフレームワーク { get; } public ユーザーリポジトリ() => なんかのフレームワーク = new なんかのフレームワーク(); public void Save(ユーザー _ユーザー) { ユーザーDAO ユーザーDAO = なんかのフレームワーク.Get(ユーザー.ID); ユーザーDAO.名前 = ユーザー.名前.なんかのフレームワーク向け格納(); ユーザーDAO.生年月日 = ユーザー.名前.なんかのフレームワーク向け格納(); ユーザーDAO.性別種別 = ユーザー.名前.なんかのフレームワーク向け格納(); なんかのフレームワーク.Save(ユーザーDAO); } }値を詰め込むときもNullか否かを意識せずに詰め込めます。
フレームワーク特有の詰め込み方がある場合には拡張メソッドなどで特有の処理を書いたほうが良いかと。
ドメインの方にフレームワーク特有の処理を書いてしまうと業務知識が汚染される気がします。どうしてもNullか否かを分岐したいとき
その場合は
bool IsNull()なんかを値オブジェクトに用意するのではなく
if (o is Null名前)のように型判定で分岐しましょう。
まとめ
業務知識としてはnullは許したくなくてもDB等保存側の制限でNull許容にしなければならないときがあります。
その際はなあなあにせずきちんと業務知識として検討しないといけません。
(nullをそのまま代入するか、別値として内部では処理するか、とか)
その際にNullObjectとして型分けしておけば処理の差分が書きやすいかと思っています。値オブジェクトのNullableな値を取り扱う場合についてNullObjectパターン以外で便利な方法があればご教示頂きたいです。
あと値オブジェクトのNullObjectパターンだとこの場合に対応出来ない、とか。
- 投稿日:2019-10-28T16:04:41+09:00
【Xamarin】C#でAndroidアプリを作ってみる 4 (画面作成2)
概要
前回:C#でAndroidアプリを作ってみる 3 (画面作成1)
今回は、選択した日付の干支×星座算出部分を作ります。干支を算出する
干支の求め方を参考に計算します。
MainActivity.csに以下のコードを追加します。MainActivity.cs// 十二支 private const string ETO = "申酉戌亥子丑寅卯辰巳午未"; /// <summary> /// 干支の算出 /// </summary> /// <param name="year"></param> /// <returns></returns> private string GetEto(int year) { return ETO[year % 12].ToString(); }星座を算出する
星占いの星座です。
MainActivity.csに以下のコードを追加します。MainActivity.csusing System.Linq; // 星座 private static Tuple<string, int, int>[] SEIZA = { Tuple.Create("牡羊座", 321, 419), Tuple.Create("牡牛座", 420, 520), Tuple.Create("双子座", 521, 621), Tuple.Create("蟹座", 622, 722), Tuple.Create("獅子座", 723, 822), Tuple.Create("乙女座", 823, 922), Tuple.Create("天秤座", 923, 1023), Tuple.Create("蠍座", 1024, 1122), Tuple.Create("射手座", 1123, 1221), Tuple.Create("山羊座", 1222, 1231), Tuple.Create("山羊座", 101, 120), Tuple.Create("水瓶座",121, 218), Tuple.Create("魚座", 219, 320) }; /// <summary> /// 星座の算出 /// </summary> /// <param name="month"></param> /// <param name="day"></param> /// <returns></returns> private static string GetSeiza(int month, int day) { var target = int.Parse($"{month}{day.ToString("00")}"); var seiza = SEIZA.FirstOrDefault(s => s.Item2 <= target && target <= s.Item3); return seiza.Item1; }計算の都合上、山羊座は2期間に分けています。
干支と星座の算出結果を表示する
日付を選択した後、干支と星座を画面に表示するようにします。
MainActivity.csのDateSelectButton_Clickを以下のように変更します。MainActivity.cs/// <summary> /// SELECT DATEボタン押下時の処理 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void DateSelectButton_Click(object sender, System.EventArgs e) { var frag = DatePickerFragment.NewInstance(delegate (DateTime time) { selectedDateLabel.Text = time.ToString("yyyy年MM月dd日") + $"あなたは、{GetEto(time.Year)}年 {GetSeiza(time.Month, time.Day)}です。"; }); frag.Show(FragmentManager, DatePickerFragment.TAG); }実行してみる
実機でデバッグ実行します。
SELECT DATEボタンをタップして日付を選択し、OKをタップします。
日付の選択、干支と星座の算出結果が表示されました。まとめ
今回は、選択した日付の干支×星座を算出し、画面に算出結果を表示するところまでを行いました。
次回は、もう少し見栄えを良くしようと思います。
次回分はこちら⇒C#でAndroidアプリを作ってみる 5 (見栄えの調整)
- 投稿日:2019-10-28T15:50:18+09:00
[C#]ExcelファイルからPDFファイル出力
1. はじめに
Microsoft® Visual Studio® Community 2019 の Visual C#®で、Excel ファイルから PDF ファイル出力する方法の忘備録になります。
2. 概要
Microsoft.Office.Interop.Excelの ExportAsFixedFormat メソッドを利用して、 オープンした Excel ファイルを、PDF ファイル形式で出力します。3. 詳細
1) 参照設定
Visual Studio の [プロジェクト] メニューの [参照の追加] で [参照 マネージャー] を表示して、[COM] - [Microsoft Excel 16.0 Object Library] にチェックして [OK] を押下します。
2) サンプルコード
ExcelManager.csusing System; using Microsoft.Office.Interop.Excel; class ExcelManager: IDisposable { private Application _application = null; private Workbook _workbook = null; private bool disposedValue = false; /// <summary> /// Excelワークブックを開く /// </summary> public void Open() { // Excelアプリケーション生成 _application = new Application() { // 非表示 Visible = false, }; // Bookを開く _workbook = _application .Workbooks.Open(@"e:\book1.xlsx"); } /// <summary> /// Excelワークブックをファイル名を指定してPDF形式で保存する /// </summary> /// <returns>true:正常終了、false:保存失敗</returns> public bool SaveAsPDF() { try { // 全シートを選択する _workbook.Worksheets.Select(); // ファイル名を指定してPDF形式で保存する // ExportAsFixedFormatメソッド: ブックを PDF または XPS 形式に発行する // Type : タイプ : XlFixedFormatType xlTypePDF=PDF, xlTypeXPS=XPS // Filename: 出力ファイル名 // Quality : 種t力品質: XlFixedFormatQuality xlQualityStandard=標準品質, xlqualityminimum=最小限の品質 _workbook.ExportAsFixedFormat( Type: XlFixedFormatType.xlTypePDF, Filename: @"e:\book1.pdf", Quality: XlFixedFormatQuality.xlQualityStandard); } catch { return false; } return true; } protected virtual void Dispose(bool disposing) { if (!disposedValue) { if (disposing) { // TODO: Managed Objectの破棄 } if (_workbook != null) { _workbook.Close(); Marshal.ReleaseComObject(_workbook); _workbook = null; } if (_application != null) { _application.Quit(); Marshal.ReleaseComObject(_application); _application = null; } disposedValue = true; } } ~ExcelManager() { Dispose(false); } public void Dispose() { Dispose(true); GC.SuppressFinalize(this); }test.csusing System; using System.Windows.Forms; public partial class test: Form { public test() { InitializeComponent(); } private void Btn1_Click(object sender, EventArgs e) { // for Microsoft.Office.Interop.Excel using (var excel = new ExcelManager()) { excel.Open(); if (excel.SaveAsPDF() == false) MessageBox.Show("ファイルが既に開かれています。\n閉じてから、再試行してください。", "エラー", MessageBoxButtons.OK, MessageBoxIcon.Error); } } }参考
- 投稿日:2019-10-28T14:11:08+09:00
【Xamarin】C#でAndroidアプリを作ってみる 3 (画面作成1)
概要
前回:C#でAndroidアプリを作ってみる 2 (デバッグ実行)
今回は、ZodiacSignApp(干支×星座算出アプリ)の画面を作ります。レイアウトの作成
ZodiacSignApp\Resources\layout\activity_main.xmlを開きます。
初期状態では、RelativeLayoutタグしかありません。SELECT DATEボタンと選択日付の表示ラベルを追加します。
コードは以下の通りです。activity_main.xml<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent"> <Button android:text="SELECT DATE" android:layout_width="wrap_content" android:layout_height="wrap_content" android:minWidth="25px" android:minHeight="25px" android:id="@+id/date_select_button" /> <TextView android:id="@+id/selected_date_label" android:layout_height="match_parent" android:layout_width="wrap_content" android:layout_below="@id/date_select_button"/> </RelativeLayout>日付選択ダイアログを表示する
SELECT DATEボタンを押下すると、日付選択ダイアログが表示されるイメージです。
Android 日付の選択を参考に作ります。DatePickerFragmentクラスを作ります。
コードは以下の通りです。DatePickerFragment.csusing Android.App; using Android.OS; using Android.Widget; using System; namespace ZodiacSignApp { /// <summary> /// 日付選択ダイアログフラグメント /// </summary> public class DatePickerFragment : DialogFragment, DatePickerDialog.IOnDateSetListener { // タグ public static readonly string TAG = "X:" + typeof(DatePickerFragment).Name.ToUpper(); // 日付選択処理のデリゲート Action<DateTime> dateSelectedHandler = delegate { }; /// <summary> /// インスタンス生成 /// </summary> /// <param name="onDateSelected"></param> /// <returns></returns> public static DatePickerFragment NewInstance(Action<DateTime> onDateSelected) { var frag = new DatePickerFragment(); frag.dateSelectedHandler = onDateSelected; return frag; } /// <summary> /// ダイアログ生成 /// </summary> /// <param name="savedInstanceState"></param> /// <returns></returns> public override Dialog OnCreateDialog(Bundle savedInstanceState) { var now = DateTime.Now; var dialog = new DatePickerDialog(Activity, this, now.Year, now.Month - 1, now.Day); return dialog; } /// <summary> /// IOnDateSetListener.OnDateSetの実装 /// </summary> /// <param name="view"></param> /// <param name="year"></param> /// <param name="month"></param> /// <param name="dayOfMonth"></param> public void OnDateSet(DatePicker view, int year, int month, int dayOfMonth) { var selectedDate = new DateTime(year, month + 1, dayOfMonth); dateSelectedHandler(selectedDate); } } }SELECT DATEボタン押下で日付選択ダイアログを表示するようにします。
コードは以下の通りです。MainActivity.csusing Android.App; using Android.OS; using Android.Support.V7.App; using Android.Runtime; using Android.Widget; using System; namespace ZodiacSignApp { /// <summary> /// メインアクティビティ /// </summary> [Activity(Label = "@string/app_name", Theme = "@style/AppTheme", MainLauncher = true)] public class MainActivity : AppCompatActivity { // SELECT DATEボタン Button dateSelectButton; // 選択日付の表示ラベル TextView selectedDateLabel; protected override void OnCreate(Bundle savedInstanceState) { base.OnCreate(savedInstanceState); Xamarin.Essentials.Platform.Init(this, savedInstanceState); // Set our view from the "main" layout resource SetContentView(Resource.Layout.activity_main); selectedDateLabel = FindViewById<TextView>(Resource.Id.selected_date_label); dateSelectButton = FindViewById<Button>(Resource.Id.date_select_button); dateSelectButton.Click += DateSelectButton_Click; } /// <summary> /// SELECT DATEボタン押下時の処理 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void DateSelectButton_Click(object sender, System.EventArgs e) { var frag = DatePickerFragment.NewInstance(delegate (DateTime time) { selectedDateLabel.Text = time.ToString("yyyy年MM月dd日"); }); frag.Show(FragmentManager, DatePickerFragment.TAG); } public override void OnRequestPermissionsResult(int requestCode, string[] permissions, [GeneratedEnum] Android.Content.PM.Permission[] grantResults) { Xamarin.Essentials.Platform.OnRequestPermissionsResult(requestCode, permissions, grantResults); base.OnRequestPermissionsResult(requestCode, permissions, grantResults); } } }実行してみる
実機でデバッグ実行します。
SELECT DATEボタンをタップします。
日付を選択して、OKをタップします。
日付が選択できました。まとめ
今回は、レイアウトを作成し、日付選択ダイアログから日付を選択するところまでを行いました。
次回は、選択した日付の干支×星座算出を行います。
次回分はこちら⇒C#でAndroidアプリを作ってみる 4 (画面作成2)
- 投稿日:2019-10-28T12:43:24+09:00
C# 整数型
自分用です
型名 記憶領域サイズ 扱える値の範囲 byte 1バイト 0 ~ 255 sbyte 1バイト -128 ~ 127 short 2バイト -32,768 ~ 32,767 ushort 2バイト 0 ~ 65,535 int 4バイト -2,147,483,648 ~ 2,147,483,647 uint 4バイト 0 ~ 4,294,967,295 long 8バイト -9,223,372,036,854,775,808 ~ 9,223,372,036,854,775,807 ulong 8バイト 0 ~ 18,446,744,073,709,551,615
- 投稿日:2019-10-28T11:13:53+09:00
C# (LINQ) で Python (list, numpy) っぽくリスト操作
はじめに
Python のリストや numpy ndarray の基本操作に対応する処理を、C# でどう書けばよいか?
毎回ウェブをあちこち探すのはとても時間がかかります。普段 Python を使っていてたまに C# で書くとき、すぐに対応が見つかるように、一通りまとめておきます。
バージョン
Python 3、C# 6.0 (.NET Framework 4.6) を前提にします。.NET Framework 3.5 でも一部を除き対応していると思われます。
配列とリスト
- Python は標準リストと numpy.ndarray を扱います。
- C# はリストを主に扱います。
C# の配列とリストはどちらも
IEnumerableを実装しているので、LINQ の説明の多くは配列でも使えます。(Python 標準ライブラリの array.array はあまり使われないと思いますので省略します。)
サンプルコードでの配列名とリスト名
オブジェクト名に以下の文字を入れておきます。
宣言や宣言時の型は、誤解がない範囲で省略します。
- Python
- 標準リスト:
lst- 配列:
arr- C#
- リスト:
list- 配列:
arrayPython# 以下が import されていると仮定します import numpy as np # for numpy.ndarray lst = [1, 2, 3] # 標準リスト arr = np.array([1, 2, 3]) # numpy.ndarrayC#// 以下が using されていると仮定します using System; using System.Collections.Generic; // for List using System.Linq; List<int> list = new List<int> {1, 2, 3}; // リスト (Collections.Generic) int[] array = new int[] {1, 2, 3}; // 配列おことわり
この記事は、数値計算や機械学習の置き換え、パフォーマンスを目的とはしていません。あくまで、Python のリストや numpy.ndarray の基本操作に類似した操作を C# で書く場合の参考です。
本来の numpy 的用途(速度重視)なら、BLAS や MKL など専用演算ライブラリがベースにあるモジュールやパッケージを使った方がもちろん高速なはず。たとえば Math.NET Numerics (MKL版) とか。
Python 基本操作
range
Python# range(stop) / range(start, stop[, step]) range(5) # 0, 1, 2, 3, 4 (= range(0, 5)) range(1, 5) # 1, 2, 3, 4 # 1つ飛ばし range(3, 10, 2) # 3, 5, 7, 9 (3-9の奇数) for val in range(1, 5): print(val)C#// IEnumerable<int> Range (int start, int count); Enumerable.Range(0, 5) // 0, 1, 2, 3, 4 Enumerable.Range(1, 4) // 1, 2, 3, 4 (第2引数は要素数) // 1つ飛ばし Enumerable.Range(0, 4).Select(i => i * 2 + 3) // 3, 5, 7, 9 (*1) Enumerable.Range(3, 7).Where(i => i % 2 == 1) // 3, 5, 7, 9 (*2) foreach (var val in Enumerable.Range(1, 4)) { Console.WriteLine(val); }Python3 の
range(Python2 だとxrange)と C# のEnumerable.Rangeはどちらも遅延評価されます。range/Range もそうですが、Python では (開始値、終了値+1) とするのに対し、C# では (開始値、要素数) とするメソッドが多いようです。
Python の range に合わせる
Python の
range(start, stop, step)に合わせた実装を考えてみます。C#でrange(python)に近いもの// (*1) を拡張してみる(負の数や step < 0 にも対応.step = 0 のエラー処理省略) Enumerable.Range(0, (int)Math.Ceiling((stop-start)/(double)step)) .Select(i => i * step + start) // (*2) を拡張してみる(これだと負の数には対応できないし効率悪そう) // Enumerable.Range(start, stop-start).Where(i => i % step == start % step) // いっそのこと for ループで静的メソッドを定義してしまう方が分かりやすいかも public static IEnumerable<int> MyRange(int start, int stop, int step) { if (step > 0) for (int i = start; i < stop; i += step) yield return i; else if (step < 0) for (int i = start; i > stop; i += step) yield return i; else yield break; }遅延評価せずリストや配列にする
Pythonlst = list(range(0, 5)) arr = np.array(range(0, 5))C#list = Enumerable.Range(0, 5).ToList(); array = Enumerable.Range(0, 5).ToArray();numpy.arange
Pythonnp.arange(0, 1, 0.1) # 0, 0.1, ..., 0.9C#Enumerable.Range(0, 10).Select(i => i * 0.1); // 0, 0.1,..., 0.9一般には、先の「(*1)を拡張してみる」の実装で、小数の場合でも行けるのではと思います。
一方、先の
MyRangeを double 版の実装にして 0.1 ずつインクリメントなどしてしまうと、丸め誤差で大小比較の判定がうまくいかないです。decimal 版なら大丈夫でしょう。内包表現や各要素に対する演算
Pythonlst2 = [x * x for x in lst1] # もしくは [x ** 2 for x in lst1] arr2 = arr1 * arr1 # もしくは arr1 ** 2 (powerは遅いはず)C#list2 = list1.Select(x => x * x).ToList();内包表現 (list comprehension) を使っていたところは、LINQ の Select で何とかなることが多いです。あとは、文脈をよく考えて、遅延評価をするかしないか判断が必要になります。
遅延評価した方がよいならToList()せずIEnumerableのままにします。例:要素の総和で正規化
要素の総和が1になるように正規化する方法をまとめておきます。確率計算でもよく使います。
Pythonlst2 = [x / sum(lst1) for x in lst1] arr2 = arr1 / sum(arr1) # もしくは arr1 / arr1.sum()C#list2 = list1.Select(x => x / list1.Sum()).ToList();map, filter
Python では、イテレータが欲しい、遅延評価したい、という場合はリスト内包表現ではなく
mapやfilterを使えますが、これは LINQ だとそのままSelectとWhereに対応しそうです。Pythonfor x in map(lambda x: x * x, lst): # 各要素の二乗を列挙して表示 print(x) for x in filter(lambda x: x < 4, lst): # 4未満の要素を表示 print(x) # 組み合わせ:二乗した要素が10未満なら二乗した値を表示 for x in filter(lambda x : x < 10, map(lambda x: x * x, lst)): print(x)C#foreach(var x in list.Select(x => x * x)) // 各要素の二乗を列挙して表示 Console.WriteLine(x); foreach(var x in list.Where(x => x < 4)) // 4未満の要素を表示 Console.WriteLine(x); // 組み合わせ:二乗した要素が10未満なら二乗した値を表示 foreach(var x in list.Select(x => x * x).Where(x => x < 10)) Console.WriteLine(x);
filterとmapが組み合わさってくると、LINQ のようにメソッドチェーンで書く方が分かりやすいですね。enumerate
リストや配列の値だけでなくそのインデックスも欲しい場合、Python では
enumerateですが、C# ではSelectを使えます。Pythonlst1 = ['foo', 'bar'] lst2 = ['[{0}] {1}'.format(i, s) for i, s in enumerate(lst1)]C#var list1 = new List<string> {"foo", "bar"}; var list2 = list1.Select((s, i) => string.Format("[{0}] {1}", i, s)).ToList()LINQ の
Selectは、value, indexの順序が Python のenumerateと逆なので注意が必要です。zip および要素ごとの演算
Pythonで2つのリストをziplst3 = [x - y for x, y in zip(lst1, lst2)] # 要素ごとの引き算 sum([x * y for x, y in zip(lst1, lst2)]) # 内積計算NumPyで要素ごとの演算arr3 = arr1 - arr2 # 要素ごとの引き算 arr1.dot(arr2) # もしくは np.dot(arr1, arr2) # 内積計算C#list3 = list1.Zip(list2, (x, y) => x - y).ToList(); // 要素ごとの引き算 list1.Zip(list2, (x, y) => x * y).Sum(); // 内積計算 // もしくは list3 = Enumerable.Zip(list1, list2, (x, y) => x - y).ToList(); // 要素ごとの引き算 Enumerable.Zip(list1, list2, (x, y) => x * y).Sum(); // 内積計算3つ以上のリスト(配列)を組み合わせる
Pythonlst4 = [x + y + z for x, y, z in zip(lst1, lst2, lst3)] # 要素ごとの足し算 arr4 = arr1 + arr2 + arr3 # 要素ごとの足し算C#で3つのリストをZiplist4 = list1.Zip(list2, (x, y) => x + y) .Zip(list3, (zip1, z) => zip1 + z).ToList(); // 要素ごとの足し算LINQ の
Zipは3つ以上のリストをとれないので、Zipを繰り返して使うことになるようです。一段目の
Zipでペアをそのまま次段へ匿名型で渡せば、二段目のZipでまとめることもできます。C#で3つのリストをZip(匿名型でつなげる)list4 = list1.Zip(list2, (x, y) => new {x, y}) .Zip(list3, (zip1, z) => zip1.x + zip1.y + z).ToList(); // 要素ごとの足し算初期化
0 で初期化
Pythonlst = [0] * 10 # 1次元ならこれでOK (入れ子など参照が絡む場合は落とし穴) lst = [0 for _ in range(10)] # これもOK (入れ子など参照が絡んでくるならこちら) arr = np.zeros(10) # 0 で初期化された要素10の ndarrayC#int[] array = new int[10]; // 0 で初期化された要素10の配列 List<int> list = new int[10].ToList(); List<int> list = new List<int> (new int[10]); // コンストラクタの引数に配列を渡す同じ値で埋めて初期化
Python# False で埋めた配列 lst = [False] * 10 # 1次元ならこれでOK (入れ子など参照が絡む場合は落とし穴) lst = [False for _ in range(10)] # 入れ子リストなど参照が絡んでくるならこちら arr = np.full(10, False) # False で初期化された要素10の ndarrayC#List<bool> list = Enumerable.Repeat(false, 10).ToList(); // こちら落とし穴あり(*1) List<bool> list = Enumerable.Range(0, 10).Select(_ => false).ToList(); List<bool> list = new bool[10].Select(_ => false).ToList(); // いずれも ToList() を ToArray() にすれば配列に(*1) リストを入れ子で作る場合など、参照型で ’Repeat’ してしまうと、ひとつ変えるとすべて変わります。この落とし穴は Python の
[値] * 要素数と同じです。
https://qiita.com/yosizo@github/items/1adcff1fc974cde5256a連番の値で初期化
「遅延評価せずリストや配列にする」の通り、
RangeをToList()やToArray()で連番の値を持ったリストや配列を作ることができます。Python でrangeからlistやnp.arrayを作るのに対応します。乱数で初期化
整数乱数で初期化
Pythonimport random # 標準ライブラリ # randint(a,b)は randrange(a,b+1)のエイリアス lst = [random.randrange(0, 10) for _ in range(5)] # 0以上9以下で5個 lst = [random.randint(0, 9) for _ in range(5)] # 0以上9以下で5個 # lst = random.sample(range(10), k=5)) # 0以上9以下で5個(重複無しなので上とは異なる) arr = np.random.randint(0, 10, 5) # 0以上9以下で5個numpy の
random.randintは、標準ライブラリのrandom.randintとは異なり(むしろrandrangeと同じで)指定値未満の乱数を返すので注意!C#System.Random rg = new System.Random(); // random number generator (usingしておいてもよい) int[] rndarray = Enumerable.Range(0, 5).Select(_ => rg.Next(0, 10)).ToArray(); // 0以上9以下で5個 List<int> rndlist = Enumerable.Range(0, 5).Select(_ => rg.Next(0, 10)).ToList(); // 0以上9以下で5個
Next()の第一引数(最小値)は省略すると0以上となります。実数乱数で初期化
Pythonimport random # 標準ライブラリ lst = [random.random() for _ in range(5)] # 0以上1未満で5個 arr = np.random.rand(5) # 0以上1未満で5個C#double[] array = Enumerable.Range(0, 5).Select(_ => rng.NextDouble()).ToArray(); // 0以上1未満で5個 List<double> list = Enumerable.Range(0, 5).Select(_ => rng.NextDouble()).ToList(); // 0以上1未満で5個乱数生成アルゴリズム自体を変える方法はこちらで解説されています。
値をコピーして初期化
Pythonlst1 = [3, 1, 4] # コピー元を適当に作っておく lst2 = list(lst1) # lst1, lst2 のオブジェクトIDは異なる lst2 = lst1[:] # これも可 (lst1, lst2 のオブジェクトIDは異なる) # lst2 = lst1 # lst1, lst2 のオブジェクトIDは一致 (コピーではない) import copy lst2 = lst1.copy() # これも可 (lst1, lst2 のオブジェクトIDは異なる) arr1 = np.array([3, 1, 4]) # コピー元を適当に作っておく arr2 = np.copy(arr1) # lst1, lst2 のオブジェクトIDは異なる arr2 = arr1.copy() # これも可 (lst1, lst2 のオブジェクトIDは異なる) # arr2 = arr1 # arr1, arr2 のオブジェクトIDは一致 (コピーではない)C#var list2 = new List<int>(list1); // コピーコンストラクタで初期化 var list2 = list1.ToList(); // これも可 var array2 = new int[array1.Length]; // あらかじめ領域確保 array1.CopyTo(array2, 0); // 第二引数はコピー先の開始 index Array.Copy(array1, array2, array1.Length); // これも可 var array2 = array1.ToArray(); // これも可 var array2 = array1.Clone() as int []; // これも可 (キャスト必要)要素へのアクセスやインデックス取得
スライス (Slice)
Python(numpy)array[0:5] # 0-4番目の5要素 array[5:] # 0-4の5要素を飛ばして 5- array[3:5] # 0-2を飛ばして 3-4 の2要素C#array.Take(5) // 0-4番目の5要素 array.Skip(5) // 0-4の5要素を飛ばして 5- array.Skip(3).Take(2) // 0-2を飛ばして 3-4 の2要素 list.GetRange(3, 2) // Listならこれも可条件を満たす要素のインデックスを取得
Python(numpy)idx_lst = np.where(lst == val)C#var indexList = list.Select((x, i) => new { x, i }) .Where(xi => xi.x == val).Select(xi => xi.i);最小(最大)の値を取る要素のインデックスを取得
Python(numpy)arr = np.array([2, 1, 8, 4, 0, 8]) # 空でないとする idx = np.argmin(arr) # 最小値をとる最初の要素(0)のインデックス (4) idx = np.argmax(arr) # 最大値をとる最初の要素(8)のインデックス (2)C#var list = new List<int> { 2, 1, 8, 4, 0, 8 }; // var list = new List<int>(); // 空のケースも考慮するならコメントアウトした方 var argmin = list.Select((x, i) => new { x, i }) .Aggregate((min, xi) => xi.x < min.x ? xi : min).i; // 4 // .Aggregate(new { x = int.MaxValue, i = -1 }, (min, xi) => xi.x < min.x ? xi : min).i; // 4 var argmax = list.Select((x, i) => new { x, i }) .Aggregate((max, xi) => xi.x > max.x ? xi : max).i; // 2 // .Aggregate(new { x = int.MinValue, i = -1 }, (max, xi) => xi.x > max.x ? xi : max).i; // 2空のリストにも対応するなら
Aggregateの第一引数にシードを与えます。「特定のキーで
Min()やMax()すると、そのキーの最小(最大)値だけが戻ってきてしまうが,要素のインデックスもしくは(キーを含む)オブジェクトそのものを取得したい」という質問は昔から StackOverflow でも何度も出ているようです。(Qiita でも。)別途実装せず LINQ でシンプルに書くには、例えば以下のやり方があります。
Aggregateを使う(上のコード) -> O(n) なのでよいが可読性やや難ありOrderByやOrderByDescendingしてFirstやTake-> ソートで O(n log(n)) になるMinやMaxで取得した最小(最大)値でWhere-> 2回走査してしまうn が小さければ、可読性重視で 2, 3 もありかも。
再利用性も考え、素直にループ(イテレータ)で実装しておくか、すでにある実装を使うのもよさそう(MoreLINQ のMinByやMaxByなど)。リスト操作
よく使うリスト操作の対応です。
Python(標準リスト)lst.append(val) # 末尾へ値を追加 lst.extend(lst2) # 末尾へ別のリストを追加 lst.insert(idx, val) # idx へ値を挿入 lst[idx:idx] = lst2 # idx位置に lst2の要素すべてを挿入 lst.clear() # 要素をすべて削除 lst.remove(val) # 最初に見つかった val の値をもつ要素削除 del lst[idx:idx+len] # idx位置から len個の要素を削除 del lst[-1] # 末尾を削除C#list.Add(val); // 末尾へ値を追加 list.AddRange(list2); // 末尾へ別のリストを追加 list.Insert(idx, val); // idx へ値を挿入 list.InsertRange(idx, list2); // idx位置に list2の要素すべてを挿入 list.Clear(); // 要素をすべて削除 list.Remove(val); // 最初に見つかった val の値をもつ要素削除 list.RemoveRange(idx, len); // idx位置から len個の要素を削除 list.RemoveAt(list.Count - 1); // 末尾を削除 // その他の操作 list.RemoveAll(v => v == val); // ラムダ式で削除する要素の条件を指定 (左例は val 値をもつ要素全て削除)文字列操作
リストが絡む文字列操作に限定して取り上げます。
split, join
Pythonstr1 = 'pen-pineapple-apple-pen' str1_list = str1.split('-') str2_list = ['pen', 'pineapple', 'apple', 'pen'] # str1_list str2 = '-'.join(str2_list) # str1C#string str1 = "pen-pineapple-apple-pen"; string[] str1Array = str1.Split('-'); // Split は配列を返す List<string> str1List = str1Array.ToList(); // リストが必要なら変換 List<string> str2List = new List<string> {"pen", "pineapple", "apple", "pen"}; string str2 = string.Join("-", str2List); // 可変長引数で文字列を渡すことも可能 // string str2 = str2List.Aggregate((x, y) => x + "-" + y); // 空リストだとInvalidOperation // string str2 = str2List.Aggregate(string.Empty, (x, y) => x + "-" + y).TrimStart('-');しばらく別の言語で書いていると、Python の
joinは書き方に迷います。C# の
Splitでは、セパレータに char を渡すときは単純ですが、文字列を渡す場合は第二引数のオプション指定(StringSplitOptions.Noneなど)が必要です。
Joinは基本文字列を渡します・・・ややこしいですね。
string.Joinの代わりにAggregateを使うとメソッドチェーンで書けますが、こちらの記事で議論されているようにいくつか問題があります。
まず、空のリストが渡されると例外が投げられます。初期値として空文字を渡すと例外を回避できますが、今度は先頭にもセパレータがついてしまうため、これを削除(上の例ではTrimStart)する必要があります。StackOverflow では以下のような書き方も紹介されていました。パフォーマンスも
string.Join並みによいということです。C#/LINQのAggregateで文字列を結合string strJoin = strList.Aggregate(new StringBuilder(), (x, y) => x.Append(x.Length==0 ? "" : "-").Append(y)).ToString();ただ、いくつかの記事やスレッドを追った感じでは、あえて
Aggregateにせず、素直にstring.Joinする方がよい、という結論が多いようです。print とフォーマット指定
Pythonlst = [1/2, 3/4, 5/6] print(lst) # [0.5, 0.75, 0.8333333333333334] print("[" + ", ".join("%.2f" % x for x in lst) + "]") # [0.50, 0.75, 0.83] arr = np.array([1/2, 3/4, 5/6]) print(arr) # [0.5 0.75 0.83333333] print(np.array_str(arr, precision=2)) # [0.5 0.75 0.83] print(np.array2string(arr, precision=2, floatmode='fixed')) # [0.50 0.75 0.83] # array2string はオプション豊富.小数点以下の桁数を固定して0埋めできる # array_str の中身は array2string とのこと # numpy は以下でも可(一時的に使うなら元の設定に戻しておく) orig_printopt = np.get_printoptions() # 現在の設定を辞書へ np.set_printoptions(precision=2, floatmode='fixed') print(arr) # [0.50 0.75 0.83] np.set_printoptions(**orig_printopt) # 設定を戻す(辞書を展開して引数へ)C#var list_print = new List<double> {1.0/2, 3.0/4, 5.0/6}; Console.WriteLine("[" + string.Join(", ", list) + "]"); // [0.5, 0.75, 0.833333333333333] Console.WriteLine("[" + string.Join(", ", list.Select(x => x.ToString("0.00"))) + "]"); // [0.50, 0.75, 0.83]ソート
昇順・降順ソートと安定性
Pythonsorted_lst = sort(lst) # 昇順ソートされたリストが返る lst.sorted() # lstそのものが昇順ソートされる (in-place) sorted_data = np.sort(arr) # 昇順ソート sorted_data = np.sort(arr)[::-1] # 降順ソートC#Array.Sort(array); // arrayそのものが昇順ソートされる (inplace) Array.Reverse(array); // 昇順ソート後にひっくり返して逆順へ list.Sort(); // listそのものが昇順ソートされる list.Reverse(); // 昇順ソート後にひっくり返して逆順へ // ラムダ式を使うと逆順ソートも一回で Array.Sort(array, (x, y) => y - x); // arrayそのものが逆順ソートされる list.Sort((x, y) => y - x); // listそのものが逆順ソートされる // LINQを使ってもソートできる(特に複数のキーがある場合は便利.リストでも利用可) sorted_array = array.OrderBy(x => x); // 昇順ソート(戻り値で受け取る) sorted_array = array.OrderByDescending(x => x); // 降順ソート(戻り値で受け取る)List の内部は配列なのでソートのアルゴリズムは同じとか。
ソートアルゴリズムの安定性について:Reverse()はちょっと強引ですが、もともとArray.Sort()も安定ソートではないので、同じ値のデータの順序を気にしない場合はありでしょう。
一方で、LINQ のOrderBy()は安定ソートとのこと(Stack Overflow より)。安定なソートが欲しい場合は LINQ が手っ取り早いかも。ちなみに Python では list のソートは安定で、numpy でも 'stable' をオプションで渡すと安定性が保証されます。
ソートされたデータの添え字
Pythonidx = np.argsort(arr)C#(array1とインデックスのソート結果をarray2とidxへ)// Array.Sort で int[] array2 = new int[array1.Length]; // ソート結果を別配列にするための準備 array1.CopyTo(array2, 0); // array1 を array2 にコピー int[] idx = Enumerable.Range(0, array2.Length).ToArray(); // 連番の配列 0,1,... Array.Sort(array2, idx); // 別配列も一緒に並べ替えるオーバーロードがあるのでこれを使う for(var i=0; i < array2.Length; i++) // ついでに表示 { Console.WriteLine($"key: {array2[i]}, idx: {idx[i]}"); } // LINQ で var sortedPair = array1 .Select((x, i) => new KeyValuePair<int, int>(x, i)) .OrderBy(x => x.Key); var array2 = sortedPair.Select(x => x.Key).ToList(); var idx = sortedPair.Select(x => x.Value).ToList(); // 分かりやすい名前でタプル(ペア)を作りそのまま使ってしまうのもあり var sortedPair = array1 .Select((x, i) => (Key: x, Index: i)) // C# 7 (.NET Framework 4.7 など) .OrderBy(x => x.Key) .ToList(); sortedPair.ForEach(x => { // ついでに表示 Console.WriteLine($"key: {x.Key}, idx: {x.Index}"); });この Stack Overflow の記事を参考にしました。
二つの系列をまとめてソート
C#var list1 = new List<int> { 3, 4, 1, 2 }; var list2 = new List<int> { 6, 7, 8, 9 }; var zipped = list1.Zip(list2, (x1, x2) => new {key1 = x1, key2 = x2}); var sorted = zipped.OrderBy(x => x.key1).Select(x => x.key2); // 8, 9, 6, 7基本的な統計量や数学関数
基本的な統計量
Pythonnp.mean() np.std() np.max() np.min() np.argmax()C#double mean = data.Average(); double varP = data.Select(x => (x - mean) * (x - mean)).Sum() / data.Length; double stdevP = Math.Sqrt(var);そろそろ Math.NET などパッケージを入れたほうがよいかもしれません。
bool型の統計量
Pythonlst = [True, False, True] sum(lst) # 2 any(lst) # True all(lst) # FalseC#var list = new bool[] { true, false, true }; list.Count() // Sum() ではない list.Any(v => v) // true list.All(v => v) // false数学関数
Pythonnp.round()C#Math.Round()多次元配列
以下書きかけです。リストのリストを矩形行列としてみた操作(つまり内側のリストはすべて同じ長さと仮定)。
初期化
Pythonmat = np.zeros((NROWS, NCOLS))C#List<List<double>> mat = Enumerable.Range(0, NROWS).Select(_ => new double[NCOLS].ToList()).ToList();
Repeatではなくそれぞれ実体化していく。行や列をコピー
Pythonarr = mat[i,:].copy() arr = mat[:,j].copy() arr = mat.flatten()C#var list = mat[i]; var list = mat.Select(r => r[j]).ToList(); var list = mat.SelectMany(r => r).ToList(); // flatten列ごとの平均
Pythonmat.mean(0)C#var ret = mat.SelectMany(r => r.Select((val, j) => new {val, j})) .GroupBy(x => x.j, (key, y) => {return y.Average(z => z.val);});いったん
{val, j}を要素とするxへ平坦化したあとに、インデックスjでグルーピングしてyとし、それぞれのグループで平均を計算している。参考: https://teratail.com/questions/76764
その他(未テスト)
DeepCopy
- https://stackoverflow.com/questions/129389/how-do-you-do-a-deep-copy-of-an-object-in-net-c-specifically
- http://www.atmarkit.co.jp/ait/articles/1705/24/news040.html
転置
- 投稿日:2019-10-28T09:54:48+09:00
Taskじゃない非同期をTaskに変換したい(TaskCompletionSource)
はじめに
Task(UniTask)は便利です。
特にこの昨今awaitで結果を待てるので手続き処理っぽく書きつつ非同期処理が書けます。でも、 Taskじゃない非同期処理はどうしましょう。
例えばボタンのクリック。 ボタンのクリックはユーザーが気が向いたときにするものなので非同期です。
PlayFabやNCMBといったmBaasのAPI呼び出しなどは通信中画面が止まっては困るのでコールバックで結果(もしくはエラー)を受け取るようになっています。 これもまた非同期です。
こういった「非同期だけれど、Taskではない」
をTaskに変換してくれる便利な奴がいます。 それが TaskCompletionSource(UniTaskCompletionSource)です。
※この記事では基本Task/TaskCompletionSourceを使いますが、適宜UniTask/UniTaskCompletionSourceに読みかえてください
基本形
public Task<int> GetIntAsync(){ var tcs = new TaskCompletionSource<int>(); return tcs.Task; }intを返すTask
Task<int>に対応するのはTaskCompletionSource<int>ですし
floatを返すTaskTask<float>に対応するのはTaskCompletionSource<float>です。この返却したいTaskに対応した TaskCompletionSource を new して、 Task をreturn するのが基本形です。
しかし、このメソッドのTaskはいつまでたっても結果を返しません。 それはそう。 誰も結果を返してないからです。
結果のセット
結果を返すにはこのTaskCompletionSourceのインスタンスに
TrySetResultを呼ぶ必要があります。
(なお、キャンセルする場合はTrySetCanceled()を呼ぶ必要がありますが、それはまたの機会に・・・)public Task<int> GetIntAsync(){ var tcs = new TaskCompletionSource<int>(); tcs.TrySetResult(32768);//結果のセット return tcs.Task; } public async Task RunAsync(){ var num = await GetIntAsync(); Debug.Log(i); //→ 32768 }これで、このメソッドはTaskの結果として32768を返すようになります。
さて。これは非同期でしょうか。違いますね。 これではreturn Task.FromResult(32768);と同じです。しかし、ここで一つとても重要なことがあります。それはこの TrySetResultは非同期で呼んでも構わないということです。
と言われても良くわからない方もいるでしょう。
例えば。 最初に例で出した ボタンのクリック を絡めてみます。例1 指定したボタンをクリックしたらintを返却するTask
まず、基本形を作ります
public Task<int> ButtonClickAsync(){ var tcs = new TaskCompletionSource<int>(); return tcs.Task; }「指定したボタンを」とあるので、引数でボタンを受け取ります
public Task<int> ButtonClickAsync(Button btn){ var tcs = new TaskCompletionSource<int>(); return tcs.Task; }ボタンがクリックされたら、結果を返却するようにします。 結果を返却するには
TaskCompletionSourceのTrySetResultを呼べば良いのでした。public Task<int> ButtonClickAsync(Button btn) { var tcs = new TaskCompletionSource<int>(); btn.onClick.AddListener(() => tcs.TrySetResult(65536));//ボタンクリックイベントのコールバックで結果セット return tcs.Task; }完成です。これで、ボタンをクリックするとこの
tcs.Taskは 65536 を返却して終了するようになります。
簡単ですね。無事 Taskじゃない非同期処理をTask化できました※これだとbtnのonClickイベントが登録されっぱなしじゃないか!と思った貴方。大正解です。本当はちゃんと解除してあげてください。
では、次はほんのちょっと応用です。 (今となってはObsoleteな)WWWクラスをTask化してみます
例2 指定したURLからページ情報を取得するTask
これまた、基本形を作ります。 ページ情報なので Task は
Task<string>TaskCompletionSourceもTaskCompletionSource<string>にします。public Task<string> WWWAsync(){ var tcs = new TaskCompletionSource<string>(); return tcs.Task; }「指定したURLから」とあるので、引数でurlを受け取ります
public Task<string> WWWAsync(string url){ var tcs = new TaskCompletionSource<string>(); return tcs.Task; }WWWクラスはコルーチンで結果を待つ仕組みなので、StartCoroutineで指定したurlからWWWでコンテンツを取得するようにします。
public Task<string> WWWAsync(string url){ var tcs = new TaskCompletionSource<string>(); StartCoroutine(WWWGet(url)); return tcs.Task; } private IEnumerator WWWGet(string url) { var www = new WWW(url); yield return www; //wwww.text; //TODO ここにダウンロードしたコンテンツが入っている }結果を返却するには
TaskCompletionSourceのTrySetResultを呼べば良いのですが、コルーチンの結果は(基本的には)取得できないので、WWWGetの引数にTaskCompletionSource<string>を追加して、WWWGetの中で結果を入れてもらうように修正します。public Task<string> WWWAsync(string url) { var tcs = new TaskCompletionSource<string>(); StartCoroutine(WWWGet(url,tcs)); return tcs.Task; } private IEnumerator WWWGet(string url, TaskCompletionSource<string> tcs) { var www = new WWW(url); yield return www; tcs.TrySetResult(www.text); }完成です。 コルーチンによる非同期もTaskにすることが出来ました。
合体!
完全に蛇足ですが、上記二つの例を組み合わせると、「ボタンがクリックされるまで待ち、クリックされたら指定されたURLにアクセスしてコンテンツを取得する」Taskが作れます。
private async Task RunAsync() { var i = await ButtonClickAsync(_btn);//指定したボタンがクリックされるのを待つTask Debug.Log(i); var content = await WWWAsync("http://yahoo.co.jp");//指定したURLのコンテンツを取得するTask Debug.Log(content); }んー。すっきりしてますね!
最後に
今回は非常にシンプルな例を挙げましたが、使ってみるとっかかりにでもなれば幸いです。
Taskにしておくと結果が取得しやすいのはもちろんのこと、
Task.WhenAnyやTask.WhenAllを使って、複数あるうちのどれか一つのTaskが完了待ちや全てのTask完了待ちなど、ただの非同期処理では難しかった非同期処理同士の連携が簡単にできるようになったりするので(状況に応じて)Task化進めるのも良いと思います。補足
- 基本形として
TaskCompletionSourceをローカル変数で作りましたが、メンバ変数でも構いません。 というか、その方が便利な場合も多々あります。 ただし、TaskCompletionSourceの使いまわしはできません。 1回 SetResultしたらそれで終わりなので、また同様のTaskを呼ぶ場合はTaskCompletionSourceを再インスタンス化する必要があります。- ボタンクリックや、wwwの待ち等は実は UniRX.Async を使っていればこんなことせずに await が可能です。 (ボタンクリック待ちのTaskは
.OnClickAsync();が用意されています)今回の内容はあくまでもTaskCompletionSourceの使い方の簡単な説明なのであしからず。TaskCompletionSourceには、結果を返さないただのTaskがありません。 そのため別に返却する値が無い場合でもTaskCompletionSource<object>とし、TrySetResult(null);とやることがよくあります。 が、UniTaskCompletionSourceには非Generics版が用意されているので、返却値が無い場合はそちらを使用しましょう。- イベント周りはUniRX使った方が適切なケースも多いです。適宜使い分けましょう。
よい。Taskライフを。
- 投稿日:2019-10-28T09:53:28+09:00
[WPF] MVVM的にファイルを開くダイアログをコマンドとして実装する
はじめに
ここでのファイルを開くダイアログとは、Microsoftが提供している
WindowsAPICodePackのCommonOpenFileDialogのことを指します。入っていない方はNugetからインストールしておいて下さい。
FormsやWin32のOpenFileDialogを使用したい場合(いるのかどうかわかりませんが)は適宜読み替えて下さい。
PrismなどのMVVMインフラは利用しません。概要
WPFアプリケーションでファイルを開くダイアログを使いたい時ってありますよね。
ファイルを開くダイアログはつまるところViewなので、ViewModelでインスタンスを作ってShowDialogして…というのはNGです。具体的には次のようなコードですね。' NG例 Public Class HogeViewModel ' ... Private Sub OpenFile() Dim dialog As New CommonOpenFileDialog() If dialog.ShowDialog() = CommonFileDialogResult.Ok Then Dim fileName As String = dialog.FileName ' ... End If End Sub ' ... End Classこれじゃイカンということで、世の中にはアクションやビヘイビアを作ってコールバックを渡したり、ファイルを開くダイアログ用のサービスを作ったりするアプローチが存在します。
今回は、ViewModelで定義されたコマンドのExecuteに選択されたファイル名を渡すラッパーコマンド(?)のようなものを作って解決してみようと思います。クラス定義
ICommandを実装した、OpenFileDialogCommandクラスを作成します。OpenFileDialogCommand.vbPublic Class OpenFileDialogCommand Inherits DependencyObject Implements ICommand #Region "Dependency Property Registration" Public Shared ReadOnly CommandProperty As DependencyProperty = DependencyProperty.RegisterAttached("Command", GetType(ICommand), GetType(OpenFileDialogCommand)) #End Region #Region "Properties" Public Property Command As ICommand Get Return GetValue(CommandProperty) End Get Set(value As ICommand) SetValue(CommandProperty, value) End Set End Property #End Region #Region "Methods" ''' <summary> ''' コマンドの実行可否を手動で更新します。 ''' </summary> Public Sub RaiseCanExecute() RaiseEvent CanExecuteChanged(Me, EventArgs.Empty) End Sub #End Region #Region "ICommand" Public Function CanExecute(parameter As Object) As Boolean Implements ICommand.CanExecute If IsNothing(Me.Command) Then Return False Else Return Me.Command.CanExecute(parameter) End If End Function Public Custom Event CanExecuteChanged As EventHandler Implements ICommand.CanExecuteChanged AddHandler(value As EventHandler) AddHandler CommandManager.RequerySuggested, value End AddHandler RemoveHandler(value As EventHandler) RemoveHandler CommandManager.RequerySuggested, value End RemoveHandler RaiseEvent(sender As Object, e As EventArgs) CommandManager.InvalidateRequerySuggested() End RaiseEvent End Event Public Sub Execute(parameter As Object) Implements ICommand.Execute Dim dialog As New CommonOpenFileDialog() Dim res As CommonFileDialogResult = dialog.ShowDialog() If res <> CommonFileDialogResult.Ok Then Return If IsNothing(Me.Command) = False Then If Me.Command.CanExecute(parameter) Then Me.Command.Execute(dialog.FileName) End If End Sub #End Region End Class使い方は、
Commandプロパティにメインとなる(Executeにファイル名を渡したい)コマンドをバインドするだけです。次のような感じです。<Button Content="開く"> <Button.Command> <local:OpenFileDialogCommand Command="{Binding HogeCommand}" /> </Button.Command> </Button>一件落着…と言いたいところですが、このままだとバインディングエラーが吐かれます。
System.Windows.Data Error: 2 : Cannot find governing FrameworkElement or FrameworkContentElement for target element. 以下略コードに問題はないはずなのにバインディング先が見つからんと言われます。
結論から言うと、DataContextが引き継がれていないためこのようなエラーが吐かれます。
DataContextを引き継ぐようにするには、Freezableを継承する必要があります。FreezableはDependencyObjectを継承しているので継承部分だけ書き換えればOKです。OpenFileDialogCommand.vbPublic Class OpenFileDialogCommand Inherits Freezable Implements ICommand ' ... #Region "Freezable" Protected Overrides Function CreateInstanceCore() As Freezable Return New OpenFileDialogCommand() End Function #End Region End Class
Freezableを継承したクラスは、CreateInstanceCoreメソッドをオーバーライドする必要があります。ここでは自身のクラスのインスタンスを返せば良いです。
これでバインディングエラーはなくなるはずです。おわりに
このままだとダイアログのプロパティを何も設定せずに表示するので使い勝手が悪いです。なので必要に応じてプロパティをXamlから設定できるようにしておくと良いでしょう。
追記
2019/10/28 DependencyObjectが抜けていたのを修正しました。
- 投稿日:2019-10-28T09:12:28+09:00
内容
1.基本UI
2.カードデータPrefab作成
3.デッキのランダム生成
4.デッキから手札へカードドロー
5.手札がMAXならこれ以上ドローしない
6.デッキ枯渇したらこれ以上ドローしない
7.ターン交代でアクティブプレイヤー交代
8.ターン開始時ドロー
9.リスタートでオブジェクト初期化
10.ターン交代ボタン設置
11.
- 投稿日:2019-10-28T03:59:51+09:00
Error: rzc generate exited with code 2
環境:
Visual Studio for Mac Community 8.1.3
.NET Core 2.2
ASP.NET MVC突然以下のようなエラーになり、手詰まりになる。どこが悪いのかも教えてくれない。
/usr/local/share/dotnet/sdk/NuGetFallbackFolder/microsoft.aspnetcore.razor.design/2.2.0/build/netstandard2.0/Microsoft.AspNetCore.Razor.Design.CodeGeneration.targets(5,5): Error: rzc generate exited with code 2.Razor View内でコメントを書いただけで上のエラーになる。
これは問題なし(No Problem)
@* a *@更に二重にコメントアウト。これが気に入らないらしい。(Error!)
@*@* a *@*@こちらで議論されているが解決に至っていない様子。SDKを再インストールだの・・・。
https://developercommunity.visualstudio.com/content/problem/403142/rzc-generate-exited-with-code-2.htmlちなみに、Windows版 Visual Studioはわかりやすいエラーになり、問題なし。
A space or line break was encountered after the "@" character. Only valid identifiers, keywords, comments, "(" and "{" are valid at the start of a code block and they must occur immediately following "@" with no space in between.
- 投稿日:2019-10-28T01:27:16+09:00
.NET MVC Frameworkにおける属性チェックの基本と実装について
環境
- Visual Studio 2019
- .NET Framework 4.7.2
- .NET MVC 5.2.7.0
検証の為の属性の付与と、その概要
.NET MVCでは、以下のようにSystem.ComponentModel.DataAnnotations namespaceに属する各Attributeクラスのメソッドを"[]"で囲んだ記法によって、POST時に必須等の属性チェックを行うことができる。
※例えば、
[Required]は上記namespaceのRequiredAttributeクラス(ValidationAttributeクラスを継承)に属している。HelperViewModel.csusing System.ComponentModel.DataAnnotations; namespace HtmlHelperTest.ViewModels { public class HelperViewModel { [Required(ErrorMessage = "{0}は必須です。")] [StringLength(10, ErrorMessage = "{0}は{1}文字以内で入力してください。")] [Display(Name = "サンプルプロパティ")] public string TmpProperty { get; set; } } }個別の属性については説明を省くが、上記のような属性を付与した状態でPOSTすると、
@Html.ValidationMessageFor()を記述した箇所に以下のようにエラーメッセージをレンダリングする。
※個別の属性についてはSystem.ComponentModel.DataAnnotations namespaceを参照実際に出力されるエラーメッセージは各属性の
()内にあるErrorMessageで指定している。
ちなみに、[StringLength(10, ErrorMessage = "{0}は{1}文字以内で入力してください。")]のように{n}となっている個所にはそれぞれ、
{0}:[Display(Name = "")]で指定したプロパティの表示名称{1~n}:各属性の第一引数~第n引数が表示される。
エラーメッセージの表示位置について
前節で
@Html.ValidationMessageFor()に各プロパティの属性チェックのエラーメッセージがレンダリングされると書いたが、実際のViewファイルへの記述は以下のようになっている。Index.cshtml@model HtmlHelperTest.ViewModels.HelperViewModel @using (@Html.BeginForm()) { @Html.AntiForgeryToken() <div> <div> @Html.LabelFor(model => model.SumpleProperty) </div> <div> @Html.TextBoxFor(model => model.SumpleProperty) @Html.ValidationMessageFor(model => model.SumpleProperty) </div> <div> <input type="submit" value="OK" /> </div> </div> }他のHtmlHelper同様、
@Html.ValidationMessageFor()の引数にプロパティを指定するだけだ。
このような記述によって、
このように任意の場所にエラーメッセージを表示させることができる。
しかし、場合によっては画面上部や特定の場所にエラーメッセージをまとめて表示させたい場合もある。
そのような場合には、以下のように@Html.ValidationSummary()を追加して、第一引数にfalseを指定すればよい。Index.cshtml@model HtmlHelperTest.ViewModels.HelperViewModel @using (@Html.BeginForm()) { @Html.AntiForgeryToken() <!-- ↓変更箇所 コード追加↓ --> @Html.ValidationSummary(false) <!-- ↑変更箇所 コード追加↑ --> <div> <div> @Html.LabelFor(model => model.SumpleProperty) </div> <div> @Html.TextBoxFor(model => model.SumpleProperty) <!-- ↓変更箇所 エラー項目であることだけ分かるように"*"のみ表示↓ --> @Html.ValidationMessageFor(model => model.SumpleProperty, "*") <!-- ↑変更箇所 エラー項目であることだけ分かるように"*"のみ表示↑ --> </div> <div> <input type="submit" value="OK" /> </div> </div> }こうすることで、以下のように箇条書きでエラー一覧が表示されるようになる。
また、ここではエラー項目が分かるように"*"をエラーメッセージの代わりに表示させている。
ちなみに、@Html.ValidationSummary()の第二引数に箇条書きの上に表示させるメッセージを指定できる。
※ここでは@Html.ValidationSummary(false, "エラー一覧")として実行。Htmlタグに自動付与されるValidation関連クラス
ここまでで属性チェックによるエラーメッセージを任意の箇所に表示させることができたが、今のままだと以下のようにPOST前の状態でもエラーメッセージが表示されっぱなしになる。
GET時やエラーがない場合は非表示にしたい。
⇒@Html.ValidationSummary()やHtml.ValidationMessageFor()が
自動で付与してくれるクラスを利用する。
@Html.ValidationSummary()およびHtml.ValidationMessageFor()は、プロパティのエラーの有無によって自動的にHtmlタグへクラスを付与してくれる。
付与されるクラスの一覧は以下。
- validation-summary-valid:エラーがない場合、
@Html.ValidationSummary()の生成したdiv要素に付与。- validation-summary-errors:エラーがある場合、
@Html.ValidationSummary()の生成したdiv要素に付与。- input-validation-error:エラーがある場合、エラーとなったプロパティを使用している要素に付与。
- field-validation-valid:エラーがない場合、
Html.ValidationMessageFor()の生成したspan要素に付与。- field-validation-error:エラーがある場合、
Html.ValidationMessageFor()の生成したspsn要素に付与つまり、class="validation-summary-valid"の要素は非表示とし、class="validation-summary-errors"の要素は表示させればよいことになる。
これらは基本的にSite.css内に記載する。参考サイトによると以前はSite.cssに以下のような記述がデフォルトで用意されていたようだが、現在は自分で記載する必用があるようだ。
(もしかして今は別の方法がスタンダードになっている?)Site.css.field-validation-error { color: #ff0000; } .field-validation-valid { display: none; } .input-validation-error { border: 1px solid #ff0000; background-color: #ffeeee; } .validation-summary-errors { font-weight: bold; color: #ff0000; } .validation-summary-valid { display: none; }この状態でビルドすると、当然以下のように必用な時のみ表示させることができる。
(ついでにエラーメッセージが赤字になっている)
参考サイト:@IT 連載:ASP.NET MVC入門
- 投稿日:2019-10-28T00:00:03+09:00
【Xamarin】C#でAndroidアプリを作ってみる 2 (デバッグ実行)
概要
前回:C#でAndroidアプリを作ってみる 1 (Xamarinインストール)
今回は、ZodiacSignAppを実機でデバッグ実行してみます。プロジェクトのプロパティを設定する
- ZodiacSignAppプロジェクトのプロパティを開きます。
「アプリケーション」の「Androidバージョンを使用したコンパイル:(ターゲットフレームワーク)」を選択します。
※選択したAndroid SDKのバージョンがインストールされていない場合、エラーメッセージに従ってインストールします。実機を設定する
デバッグ実行する
まとめ
今回は、ZodiacSignAppを実機で動かしてみるところまでを行いました。
次回は、ZodiacSignApp(干支×星座算出アプリ)の画面を作ります。
次回分はこちら⇒C#でAndroidアプリを作ってみる 3 (画面作成1)






































