- 投稿日:2020-09-25T23:28:32+09:00
BluetoothからiPhoneを識別することはできなかったって話
背景
スマホが発信するBluetoothを検知すればICカードとか使わないで入退室通知ができるんじゃないかと思ったので色々調べてた
iosはペアリングするまでランダムなMACアドレスを送信する
らしいです。
参照:https://www.quora.com/Do-Bluetooth-devices-have-a-persistent-ID-like-a-MAC-address
実際 BluetoothLEAdvertisementReceivedEventArgs を使用して BluetoothAddress を取得してみましたが、iPhone の Bluetooth をオンにした時に検出されるアドレスは、毎回ランダムな2つの BluetoothAddress と数分置きにランダムになる一つの BluetoothAddress (こいつはUUIDを持ってた。UUIDも数分置きに一緒に変わる) でした。
ちなみに検索履歴の彼方に行ってしまってどこで見かけたかは分かりませんが、以下のコードで BluetoothLEAdvertisementReceivedEventArgs.BluetoothAddress からMACアドレスを取得できました。
private void hoge(BluetoothLEAdvertisementReceivedEventArgs args) { string macAddres = string.Join(":", BitConverter.GetBytes(args.BluetoothAddress).Reverse().Select(x => x.ToString("X2"))).Substring(6); }iosアプリから制作するなら、BluetoothでiPhoneを識別できるのかも
2014.04.03 マーケティングブログ iOS/Androidで端末を識別するIDまとめ https://iridge.jp/blog/201404/4836/
によると、どうやら簡単に端末を追跡できるものはセキュリティ的によろしくない、という流れがあったようです。
iosにも、UDID、MACアドレス、など端末を識別するIDはありますが、2014年の時点でUDID、MACアドレスは既に自由に使用できなくなり、代わりにアプリで使用できる identifierForVendor(IDFV)、AdvertisingIdentifier が提供されたとあります。僕がやりたかったBluetoothでiPhoneを識別して入退室管理、もアプリ制作から取り組めばできるのかもしれません。
おわりに
僕はアンドロイドは持ってないのでアンドロイドで検証はしていませんが、少なくとも iPhone をペアリングなし Bluetooth で識別するのは無理っぽい。無念
もしペアリングなし Bluetooth で iPhone を識別する方法を知っているという方がいたら、コメントしてくださると嬉しいです。
- 投稿日:2020-09-25T21:59:57+09:00
c# 画像をバイナリーデータとしてDBに保存する
DBに画像をバイナリーデータで保存する必要があったためのメモです。
なんだかんだ初ですのでちょっと沼りました。
ただの技術メモなのでご了承下さい。使用DBはPostgreです。
それでは早速
下記が画像をDBに突っ込む過程です。Bitmap bmp = new Bitmap(@"C:¥sample.jpg"); MemoryStream ms = new MemoryStream(); bmp.Save(ms,ImageFormat.jpeg); byte[] binaryData = ms.ToArray(); NpgsqlCommand cmd = new NpgsqlCommand(); cmd.Connection = 環境にあった設定 cmd.CommandText = ("INSERT INTO TableName (Column) VALUES (:binaryData)"); NpgsqlParameter param = cmd.Parameter,Add("binaryData",NpgsqlDbType.Bytea); param.Value = binaryData; cmd.ExecuteNonQuery();
Bitmap bmp = new Bitmap(@"C:¥sample.jpg");
Bitmapの引数に画像の場所を指定します。
MemoryStream ms = new MemoryStream();
メモリを使って読み取りなどできるやつです。
bmp.Save(ms,ImageFormat.jpeg);
Save(Stream, ImageFormat)
このイメージを、指定した形式で指定したストリームに保存します。らしいですね。
バイナリーデータに変換します。
byte[] binaryData = ms.ToArray();
バイナリーデータを取り出します。
cmd.CommandText = ("INSERT INTO TableName (Column) VALUES (:binaryData)");
NpgsqlParameter param = cmd.Parameter,Add("binaryData",NpgsqlDbType.Bytea);
param.Value = binaryData;
こちらについてはルールっぽい?
sql実行するための準備ですね。
https://www.postgresql.jp/document/9.4/html/datatype-binary.html
絶賛学習中です。
cmd.ExecuteNonQuery();
sql実行。続いて、バイナリーデータから画像表示です。
MemoryStream ms = new MemoryStream(binaryData); Bitmap bm = new Bitmap(ms); PictureBox.image = bm;
MemoryStream ms = new MemoryStream(binaryData);
先ほどのバイナリーデータをMemoryStreamで読んであげます。
Bitmap bm = new Bitmap(ms);
ビットマップに変換してあげます。
PictureBox.image = bm;
これで画像が表示できます。
- 投稿日:2020-09-25T19:27:12+09:00
Update your C# in Unity ~ 式形式の関数メンバー ~
Unityにおいて古いC#しか使えない時代もありました。しかし、それは過去のことです。本稿執筆時の最新LTSであるUnity 2019.4ではC# 7.3がサポートされています。また、本稿執筆時の最新Beta版であるUnity 2020.2ではC# 8.0がサポート予定です。
長らくUnityで古いC#しか使えなかったことで、「C#にこんな機能あるのか?知らなかった!」となることがある方も多いのではないでしょうか?この「Update your C# in Unity」シリーズでは、「C#の比較的新しい機能をUnityでこんな風に使えるよ!」という紹介を行います。
言語機能名: 式形式の関数メンバー
追加バージョン: C# 6.0で新規追加、C# 7.0で可能なメンバー追加
説明: メソッドやプロパティーなどのメンバーの実装が単一の式の場合、より簡潔にそのメンバーを記述できる機能
C#でコードを書いていると、単一のメソッドを呼び出しているだけのメソッドを書く時があります。例えば、次のようにStartCoroutineを呼び出してCoroutineを返すメソッドです。
using System.Collections; using UnityEngine; public class Launcher : MonoBehaviour { public Coroutine Launch() { return StartCoroutine(LaunchImpl()); } private IEnumerator LaunchImpl() { // 略 yield break; } }このようにメソッドの実装が単一の式で記述されている場合、より簡潔に式形式でメソッドを記述することができます。
using System.Collections; using UnityEngine; public class Launcher : MonoBehaviour { // 式形式で記述 // return や { や }がいらない public Coroutine Launch() => StartCoroutine(LaunchImpl()); private IEnumerator LaunchImpl() { // 略 yield break; } }
return
や{
、}
は処理の本質ではないボイラープレートな記述です。式形式の関数メンバーを活用することで、処理の本質のみを記述した簡潔な記述になりました。ちなみに次のように、返値がvoidなメソッドも記述できます。
using UnityEditor; public static class AssetUpdater { [MenuItem("Assets/ForceReserializeAssets")] private static void ForceReserializeAssets() => AssetDatabase.ForceReserializeAssets(); }式形式の関数メンバーの使い所として多いのは、単純な処理で実装されたToStringメソッドとゲッターオンリーのプロパティです。
[Serializable] public class Circle { [SerializeField] private float x; [SerializeField] private float y; [SerializeField] private float radius; public Circle(float x, float y, float radius) { this.x = x; this.y = y; this.radius = radius; } // フィールドをそのまま返すのにも使える public float X => x; public float Y => y; public float Radius => radius; // ロジックを記述したプロパティにも使える public float Area => Mathf.PI * radius * radius; // ToStringの実装にも使える public override string ToString() => $"Center ({X},{Y}) Radius:{Radius}"; }ゲッター・セッター両方あるプロパティーやインデクサーにも使えます。
[Serializable] public class State { [SerializeField] private int score; public State(int score) { this.score = score; } public int Score { get => score; set => score = value; } } public class Dungeon { private int[][] map; public int this[int i, int j] { set => map[i][j] = value; get => map[i][j]; } }「別に短くなっても対して嬉しくないんじゃないか?」という疑問を持った方もいるかもしれません。
その疑問への回答は、「短いメンバーがずらっとたくさん並んだ時に、式形式のメンバーで記述すると、非常に短くなって嬉しい」というものです。
例えば、Vector2のいくつかのオペレーターを、従来どおりの書き方で実装すると、おそらくこのようになるでしょう。public static Vector2 operator +(Vector2 a, Vector2 b) { return new Vector2(a.x + b.x, a.y + b.y); } public static Vector2 operator -(Vector2 a, Vector2 b) { return new Vector2(a.x - b.x, a.y - b.y); } public static Vector2 operator *(Vector2 a, Vector2 b) { return new Vector2(a.x * b.x, a.y * b.y); } public static Vector2 operator /(Vector2 a, Vector2 b) { return new Vector2(a.x / b.x, a.y / b.y); } public static Vector2 operator -(Vector2 a) { return new Vector2(-a.x, -a.y); } public static Vector2 operator *(Vector2 a, float d) { return new Vector2(a.x * d, a.y * d); } public static Vector2 operator *(float d, Vector2 a) { new Vector2(a.x * d, a.y * d); } public static Vector2 operator /(Vector2 a, float d) { return new Vector2(a.x / d, a.y / d); }
{
と}
でかなりスペースをとっています。
式形式でメンバーを記述することで嬉しいのは、このようにメンバーがずらっと並んだ時です。本質でない「return
や{
、}
」で行数やスペースを取らなくなります。これはこんな感じで短くなります。
public static Vector2 operator +(Vector2 a, Vector2 b) => new Vector2(a.x + b.x, a.y + b.y); public static Vector2 operator -(Vector2 a, Vector2 b) => new Vector2(a.x - b.x, a.y - b.y); public static Vector2 operator *(Vector2 a, Vector2 b) => new Vector2(a.x * b.x, a.y * b.y); public static Vector2 operator /(Vector2 a, Vector2 b) => new Vector2(a.x / b.x, a.y / b.y); public static Vector2 operator -(Vector2 a) => new Vector2(-a.x, -a.y); public static Vector2 operator *(Vector2 a, float d) => new Vector2(a.x * d, a.y * d); public static Vector2 operator *(float d, Vector2 a) => new Vector2(a.x * d, a.y * d); public static Vector2 operator /(Vector2 a, float d) => new Vector2(a.x / d, a.y / d);一覧した際に非常に短くなりましたね。
ちなみにコンストラクターでも活用できます。「読み取り専用の自動プロパティ」と「タプルの生成・分解」と組み合わせてこんな感じもかけます。(バリュータプルを使っているけど、内部的にはバリュータプルは生成されません)マイクロソフトの公式ドキュメントではたまにこの書き方を見かけます。
public class Point { public int X { get; } public int Y { get; } public Point(int x, int y) => (X, Y) = (x, y); } // 内部的にはこんな感じ // バリュータプルを使っているけど、内部的にはバリュータプルは生成されませんhttps://sharplab.io/#v2:EYLgtghgzgLgpgJwDQBMQGoA+ABATARgFgAoE7AZgAI9KAFAewEsA7GSgbxMu+qpbYAaHSgHM4MANyUAvlx4VK/SgE1hYyTLnctvOk1YAKJQA8ki1pQCeASkoBeAHyUDAs8tt3npq9YklZxEA=== //public class Point //{ // [CompilerGenerated] // [DebuggerBrowsable(DebuggerBrowsableState.Never)] // private readonly int <X>k__BackingField; // // [CompilerGenerated] // [DebuggerBrowsable(DebuggerBrowsableState.Never)] // private readonly int <Y>k__BackingField; // // public int X // { // [CompilerGenerated] // get // { // return <X>k__BackingField; // } // } // // public int Y // { // [CompilerGenerated] // get // { // return <Y>k__BackingField; // } // } // // public Point(int x, int y) // { // <X>k__BackingField = x; // <Y>k__BackingField = y; // } //}
式形式の関数メンバーを使うと、メソッドやプロパティーなどのメンバーの実装が単一の式の場合、より簡潔にそのメンバーを記述することができます。
「別に短くなっても対して嬉しくないんじゃないか?」という疑問を持った方もいるかもしれません。
しかし「短いメンバーがずらっとたくさん並んだ時に、式形式のメンバーで記述すると、非常に短くなって嬉しい」です。
ぜひ活用してください。
- 投稿日:2020-09-25T16:48:55+09:00
DI(Dependency Injection)のサンプル(コンストラクターインジェクション)
https://qiita.com/takmot/items/ad687ef6f5b058ddda67
前回記事に続いてコンストラクターインジェクションのDIサンプルです。ソースコード
ソースコードは前回のものを使用して、IAppService.csだけ追加しています。
├─interfaces
│ ├──IMessageService.cs
│ └──IAppService.cs (追加)
└─services
│ └──MessageService.cs
│
└─application.cs
└─Program.cs一応全ファイルのコードを載せます。
IMessageService.csnamespace di_sample.IF { public interface IMessageService { string Name { get; } string GetMessage(); } }IAppService.csnamespace di_sample.IF { public interface IAppService { void greeting(); } }サービスは同じインターフェースを継承したものを2個作っています。
サービス区別用にプロパティにNameを持っています。(前回と変化なし)MessageService.csusing di_sample.IF; public class MessageServiceMorning : IMessageService { public string Name { get; } = "Morning"; private string message = ""; public MessageServiceMorning() { message = "Good morning."; } public string GetMessage() { return message; } } public class MessageServiceAfternoon : IMessageService { public string Name { get; } = "Afternoon"; private string message = ""; public MessageServiceAfternoon() { message = "Good afternoon."; } public string GetMessage() { return message; } }以下applicationは、
IMessageService
型サービスのリストをコンストラクタでもらうようにします。
greeting()
がコールされると、
IMessageService
型サービスのリストからプロパティNameが"Morning"であるサービスのGetMessage()
をコールします。application.csusing System; using System.Collections.Generic; using Microsoft.Extensions.DependencyInjection; using di_sample.IF; namespace di_sample.APP { public class application : IAppService { IEnumerable<IMessageService> _services; // IMessageService型のサービスリスト public application(IEnumerable<IMessageService> Services) { _services = Services; } public void greeting() { writeMessage("Morning"); } private void writeMessage(string name) { foreach(var service in _services) { if (service.Name == name) { // プロパティでサービスを判定 Console.WriteLine(service.GetMessage()); } } } } }以下で
IAppService
インターフェースを持つapplicationを追加でDIコンテナに登録しています。
provider.GetService<IAppService>()
でIAppService
インターフェースを持つサービスを取り出し、そのサービスのgreeting()
をコールしています。Program.csusing System; using Microsoft.Extensions.DependencyInjection; using di_sample.APP; using di_sample.IF; namespace di_sample { class Program { static void Main(string[] args) { var services = new ServiceCollection(); // DIコンテナ services.AddSingleton<IMessageService, MessageServiceAfternoon>(); // サービス登録 services.AddSingleton<IMessageService, MessageServiceMorning>(); // サービス登録 services.AddSingleton<IAppService, application>(); // サービス登録 var provider = services.BuildServiceProvider(); var service = provider.GetService<IAppService>(); // IAppService型のサービスを取得 service.greeting(); Console.ReadLine(); } } }
provider.GetService<IAppService>()
を実行すると、
applicationのコンストラクタが呼ばれますが、
その前にapplicationのコンストラクタのパラメータ(IEnumerable<IMessageService> Services
)を取得するため、MessageServiceMorning
とMessageServiceAfternoon
のコンストラクタがコールされます。参考記事
https://qiita.com/TsuyoshiUshio@github/items/20412b36fe63f05671c9
- 投稿日:2020-09-25T16:12:59+09:00
DI(Dependency Injection)のサンプル
DI関連の記事をいくつか読んで、大体の使い方はわかったが、
自分で書かないと腑に落ちない部分もあったため、書いてみたサンプルです。
そのためDIの説明的なものは特に触れません。ソースコード
ベースは
dotnet new console
で生成したプロジェクトになります。
以下のようにinterfacesフォルダにインターフェースクラスを定義したファイルを、
servicesフォルダにインターフェースを継承したサービスクラスを定義したファイルを作成しています。├─interfaces
│ └──IMessageService.cs
└─services
│ └──MessageService.cs
│
└─application.cs
└─Program.csIMessageService.csnamespace di_sample.IF { public interface IMessageService { string Name { get; } string GetMessage(); } }サービスは同じインターフェースを継承したものを2個作っています。
サービス区別用にプロパティにNameを持っています。MessageService.csusing di_sample.IF; public class MessageServiceMorning : IMessageService { public string Name { get; } = "Morning"; private string message = ""; public MessageServiceMorning() { message = "Good morning."; } public string GetMessage() { return message; } } public class MessageServiceAfternoon : IMessageService { public string Name { get; } = "Afternoon"; private string message = ""; public MessageServiceAfternoon() { message = "Good afternoon."; } public string GetMessage() { return message; } }以下でDIコンテナを生成し、IMessageServiceインターフェースを持つクラス2つを登録します。
Program.csusing System; using Microsoft.Extensions.DependencyInjection; using di_sample.APP; using di_sample.IF; namespace di_sample { class Program { static void Main(string[] args) { var services = new ServiceCollection(); // DIコンテナ services.AddSingleton<IMessageService, MessageServiceAfternoon>(); // サービス登録 services.AddSingleton<IMessageService, MessageServiceMorning>(); // サービス登録 var app = new application(services); Console.ReadLine(); } } }以下で、サービスの取り出し、サービスの区別、サービスの持つ関数コールを行います。
using System; using System.Collections.Generic; using Microsoft.Extensions.DependencyInjection; using di_sample.IF; namespace di_sample.APP { public class application { IEnumerable<IMessageService> Services; // IMessageService型のサービスリスト public application(ServiceCollection services) { var provider = services.BuildServiceProvider(); Services = provider.GetServices<IMessageService>(); // IMessageService型のサービスを取得 writeMessage("Morning"); } private void writeMessage(string name) { foreach(var service in Services) { if (service.Name == name) { // プロパティでサービスを判定 Console.WriteLine(service.GetMessage()); } } } } }ちなみに、サービス(
MessageServiceMorning
,MessageServiceAfternoon
)のコンストラクタは、
サービスを取り出すタイミング(Services = provider.GetServices<IMessageService>();
)でコールされます。
using Microsoft.Extensions.DependencyInjection;
にはパッケージの追加が必要です。
https://www.nuget.org/packages/Microsoft.Extensions.DependencyInjectiondotnet add package Microsoft.Extensions.DependencyInjection --version 3.1.8コンストラクターインジェクションを使用したサンプルも書こうと思います。
参考記事
https://qiita.com/TsuyoshiUshio@github/items/20412b36fe63f05671c9
https://qiita.com/saeki4n/items/22a276dcac9ef537ee25
- 投稿日:2020-09-25T14:00:25+09:00
議論の前に、その前提を確認しておこう!(ソフトウエアの品質とは?)
1.問題提起
こちらのサイトでは様々な論が投稿されています。
僕は特に、プログラミング言語機能の解説や流行の開発パターンのメリットを語るものなど、根底にオブジェクト指向が潜むものに関心があります。しかし、本来は有益な内容であったとしても曖昧な部分があって、結局は役に立たなかったり、理解できなかったりするものも少なくないように思います。惜しい気がするのです。
これは僕にとっての問題というだけでなく、おそらく、多くの人がその知識や経験を実際に応用しようとしたときに役に立てにくい、といった問題になっていると思うのです。
1-1.曖昧であったり、抽象的な説明に終始
我々善良なエンジニアは、誰しも「より良いものを作りたい」と思っているはずです。そして、このサイトでは知識や経験を共有してお互いにより良いものを作れるようになりたい、という願いがあるのだと思います。
その、「より良いもの」ってなーに? (チコちゃん風に)
これをはっきりさせないために、目標がうまく定まらず、結果的に内容が曖昧になりがちなのではないかな、と思うのです。
1-2.誰にとっての問題か?
「こうすれば良くなりますよ」とか、「ああすればより安全なコードになりますよ」とか、
それがだれにとって良くなるのか、だれにとって安全になるのかが不明瞭な説明を多く見かけます。
自明であるかのような説明なのですが、いつの間にかその「だれ」が別な人に替わってしまっている、といった文章もありがちな気がします。それを読んで理解した気になっても、いざ実践の場で応用しようとするとうまく行かなかったりします。2.ソフトウエアの品質とは?
「より良いものを作りたい」、つまり、品質の高いソフトウエアを作りたいということですね。
では、ソフトウエアの品質とは具体的にはどういうことでしょうか。
まずはここを確認しておきましょう。
- 機能性
- 安定性
- 保守性
僕は、ソフトウエアの品質はこの3点に集約できると考えています。
そして、これらのどれに役立つ話なのか、しっかりと意識された文章が優れているのだと思います。2-1.機能性
使いやすさや処理速度等の性能、ゲームなどではその面白さまで含まれます。
企画や機能設計など、プログラミング以前のフェーズが大きく影響し、プログラミングにおいては主に処理速度(場合によっては実行モジュールの小ささ)のことを指します。
安定性や保守性とトレードオフの関係になることがあります。2-2.安定性
動作の安定性のことで、バグが含まれないことが求められます。
安定性と機能性は相反する場合がありますが、現在はハードウェアの性能が高くなった分労力を安定性に振り分ける余裕があるので、多くのプログラミングでこれがせめぎ合うケースは少ないと思われます。もちろん、リソースに余裕のないハードウェアをターゲットにしたり、処理速度に関する要求仕様が非常にシビアなジャンルもあり、全てが容易に両立できるわけではありません。
2-3.保守性
機能追加や修正のしやすさです。
他の要素に比べて見落とされがちですが、非常に重要な要素です。
機能性とはトレードオフの関係になる場合もありますが、安定性とはトレードオンの関係になる場合が多いと思われます。3.全ての立場を見つけよう
少なくとも、以下に挙げるいくつかの立場については常に明確に意識して(あえて曖昧にすることを含め)議論を進めたいものです。足りなければ是非追加してください。
そして、どの立場にとっての話なのかを明確にすることで格段に読者の理解が進むのではないでしょうか(文章の作者にとっても思考の助けになるはずと思います)。
逆に避けたいのは、全ての立場を含んだような言い回しかも知れません。まるで、神の立場でのお言葉のような。
たとえば、そんなオブジェクト指向の聖書のような書籍は存在しそうですが、役に立て難いのです。3-1.明日の自分は他人とみなす
人は忘れる生き物で、ソフトウエア制作の全工程を独りで行ったとしても、客観的な視点を忘れないようにしましょう、という言葉です。
ここでは客観的な視点、つまりコーディング担当者と、非担当者の2者が現れました。3-2.各プログラム担当者
実際にAという部分プログラムとBという部分プログラムの担当者が異なる場合もありますが、同一人物だったとしてもそれは異なる立場であると考えます。
なぜなら、部分のプログラム(関数だったりクラスだったり)にはプロバイダとユーザが存在するからです。プロバイダというのは、実際にそれを造る人やそのプログラムそのもの。ユーザはそれを使ってプログラムを作る人、あるいは使う側のプログラムそのものです。
もちろん、ほとんどのプログラムはプロバイダとユーザの両方の立場を含みます。この2者以外に忘れてならないのは、プログラムのプロバイダとユーザの両方を俯瞰する立場です。それは、3-1の非担当者とも共通する、言わば客観的な視点をもつ立場です。
3-3.その他の立場
プログラミングでは3-1、3-2等が考えられますが、完成したプログラムを前提にすれば、制作者と使用者といった立場などが考えられます。他にも見落としている立場があるかも知れませんので、随時追加して考えて見ましょう。
- 投稿日:2020-09-25T13:12:29+09:00
GridViewで表示を自分で制御したい
GridViewタグのプロパティは以下を設定
AutoGenerateColumns ⇒ DataBindしたときに、指定したColumn以外は表示しないようにする。
ViewStateMode ⇒表示だけの利用ならdisabledにすると余計な情報を送らなくて済む。<asp:GridView ID="grd01" runat="server" AutoGenerateColumns="False"> (中身は後述) </asp:GridView>asp:GridViewタグ配下の設定
- 構成は、 asp:GridView > columns > asp:TemplateField の順にする。
- asp:TemplateFieldタグ配下にHeaderTemplateタグ(ヘッダー行に利用)とItemTemplateタグ(表示モードの表内容)を用意する。
これで1つの列
<asp:TemplateField> <HeaderTemplate><!-- この中をHTMLで自由に装飾できる --></HeaderTemplate> <ItemTemplate><!-- この中をHTMLで自由に装飾できる --></ItemTemplate> </asp:TemplateField> : 必要な列分だけ繰り返す。asp:TemplateFieldタグ配下の設定
- HeaderTemplateタグの中には、一般のhtmlタグを利用できる。
- ItemTemplateタグの中には、一般のhtmlタグとaspのコントロールを設定できる。
- .cs側で設定したDataBindの値は<%# DataBinder.Eval(Container.DataItem, {列名} ) %>で注入できる。
こんな感じ
<asp:TemplateField> <HeaderTemplate><div>列01</div></HeaderTemplate> <ItemTemplate> <div> <asp:Label ID="iptCol01" runat="server" Text='<%# DataBinder.Eval(Container.DataItem, "col01") %>' /> </div> </ItemTemplate> </asp:TemplateField>まとめ(サンプル)
aspx側
<asp:GridView ID="grd01" runat="server" AutoGenerateColumns="False" ViewStateMode="Disabled" > <Columns> <asp:TemplateField> <HeaderTemplate><div>列01</div></HeaderTemplate> <ItemTemplate> <div> <asp:Label ID="iptCol01" runat="server" Text='<%# DataBinder.Eval(Container.DataItem, "col01") %>' /> </div> </ItemTemplate> </asp:TemplateField> <asp:TemplateField> <HeaderTemplate><div>列02</div></HeaderTemplate> <ItemTemplate> <div> <asp:Label ID="iptCol02" runat="server" Text='<%# DataBinder.Eval(Container.DataItem, "col02") %>' /> </div> </ItemTemplate> </asp:TemplateField> </Columns> </asp:GridView>aspx.cs(C#)側
//(ここから)実務ではDBから取得したデータをBindするが、サンプルなので、データを自作 DataTable table = new DataTable(); table.Columns.Add("col01",typeof(String)); table.Columns.Add("col02",typeof(String)); table.Columns.Add("col03",typeof(String)); DataRow row = table.NewRow(); row["col01"] = "ID 001"; row["col02"] = "サンプル太郎"; row["col03"] = "あいうえお"; table.Rows.Add(row); row = table.NewRow(); row["col01"] = "ID 002"; row["col02"] = "サンプル 次郎"; row["col03"] = "かきくけこ"; table.Rows.Add(row); //(ここまで)実務ではDBから取得したデータをBindするが、サンプルなので、データを自作 //C#側は、DataGridオブジェクトにデータを設定して、Bindするだけ。 grd01.DataSource = table; grd01.DataBind();
- 投稿日:2020-09-25T13:12:29+09:00
GridViewの表示を自分で制御したい
GUIを利用せずに自分で制御する方法を記載。
GridViewタグのプロパティは以下を設定
AutoGenerateColumns ⇒ DataBindしたときに、指定したColumn以外は表示しないようにする。
ViewStateMode ⇒表示だけの利用ならdisabledにすると余計な情報を送らなくて済む。<asp:GridView ID="grd01" runat="server" AutoGenerateColumns="False"> (中身は後述) </asp:GridView>asp:GridViewタグ配下の設定
- 構成は、 asp:GridView > columns > asp:TemplateField の順にする。
- asp:TemplateFieldタグ配下にHeaderTemplateタグ(ヘッダー行に利用)とItemTemplateタグ(表示モードの表内容)を用意する。
これで1つの列
<asp:TemplateField> <HeaderTemplate><!-- この中をHTMLで自由に装飾できる --></HeaderTemplate> <ItemTemplate><!-- この中をHTMLで自由に装飾できる --></ItemTemplate> </asp:TemplateField> : 必要な列分だけ繰り返す。asp:TemplateFieldタグ配下の設定
- HeaderTemplateタグの中には、一般のhtmlタグを利用できる。
- ItemTemplateタグの中には、一般のhtmlタグとaspのコントロールを設定できる。
- .cs側で設定したDataBindの値は<%# DataBinder.Eval(Container.DataItem, {列名} ) %>で注入できる。
こんな感じ
<asp:TemplateField> <HeaderTemplate><div>列01</div></HeaderTemplate> <ItemTemplate> <div> <asp:Label ID="iptCol01" runat="server" Text='<%# DataBinder.Eval(Container.DataItem, "col01") %>' /> </div> </ItemTemplate> </asp:TemplateField>まとめ(サンプル)
aspx側
<asp:GridView ID="grd01" runat="server" AutoGenerateColumns="False" ViewStateMode="Disabled" > <Columns> <asp:TemplateField> <HeaderTemplate><div>列01</div></HeaderTemplate> <ItemTemplate> <div> <asp:Label ID="iptCol01" runat="server" Text='<%# DataBinder.Eval(Container.DataItem, "col01") %>' /> </div> </ItemTemplate> </asp:TemplateField> <asp:TemplateField> <HeaderTemplate><div>列02</div></HeaderTemplate> <ItemTemplate> <div> <asp:Label ID="iptCol02" runat="server" Text='<%# DataBinder.Eval(Container.DataItem, "col02") %>' /> </div> </ItemTemplate> </asp:TemplateField> </Columns> </asp:GridView>aspx.cs(C#)側
//(ここから)実務ではDBから取得したデータをBindするが、サンプルなので、データを自作 DataTable table = new DataTable(); table.Columns.Add("col01",typeof(String)); table.Columns.Add("col02",typeof(String)); table.Columns.Add("col03",typeof(String)); DataRow row = table.NewRow(); row["col01"] = "ID 001"; row["col02"] = "サンプル太郎"; row["col03"] = "あいうえお"; table.Rows.Add(row); row = table.NewRow(); row["col01"] = "ID 002"; row["col02"] = "サンプル 次郎"; row["col03"] = "かきくけこ"; table.Rows.Add(row); //(ここまで)実務ではDBから取得したデータをBindするが、サンプルなので、データを自作 //C#側は、DataGridオブジェクトにデータを設定して、Bindするだけ。 grd01.DataSource = table; grd01.DataBind();
- 投稿日:2020-09-25T09:51:05+09:00
【C#】【DxLib】サウンドタイプを"WASAPI"や"DirectSound"や"ASIO"にしたい。【 WASAPI共有対応 】
概要
今回はDxLibの関数を利用して、サウンドタイプを"WASAPI"や"DirectSound"にする方法を紹介します。
誰でも簡単にできると思うので、やっていきましょう!やりたいこと
ソフトウェアのサウンドタイプを"WASAPI"や"DirectSound"にしたい。
要素
DX.DxLib_Init();
の前部に以下のコードを追加してください。DirectSound :
DX.SetEnableWASAPIFlag(DX.FALSE);
WASAPI(共有) :
DX.SetEnableWASAPIFlag(DX.TRUE, DX.FALSE);
WASAPI(排他) :
DX.SetEnableWASAPIFlag(DX.TRUE, DX.TRUE);
ASIO :
DX.SetEnableASIOFlag(TRUE);
使用例
//enumを利用したほうがわかりやすいと思いますが、今回はintで分岐処理を行います。 //type = 0: DirectSound //type = 1: ASIO //type = 2: WASAPI(排他) //type = 3: WASAPI(共有) int type = 0; switch (type) { case 0: DX.SetEnableWASAPIFlag(DX.FALSE); break; case 1: DX.SetEnableASIOFlag(DX.TRUE); break; case 2: DX.SetEnableWASAPIFlag(DX.TRUE, DX.TRUE); break; case 3: DX.SetEnableWASAPIFlag(DX.TRUE, DX.FALSE); break; default: goto case 3; }最後に
今回は以上です。
何か分からないことがあれば、ご気軽にコメント欄で聞いてください。
- 投稿日:2020-09-25T09:07:29+09:00
シンプルになぜinterfaceを使うのかを考察
それは実装をシンプルにして部品化を促すため
interfaceはカオスな現実世界と実装をつなぐ、あるいは分離する役割を担っています。
部品化しやすくするために、実装はできるだけシンプルにinterfaceでは多重継承を許し、一般クラスでは許さない理由
interfaceはカオスな現実世界に近い業務仕様を記述するため、多重継承を許容することで、実装レベルの一般クラスの複雑化を代替します。
※補足
多重継承を許容:言語によって差異はありますが、interfaceの意味を尊重するなら、一般クラスをinterfaceようにするなどして、多重継承を自主的に制限することがオブジェクト指向の本来の利点を活用することになると考えます。おまけ
interface間の関係性や条件を規定するのは、業務設計者です。
interface内に実装(処理)条件を持ち込まないように注意が必要です。
逆に実装(処理)にinteface間の(業務)条件を持ち込まないよう注意することが重要です。
業務と処理を関連付ける機能は双方の共通言語になります。
- 投稿日:2020-09-25T09:07:29+09:00
シンプルに「なぜinterfaceを使うのか」を考察
それは実装をシンプルにして部品化を促すため
interfaceはカオスな現実世界と実装をつなぐ、あるいは分離する役割を担っています。
部品化しやすくするために、実装はできるだけシンプルに、複雑さはinterfaceがブロック!interfaceでは多重継承を許し、一般クラスでは許さない理由
interfaceはカオスな現実世界に近い業務仕様を記述するため、多重継承を許容することで、実装レベルの一般クラスの複雑化を代替します。
※補足
シンプル化:問題領域を小さくすることで、問題の連鎖や波及を制限し、部品としての汎用性が向上します。
多重継承を許容:言語によって差異はありますが、interfaceの意味を尊重するなら、他の言語でも一般クラスをinterfaceのようにするなどして、多重継承を自主的に制限することがオブジェクト指向の本来の利点を活用することになると考えます。おまけ
interface間の関係性や条件を規定するのは、業務設計者です。
interface内に実装(処理)条件を持ち込まないように注意が必要です。
逆に実装(処理)にinteface間の(業務)条件を持ち込まないよう注意することが重要です。
業務と処理を関連付ける機能は双方の共通言語になります。
- 投稿日:2020-09-25T00:36:06+09:00
Blazor + canvas APIでアニメを描けるようにしてみた
はじめに
Blazorなどの学習を兼ねてアニメを描けるシンプルなお絵描きWebアプリを作りました。
Blazorを使っているとは口ばかりで、C#よりもTypeScriptを書いてることの方が多いです。
某謎アニ団さんぐらいアニメを描けたら良かったのですが、私では瞬きが限界です。現在は8fps(いわゆる三コマ打ち)のみですが、いずれはフレームレートを変更できるようにします。レートの変更だけなら楽ですが、アニメーション1秒の再生に24枚(24fps:日本のアニメの一般的なフレームレート)は普通描かないので、その空フレームに対する対応をする必要があります。
環境
Blazor WebAssembly 3.2.1 + .NET Standard 2.1
Microsoft.TypeScript.MSBuild 4.0.3
Firebase他、Blazorのテンプレートに内包されてたBootstrapなど。
実装について
Blazorと言いながらBlazorで行っているのはUIの制御だけです。
線の描き方
Canvas上で線を描く実装方法を探していたところ、
lineTo()
のメソッドを使用した例が多かったのですが、ペンを素早く動かした時に変な描き方になることがあり、ペンの軌道に合わせて円を置いていく形になりました。
ただし、そのままでは素早く描こうとしたときに線が切れてしまうので、以下のように微小な変化量を使って間を埋めるようにしています。しかし、現在では直線的な補完の仕方なので、もう少し曲線的な形を考えたい。数学を復習する必要がありそう。private prevX :number; private prevY :number; public drawLineWithPen(x:number, y:number, isDrawing:boolean){ let scaledX = x/this.scaleRate; let scaledY = y/this.scaleRate; if(!isDrawing) { this.context.beginPath(); this.context.moveTo(scaledX,scaledY); } else { // 分割数 let div = 200; let dx = (scaledX - this.prevX) / div; let dy = (scaledY - this.prevY) / div; let r = this.context.lineWidth/2; for(let i = 0; i<=div; i++){ let x = this.prevX + dx*i; let y = this.prevY + dy*i; this.context.beginPath(); this.context.moveTo(x,y); this.context.arc(x,y, r, 0, 2 * Math.PI, false); this.context.stroke(); this.context.fill(); this.context.closePath(); } } this.prevX = scaledX; this.prevY = scaledY; }ペンの入り抜きに関しては時間の差分をパラメータとして円の大きさや透明度を変えていくような形を考えています。
オニオンスキンの表示方法
オニオンスキンとは編集しているフレームの前後にあるフレームを特定の色で表示する機能です。今回は後方のフレームをピンク、前方のフレームを水色で表示しています。
あらかじめ保持しておいたImageDataの配列に対して、コピーを作り色の置換と透明度の設定を行っています。その後、Bitmapのデータを作成したのち、さらに
drawImage()
のメソッドを利用してオニオンスキン表示用のキャンバスへ書き込んでいます。ImageData
で直接canvasに書き込めれば良かったのですが、putImageData()
ではcanvasのすべてを置き換えてしまうのでこのような形になっています。private setOnionSkinsInternal(start:number, end:number, color:Color, frames: ImageData[], isPrev:boolean){ let startNum = Math.max(start, 0); let endNum = Math.min(end,frames.length); for(let i= startNum; i< endNum; i++) { let imageData = new ImageData(frames[i].data.slice(),frames[i].width,frames[i].height); for(let j=0;j<imageData.data.length; j+=4) { imageData.data[j] = color.r; imageData.data[j+1] = color.g; imageData.data[j+2] = color.b; // 現在のフレームから遠いフレームは透過度を強くして表示を薄くする if(isPrev) imageData.data[j+3] = imageData.data[j+3] * (i+1) / (endNum+1); else imageData.data[j+3] = imageData.data[j+3] * (startNum+1) / (i+1); } window.createImageBitmap(imageData).then( (img) => { // scale()で設定した倍率分、imageがさらに縮小されるので、倍率で割る? this.context.drawImage(img,0,0,this.width/this.scaleRate,this.height/this.scaleRate); } ).catch(() => { console.log(`${i} Error`)}); } }1枚あたり921,600かそれ以上のループが回るので、サイズを上げて10枚ほど表示しようとするとだいぶ重くなります。もう少し良い方法を考えたい。オニオンスキンの枚数分canvasを追加する……という方法も考えましたが試していません。
消しゴムとレイヤーの合成
https://hai3.net/blog/html5-canvas-eraser/
こちらの方法を参考にさせていただきました。ありがとうございます。
CanvasRenderingContext2D.globalCompositeOperation = "destination-out";
消しゴムでの描画時はこれを設定してあげればOKでした。https://developer.mozilla.org/ja/docs/Web/API/CanvasRenderingContext2D/globalCompositeOperation
MDNを読む限り、レイヤーの合成やペン先の水彩っぽさなどはこれを用いて実現できそうな感じがします。現在、消しゴムからペンへの切り替え方が、色を切り替えるかペン先サイズを変えるかしかないので、ちゃんとラジオボタン的な切替方法を作っておきたいと思います。
undo/redo
履歴保存用の
ImageData[]
を持っておいて、undo/redoメソッドが呼び出されたその都度PopやPushするだけです。履歴の保存に関してはTypeScript側で全て行うことも考えましたが、UI側で制御したいと思い、呼ぶタイミングはBlazor側に任せてあります。現状、実装が楽なので各フレーム毎にそれぞれ履歴を持つようになっていますが、フレームの削除を実装するタイミングで履歴の持ち方を変えそうです。public undo(){ if(this.currentFrame.prevHistory.length > 0) { let prev = this.currentFrame.prevHistory.pop() ?? new ImageData(this.width,this.height) ; this.currentFrame.nextHistory.push(this.getImageData()); this.putImageData(prev); } } public redo(){ if(this.currentFrame.nextHistory.length > 0){ let next = this.currentFrame.nextHistory.pop() ?? new ImageData(this.width,this.height); this.currentFrame.prevHistory.push(this.getImageData()); this.putImageData(next); } } public saveHistory(){ this.currentFrame.prevHistory.push(this.getImageData()); // 新しく履歴が追加された時、前方方向の履歴は削除する。 this.currentFrame.nextHistory = []; }canvas要素の優先順位
<canvas id="1"></canvas> <canvas id="2"></canvas> <canvas id="3"></canvas>イベントを拾うのが
id="1"
の場合、一番下に持っていかないとイベントが拾えません。TypeScript + dotnet CLI
dotnet add package Microsoft.TypeScript.MSBuild
のコマンドとjsonファイルの設定でdotnet run
した時などに.tsファイルもC#のコードと共にコンパイルされるので非常に楽でした。
Fableを使ってF#で書くのも考えたのですが、メソッド名の生成方法からメソッド名を指定しなければいけないJavaScript相互運用の仕様上厳しそうなので諦めました。今後追加する機能
GitHubのREADMEにやたらめったらと書きましたが、描き心地の問題は大きいので、線がカクついたり、入り抜きが一辺倒である問題は早めに解決したい。あとWeb Storage APIを使えば色やペンのサイズの設定値を保存できそうなので、そのあたりも対応していきたい。
タブレットやスマートフォン対応はやるとしてもだいぶ後の話になるでしょう。
私について
かつて、Windows Formアプリケーションを使ったシステム制作に関わっていたらしいニートです。
C#とCがほんの少しわかるだけで、TypeScriptなどの経験は特にありません。作っている意図
某お絵かきソフトは気軽にアニメーションを描いて動かせないし、アニメ制作するにしても使いにくいし、画面効果もないし、意外とこちら方面に目を向けたお絵描き(Web)アプリは無さそうだなという考えから。また、データベースの操作やログイン機能などがありませんが、機会・質などの問題は別として、どこかで誰かに見せたい時に見せられる用です。
元々はCanvas APIのお試しでアニメ機能などは考えていませんでしたが、作っているうちに機能を載せたくなってきて今に至ります。
おわりに
まだまだ実装したい機能や改善したい点など色々あるため、私のモチベーションが続く限りはやっていきたいですね。
TypeScriptはMicrosoftが開発に関わっているのもあり、C#に似ている部分があるのとdotnet CLIとの組み合わせもあって、コンパイル時にTypeScript側のエラーがある程度わかるのでだいぶ楽でした。Blazorも自身だけでブラウザのAPIに触れればいいのですが、IJSRuntimeを介してしかできないのでBlazor(C#)のみというのは厳しそうです。