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

[講座] Twitter Stream API を使って Azure でデータ分析 - Step1 (Sampled Stream V2 API)

こんにちは。ドクです。 久しぶりに Twitter API を使って、データ分析をしてみようと思います。前回は、第二次安倍内閣が誕生する前の衆議院選挙時に、Twitter API を使いましたが、面白い傾向が見てとれました。かなり昔ですね。。。 講座の最終目標 Twitter のツイート データを Azure Data Lake Storage Gen2 (ADLS Gen2) に Parquet 形式のファイルとして自動的に蓄積し、Azure Synapse Analytics - Serverless SQL pool を使って分析できるようにします。ツイート データの継続的な取得と ADLS Gen2 へのデータ蓄積には、Azure Functions を利用します。 Step1 の目標 2021 年 5 月現在、プレビューである Twitter - Sampled Stream V2 API を C# から扱えるようにすることです。このステップでは、Azure は関係なく、ローカル環境に単純なコンソール アプリケーションを作成し、動作させます。 Twitter API Twitter の開発者向けプログラムを通して提供される API です。各 API では要求に対するレート制限が設定されており、Stream API の一般的な無償利用の場合、50 万ツイート/月の取得制限が入ります。今回は、Twitter 社によってサンプリングされたツイートをリアルタイムに受け取れる Sampled Stream V2 API を利用します。 開発環境 (OSS) 開発には、以下を利用します。OSS (無償) かつ クロス プラットフォームとなりますので、Windows / Mac / Linux などお好きな OS/デバイスをご利用ください。 ツール : Visual Studio 2019 Community or VS Code ランタイム : .NET 5 or .NET Core 3.1 言語 : C# Twitter API 環境の整備 1. Twitter アカウントの作成 API 用の Twitter アカウントを作成します。携帯電話番号やメールアドレスなどを正しく登録してください。 2. Twitter API の利用登録 手順 (1) Twitter Developer サイト の右上にある 「Apply」をクリックします。 手順 (2) 以下ページの「Apply for a developer account」ボタンをクリックします。 手順 (3) ログイン後、開発者属性を選択します。個人による一般的な無償利用の場合、「Hobbyist」を選択します。 手順 (4) 利用目的や利用内容を入力します。以下は、Stream API を利用する上で回答が必要な最低限の項目となります。英語での記述となりますが、最低文字数をクリアしていれば、通常は問題なく承認されます。 <<回答を入力します>> <<回答を入力します>> <<回答を入力します>> 手順 (5) 登録/承認が上手く行くと、以下のような Developer ポータルの画面になります。登録中に API Key / API Secret が表示され、書き留めておく必要がありますが、書き留めておくのを忘れた場合、PROJECT APP の右側の鍵マークをクリックして、再生成 (Regenerate) することもできます。 手順 (6) 手順 5 の画面の PROJECT APP の右側の鍵マークをクリックして、Authentication Token を作成しておく必要があります。アプリ認証 (V2 API) では API Key / API Secret / Bearer Token が必要で、ユーザー認証 (v1 API) では API Key / API Secret / Access Token / Access Token Secret が必要になりますので、すべて書き留めておいてください。 Twitter API を呼び出す C# コードの開発 今回の手順では、Visual Studio 2019 を利用します。 1. Visual Studio 新規プロジェクトの作成 Visual Studio 2019 を起動し、「新しいプロジェクトの作成」で「C# コンソール アプリケーション」を選択してください。「C# コンソール アプリケーション (.NET Framework)」は選択しないように注意してください。.NET Core および .NET 5 以降は、クロス プラットフォームの OSS ランタイムとなりますが、.NET Framework は Windows 専用の商用版ランタイムで、4.8.x が最終バージョンとなり、新規開発は行われないものとなります。 2. Twitter API SDK 用 Nuget パッケージの取得 Visual Studio の「Nuget パッケージの管理」機能を使って、Twitter API 用の SDK (TweetinviAPI) を取得します。 3. ライブラリ参照の追加(コード) 以下は、サンプルコードの為の最低限のライブラリ参照となります。 using System; using System.Threading.Tasks; using Tweetinvi; 4. 認証とストリームの作成(コード) 以下は、Sampled Stream V2 API を利用するのに必要なアプリ認証、および、ストリーム作成のコードとなります。事前に控えておいた API Key / API Secret / Bearer Token の値で、以下のパラメーターを置き換えてください。 var bearerToken = "<your Bearer Token>"; var client = new TwitterClient("<your API Key>", "<your API Secret>", bearerToken); var stream = client.StreamsV2.CreateSampleStream(); 5. イベントハンドラーと処理内容の記述(コード) 以下は、ストリームからツイートを1件読み取る度に呼ばれる TweetReceived に処理内容を記述しています。args.Tweet.Lang を利用して、日本語のツイートだけを表示するようにします。 stream.TweetReceived += (sender, args) => { var lang = args.Tweet.Lang; if (lang.ToLower() == "ja") // Display only Japanese tweets { Console.WriteLine($"** Text : {args.Tweet.Text}"); } ++counter; if (counter >= maxCount) { stream.StopStream(); } }; 6. ストリーム読み取りの開始(コード) 以下は、ストリームの読み取り開始となります。ストリームは一度開始すると、レート制限などに掛からない限り終了しない為、手順 5 では StopStream() を使って、読み取り件数に応じてストリームを終了するようにしています。 await stream.StartAsync(); 7. コード全体 上記で主要なコードについて説明しましたが、以下はコード全体となります。 Program.cs using System; using System.Threading.Tasks; using Tweetinvi; namespace TwitterStreamApiConsole { class Program { private static readonly int maxCount = 100; private static int counter; static void Main(string[] args) { counter = 0; // Start Twitter Stream reading with Sampled Stream V2 API StartSampledStreamV2().Wait(); Console.ReadLine(); } /// <summary> /// Twitter Sampled Stream V2 API /// Nuget Package : Tweetinvi /// https://linvi.github.io/tweetinvi/dist/intro/basic-concepts.html#twitterclient /// </summary> private static async Task StartSampledStreamV2() { Console.WriteLine($"***** Stream started. {DateTime.UtcNow}"); // Application client & stream var bearerToken = "<your Bearer Token>"; var client = new TwitterClient("<your API Key>", "<your API Secret>", bearerToken); var stream = client.StreamsV2.CreateSampleStream(); // Read stream stream.TweetReceived += (sender, args) => { var lang = args.Tweet.Lang; if (lang.ToLower() == "ja") // Display only Japanese tweets { Console.WriteLine("----------------------------------------------------------------------"); Console.WriteLine($"** CreatedAt : {args.Tweet.CreatedAt}"); Console.WriteLine($"** Source : {args.Tweet.Source}"); Console.WriteLine($"** Text : {args.Tweet.Text}"); } ++counter; if (counter >= maxCount) { stream.StopStream(); } }; await stream.StartAsync(); Console.WriteLine(); Console.WriteLine($"***** Stream stopped. {DateTime.UtcNow} (counter : {counter})"); } } } 8. デバッグ実行 デバッグ実行して、以下のようにツイートがリアルタイムに表示されれば、成功です。お疲れ様でした。 次のステップへ Step2 では、キーワードでストリームをフィルタリング可能な Filtered Stream (V1.1) API のコードを作成します。 参照 C# 向け Twitter API SDK (TweetinviAPI) Nuget Package サイト TweetinviAPI - Sampled Stream API リファレンス クロス プラットフォーム .NET 概要 Twitter 開発者向けサイト Twitter API サイト Visual Studio 2019 Community サイト Visual Studio Code のサイト
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

SonarCloudで指摘されたIssueを解消してみる(対象はC#のコード)

はじめに Sonarcloudを利用してみて、導入したリポジトリにいろいろと改善点があるぞと教えてくれたので、 どんな問題が出ていて、どう対応するかまとめてみました。 SonarCloudのIssueについて SonarCloudを導入してスキャンをかけた結果のうち、Issueタブに指摘が表示されます。 SonarcloudのIssue対応の設定は以下のように行う。 指摘された直後の場合はOpendと表示されている箇所をクリックすると画像のような選択が行える。 カーソルを合わせた際のツールチップを翻訳すると下記のような設定項目となっている。 Confirm この問題は検討されており、最終的には何か対処しなければなりません。 Resolve as fixed この問題はコード上で修正されており、次の解析で解決されるのを待っています。 実際には修正されていなかった場合は再オープンされます。 Resolve as false positive この問題は、解析エンジンの限界によるものなので、無視しても構いません。その労力はカウントされません。   Resolve as wont't fix この問題は、そのルールがこの文脈では無関係であるため、無視することができます。その努力はカウントされません。 では指摘が実際にどの選択肢に当てはまるのかは1つ1つ確認していかなければなりません。 とは言っても難しいことではなく、指摘の末尾にあるWhy is this an issue?をクリックすれば、 詳細な指摘の説明が出てくるため、その説明と実コードを見比べれば何をどうすべきかわかります。 たとえば以下は、「このコメントアウトされたコードを削除します。」と指摘されており Why is this an issue?より詳細を読むとなぜ問題となっているのか教えてくれます。 「プログラマーは、プログラムが肥大化し、可読性が低下するため、コードをコメントアウトしてはいけません。 使われていないコードは削除すべきであり、必要であればソースコントロールの履歴から検索することができます。」 この内容を踏まえて、実際に修正すべきかどうか判断すれば良いだけです。 例:SonarCloudで指摘された内容 導入したリポジトリはLoC1000ちょっとの大きなものではないですが、結構指摘されていたので参考としてどんな問題が検出されるのか紹介します。 Update this implementation of 'ISerializable' to conform to the recommended serialization pattern. ExceptionクラスがISerializableを継承しており、system.serializable属性をつけた方がいいと指摘されている。 dotnetのExceptionクラスを継承したクラスでもSystem.Serializable属性がついていないものもあり、実際にその属性が不要であることが多いため今回の検出箇所では対応不要と判断した。 ⇨Resolve as wont't fixを設定 All 'Read' method overloads should be adjacent. オーバーロードが隣接していません。 ファイルパス引数のReadメソッドとStream引数のReadメソッドが離れており指摘されてる。 後々追加したオーバーロードのメソッドが隣接していなかったため修正した。 ⇨修正した後にResolve as fixedを設定 Remove this commented out code. 不要なコメントは削除すべき。 一部の実装箇所を現在は作りこんでおらず、そこをコメントアウトしており指摘されてる。 ⇨現在は作りこんでいないためConfirmとして指摘を残すようにしている。 Change the visibility of this constructor to 'protected'. アクセス修飾子をpublicからprotectedにできると指摘されてる。 ⇨その通りであったため修正した後にResolve as fixedを設定 Refactor 'Issues' into a method, properties should not copy collections. とあるクラスのIssuesというプロパティのゲッタでToListして値を返しているが、ToListは単純なフィールドアクセスより遅くなるためユーザがパフォーマンスの低さに驚かないようにリファクタリングすべきと指摘されている。 ⇨ToListとせずにそのままリストを返しても問題なかったため、修正した後にResolve as fixedを設定 'System.Exception' should not be thrown by user code. 'System.Exception'は、ユーザーコードがスローしてはならないと指摘されている。 標準のExceptionをユーザがスローするとユーザーコードから意図して例外をスローしているのか、意図せず例外が起きているのかがわからなくなってしまうため修正が必要。 ⇨ユーザ定義の例外クラスを利用するように修正した後にResolve as fixedを設定 最後に 見落としがちなところを自動で検出してくれるのでありがたいですね。 他にもあれば備忘録もかねて追記していきます。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Rigidbodyに関する問題の備忘録

環境 Unity 2020.3.2f1 問題の件 RigidbodyにFreeze Positionを設定したGameObjectに別のGameObjectをぶつけると、その方向に反発しない? 参考画像 下のCubeのFreeze Positionをy=0にしている するとCubeの反発もy軸方向が消えている 対策 どちらかにRigidbodyを設定しない、反発してほしい方向にFreeze Positionを設定しないなどが考えられる。 終わりに 誰か解決方法を知っている方はコメントお願いします
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

WPF-Viewを明示的に更新する

WPFで重たい処理をスレッド化せずに、処理の途中でViewを更新しようとしても変わらない。 本来はビジネスロジックはスレッドで動かして、UIスレッドを邪魔しない機構を検討すべきではあるが、下記参考ページの方法が手っ取り早かったので採用。 https://www.ipentec.com/document/csharp-wpf-implement-application-doevents 下記、プログレスバーの更新を例に解決方法を説明。 実行結果 XAML プログレスバーと、重たい処理を発火するボタンを配置。 プログレスバーのレンジは0~100で、進捗値はViewModelの"prog.Value"にバインド。 <Window x:Class="prism_test.Views.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:prism="http://prismlibrary.com/" prism:ViewModelLocator.AutoWireViewModel="True" Title="{Binding Title}" Height="170" Width="525"> <Grid> <StackPanel> <ProgressBar x:Name="progressBar" Maximum="100" Minimum="0" Value="{Binding prog.Value}" Height="50" Margin="10,10,10,10" /> <Button Content="Button" HorizontalAlignment="Left" Height="40" Margin="10,10,10,10" Width="100" Command="{Binding DoHeavyProc}"/> </StackPanel> </Grid> </Window> コードビハインド 肝になるのが"UpdataView"メソッド。これをViewModelからデリゲートを介して呼び出す。 using System.Windows; using System.Windows.Threading; namespace prism_test.Views { /// <summary> /// Interaction logic for MainWindow.xaml /// </summary> public partial class MainWindow : Window { /// <summary> /// ViewModelへの参照 /// </summary> ViewModels.MainWindowViewModel vm; /// <summary> /// コンストラクタ /// </summary> public MainWindow() { InitializeComponent(); // ViewModelへの参照記憶 vm = (ViewModels.MainWindowViewModel)DataContext; // View側処理をViewModelに設定 vm.UpdateView = UpdataView; } /// <summary> /// Viewの更新 /// </summary> /// <param name="prog">進捗</param> public void UpdataView() { DispatcherFrame frame = new DispatcherFrame(); var callback = new DispatcherOperationCallback(obj => { ((DispatcherFrame)obj).Continue = false; return null; }); Dispatcher.CurrentDispatcher.BeginInvoke(DispatcherPriority.Background, callback, frame); Dispatcher.PushFrame(frame); } } } ViewModel ボタン押下時に進捗値を20ms毎にカウントアップしています。 using Prism.Mvvm; using Reactive.Bindings; namespace prism_test.ViewModels { public class MainWindowViewModel : BindableBase { private string _title = "Prism Application"; public string Title { get { return _title; } set { SetProperty(ref _title, value); } } /// <summary> /// 進捗 /// </summary> public ReactiveProperty<int> prog { get; set; } = new ReactiveProperty<int>(); /// <summary> /// コマンド /// </summary> public ReactiveCommand DoHeavyProc { get; } /// <summary> /// View処理へのデリゲート /// </summary> public System.Action UpdateView; /// <summary> /// コンストラクタ /// </summary> public MainWindowViewModel() { prog.Value = 0; this.DoHeavyProc = new ReactiveCommand().WithSubscribe(this.buttonpush); } /// <summary> /// ボタン押下 /// </summary> public void buttonpush() { prog.Value = 0; for(var i = 0; i < 100; i++) { prog.Value++; // Viewを更新 UpdateView(); System.Threading.Thread.Sleep(20); } } } }
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Unity・C#】軽量なObjectPoolを作りたい

やりたいこと ObjectPoolingは大量のオブジェクトを扱う際、メモリ割り当てをしないように一度生成したオブジェクトを常に使い回すための仕組みです。 しかしObjectPoolは結構再開発されまくってて、目的・用途が様々になってきています。 なので、ここで一旦要件をまとめておきたいと思います。 ・プールされるオブジェクトはGameObjectのComponentである。 ・GameObjectには必ず同じComponentがアタッチされている。 ・プールされるオブジェクトは個体識別しない。 ・プールされるオブジェクトに生成時、破棄時のコールバックが欲しい ・プールはシングルトン ・例外なくちゃんとエラーを吐いてくれる コードはGithubにあげています。 実装 オブジェクトプール プールするための配列はQueue<T>を使用しました。 CreatePool(int)で許容量を設定しつつプールを生成します。 ここで許容量を設定するのは、許容量を超えた際にメモリの再割り当てが発生するのを防ぐためです。 また、普通のQueueとは少し違いプールの許容量を超えたら、再割り当てするかしないかをisFixedで設定することができます。(普通は自動で再割り当てされてしまう) public void CreatePool(int capacity, bool isFixed) { if (m_isActive) { Debug.LogWarning("Poolは既に生成されています!"); return; } if (prefab == null) { Debug.LogError("Prefabがセットされていません!"); return; } m_capacity = capacity; Pool = new Queue<IPoolable<T2>>(capacity); for (int i = 0; i < capacity; i++) { T2 obj = Instantiate(prefab, transform).GetComponent<T2>(); obj.gameObject.SetActive(false); Pool.Enqueue(obj); } m_isActive = true; m_isFixed = isFixed; } Release()でプールからオブジェクトを取り出すことができます。 基本的に余計なことはしないスタンスなので、コールバックを呼び出しオブジェクトを取り出すだけの実装になっています。 ここで例外をキャッチしているのは、Pool.Dequeue()でプールが空の状態の時に例外を発生させるからです。ドキュメントによると、TryDequeue(out T)が使えるらしいのですが、恐らくバージョン的に存在しなかったため例外処理をしています。 public T2 Release() { if (!PoolIsAvailable()) return null; try { IPoolable<T2> obj = Pool.Dequeue(); T2 entity = obj.Entity; entity.gameObject.SetActive(true); obj.OnReleased(); return entity; } catch { Debug.LogError("Release可能なPoolEntityが存在しません!"); return null; } } Catch(T)でコールバックを呼び出し、プールにオブジェクトを返却します。 IsFixedを指定した場合、CanDequeue()で弾かれて追加されるのを防ぎます。 public void Catch(IPoolable<T2> obj) { if (!PoolIsAvailable()) return; if (!CanDequeue()) return; obj.OnCatched(); obj.Entity.gameObject.SetActive(false); Pool.Enqueue(obj); } プールされるオブジェクト プールされるComponentにはIPoolable<T>の実装を強制します。 IPoolable<T>の実装はこのようになっています。 public interface IPoolable<T> where T : MonoBehaviour { T Entity { get; } void OnReleased(); //生成時(取得) void OnCatched(); //破棄時(返却) } EntityはIPoolableを実装するクラスのインスタンスを返させます。 OnReleased(), OnCatched()はそれぞれ生成時、破棄時に呼び出されます。 (キャッチアンドリリースってわかりやすい) 使い方 ObjectPool<継承するクラス, プールするクラス>を継承して、利用することができます。 CreatePool(capacity, isFixed)でプールを作成し、DestroyPool()でプールを丸ごと破棄できます。 public class HogePool : ObjectPool<HogePool, Hoge> { private void Start() { CreatePool(100, true); } } 取得と返却は先ほど言った通り、Release(), Catch(T)でできます。 HogePool.Instance.Release(); HogePool.Instance.Catch(this); //返却するクラスのインタンスを渡す サンプルもリポジトリ内にあるので、活用してみてください。 まとめ ・そんなに多機能でもなく、Queue<T>を使ってるのでパフォーマンス的には問題ないはず、、? ・返却はCatch(IPoolable<T>)でinterfaceを渡している。暗黙的なキャストが発生しているので、ここがネック。 ・Queue<T>を自作クラスでもっと軽量にすれば軽くできるかなと思った。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Prismでメッセージボックスの自作。TemplateSelectorで表示ボタンも変更できます

はじめに MessageBoxをMVVMパターンで自作するが面倒で、これまでMessageBoxをそのまま使ってきたのですが、新しくアプリを作るにあたって、改めてメッセージボックスを自作したので記事にしました。 昔自作しようとしたときに、途中でよくわからなくなってしまった経験があったため、この記事では初めて作る方向けに丁寧な説明を心がけてみようと思います。 このメッセージボックスを作ることで、インターフェースの使い方や、PrismのApp.xamlの使用方法やDIについて、データテンプレートなど勉強になることがたくさんあります。 作ったことがない方は一度やってみることをお勧めします。 開発前提はC#、Prism、WPF、.Net Coreです。 実装計画 表示について PrismのIDialogServiceでViewを表示(ShowDialog)させる 表示されたViewのボタンを[Okのみ]・[Yes/No]・[OK/Cancel]に切り替えられるようにする コーディング内容 呼び出し用のインターフェースを作成する インターフェースの実装クラスでパラメータを受け取り、ShowDialogする 表示するViewデザイン内でTemplateSelectorを使用してボタンを切り替えられるようにする 実装手順 実際は自分のやりやすい手順でいいと思いますが、せっかく記事にするので、初めての人が迷わないように、なるべくエラーが出ない順番で作成します。 そのため、少し行ったり来たりしているところがありますのでご容赦ください。 プロジェクト・フォルダ準備 Prism Blank App(.NET Core)テンプレートから作成したMessageBoxSampleプロジェクト フォルダは初期値のままです Prism Module(.NET Core)テンプレートから作成したModule.MessageBoxプロジェクト Modelsフォルダ作成 Serviceフォルダ作成 TemplateSelectorsフォルダ作成 Views,ViewModelsの初期時に入っているViewAは削除しました プロジェクト依存関係 MessageBoxSampleプロジェクトの依存関係を右クリックして「プロジェクト参照の追加」を選び、Module.MessageBoxプロジェクトを選択します App.xamlにモジュールを登録 Module.MessageBoxプロジェクトを使用できるようにするために、モジュールをApp.xamlのConfigureModuleCatalogで登録します protected override void ConfigureModuleCatalog(IModuleCatalog moduleCatalog) { moduleCatalog.AddModule<MessageBoxModule>(); } Module.MessageBoxプロジェクト Modelsフォルダにenumクラス作成 メッセージに表示させるボタンの種類を指定するためのenumクラスを以下のように作成します namespace Module.MessageBox.Models { public enum MessageDialogType { OkOnly, YesNo, OkCancel } } View,ViewModel作成 Prism UserControlとしてMessageDialog.xamlを作成します。 ViewModelにIDialogAware継承 ViewをPrismのダイアログとして利用するためにはIDialogAwareを継承させる必要があります こんな感じになると思います。 namespace Module.MessageBox.ViewModels { public class MessageDialogViewModel : BindableBase, IDialogAware { public string Title => throw new NotImplementedException(); public event Action<IDialogResult> RequestClose; public MessageDialogViewModel() { } public bool CanCloseDialog() { throw new NotImplementedException(); } public void OnDialogClosed() { throw new NotImplementedException(); } public void OnDialogOpened(IDialogParameters parameters) { throw new NotImplementedException(); } } } ViewをPrismのダイアログとして登録します。 具体的にはMessageBoxModuleのRegisterTypesでRegisterDialogとして登録します namespace Module.MessageBox { public class MessageBoxModule : IModule { public void OnInitialized(IContainerProvider containerProvider) { } public void RegisterTypes(IContainerRegistry containerRegistry) { containerRegistry.RegisterDialog<MessageDialog>(); } } } ViewModel内に先ほど作成したenum値のプロパティを書きます private MessageDialogType _messageDialogValue; public MessageDialogType MessageDialogValue { get => _messageDialogValue; set => SetProperty(ref _messageDialogValue, value); } TemplateSelector作成 TemplateSelectorsフォルダにMessageDialogTemplateSelectorクラスを作成し、ViewModelのプロパティ値(MessageDialogValue)に対応するデータテンプレートを返すようにコーディングします namespace Module.MessageBox.TemplateSelectors { class MessageDialogTemplateSelector : DataTemplateSelector { public DataTemplate OkOnlyTemplate { get; set; } public DataTemplate OkCancelTemplate { get; set; } public DataTemplate YesNoTemplate { get; set; } public override DataTemplate SelectTemplate(object item, DependencyObject container) { MessageDialogType messageDialogType = ((MessageDialogViewModel)item).MessageDialogValue; switch (messageDialogType) { case MessageDialogType.OkOnly: { return OkOnlyTemplate; } case MessageDialogType.OkCancel: { return OkCancelTemplate; } case MessageDialogType.YesNo: { return YesNoTemplate; } } return base.SelectTemplate(item, container); } } } Viewデザイン MessageDialog.xamlのデザイン画面を開きます。 いっぺんにコードを乗せるとかなりの量になって混乱するかもしれないので、丁寧に少しずつ説明しながら書いていきます 慣れている人は当たり前になっている部分も多いのですっ飛ばしてください 参照 まずは< UserControl...... >となっているところに以下のように参照や値を書きます MinWidthとMinHeightは適当です。実際に使用していくと、MaxWidthとかも設定したほうがよかったりいろいろですが、ここでは省いています xmlns:templateSelector="clr-namespace:Module.MessageBox.TemplateSelectors" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d" MinWidth="250" MinHeight="100" WindowStyle 次にダイアログが表示されたときに、最大化とかできないようにしたり、タスクバーに表示されないようにするために以下のコードを書きます。ここでTitleプロパティを記述しておくことで、ViewModelのTitleを表示させることができ、メッセージのタイトルとして利用できます <prism:Dialog.WindowStyle> <Style TargetType="Window"> <Setter Property="ResizeMode" Value="NoResize" /> <Setter Property="ShowInTaskbar" Value="False" /> <Setter Property="SizeToContent" Value="WidthAndHeight"/> <Setter Property="Title" Value="{Binding Title}"/> </Style> </prism:Dialog.WindowStyle> UserControl.Resourcesにデータテンプレートを作成する データテンプレートとしてOkOnlyTemplate、YesNoTemplate、OkCancelTemplateを作成します。 このデータテンプレートはボタンの表示部分に関するものとなります。 データテンプレートにx:Key=""でキーをセットしていて、これとMessageDialogTemplateSelector クラスのプロパティとが対応することになり、templateSelector:MessageDialogTemplateSelector.... で表示を切り替えられるようにしています <UserControl.Resources> <DataTemplate x:Key="OkOnlyTemplate"> <Button Content="OK" HorizontalAlignment="Center" Width="100" Command="{Binding OkCommand}" IsDefault="True"/> </DataTemplate> <DataTemplate x:Key="YesNoTemplate"> <StackPanel Orientation="Horizontal" HorizontalAlignment="Center"> <Button Content="はい" Margin="0,0,10,0" Width="100" Command="{Binding YesCommand}"/> <Button Content="いいえ" Margin="10,0,0,0" Width="100" Command="{Binding NoCommand}" IsDefault="True"/> </StackPanel> </DataTemplate> <DataTemplate x:Key="OkCancelTemplate"> <StackPanel Orientation="Horizontal" HorizontalAlignment="Center"> <Button Content="OK" Margin="0,0,10,0" Width="100" Command="{Binding OkCommand}"/> <Button Content="キャンセル" Margin="10,0,0,0" Width="100" Command="{Binding CancelCommand}" IsDefault="True"/> </StackPanel> </DataTemplate> <templateSelector:MessageDialogTemplateSelector x:Key="MessageDialogTemplateSelector" OkOnlyTemplate="{StaticResource OkOnlyTemplate}" YesNoTemplate="{StaticResource YesNoTemplate}" OkCancelTemplate="{StaticResource OkCancelTemplate}"/> </UserControl.Resources> 実際の表示部分(Grid) さきほど説明した通り、データテンプレートはボタンの表示切替のためのものです。 最後はメッセージ表示部分も含めたデザインを作りますが、ここでポイントとなるのがボタン表示部分です。 ContentControlを使用しているのですが、d:ContentTemplateというものがあります。これを使用することで、任意のデータテンプレートを使用しながらデザインすることができます。 <Grid> <Grid.RowDefinitions> <RowDefinition/> <RowDefinition Height="25"/> </Grid.RowDefinitions> <TextBlock Grid.Row="0" Text="{Binding Message}" TextWrapping="Wrap"/> <ContentControl Grid.Row="1" Content="{Binding}" VerticalAlignment="Center" d:ContentTemplate="{StaticResource YesNoTemplate}" ContentTemplateSelector="{StaticResource MessageDialogTemplateSelector}"/> </Grid> ViewModelのコーディング ViewModelのコーディングを行おうと思いますが、その前に先に、メッセージなどを受け取るためのパラメータ用クラスを作成しておきます パラメータ用クラス作成 ModelフォルダにMessageContentクラスを作成し、以下のコードを書きます namespace Module.MessageBox.Models { public class MessageContent { public string Title { get; set; } public string Message { get; set; } public MessageDialogType MessageDialogValue { get; set; } } } ViewModlのプロパティ・フィールド部分 コマンドやプロパティ値、フィールドのコード宣言をします。さきほどのMessageDialogTypeも再度記載しています MessageContentを受け取るフィールド タイトル、メッセージ、ボタン用のプロパティ ViewのCommandでBinding設定したICommandの宣言 IDialogAwareで実装されたDialogResult, Actionのコード となります private MessageContent messageContent; private string _title; public string Title { get => _title; set => SetProperty(ref _title, value); } private string _message; public string Message { get => _message; set => SetProperty(ref _message, value); } private MessageDialogType _messageDialogValue; public MessageDialogType MessageDialogValue { get => _messageDialogValue; set => SetProperty(ref _messageDialogValue, value); } public ICommand OkCommand { get; } public ICommand CancelCommand { get; } public ICommand YesCommand { get; } public ICommand NoCommand { get; } private DialogResult result = new DialogResult(ButtonResult.None); public event Action<IDialogResult> RequestClose; コンストラクタ ViewModelのコンストラクタでICommandの初期化、メソッド呼び出しを作成します。メソッド名は説明しやすいように書いているので別の名称にしてもらっても構いません public MessageDialogViewModel() { OkCommand = new DelegateCommand(SelectedOk); CancelCommand = new DelegateCommand(SelectedCancel); YesCommand = new DelegateCommand(SelectedYes); NoCommand = new DelegateCommand(SelectedNo); } コマンドから呼び出されるメソッドを作成します SelectedOk, SelectedCancel, SelectedYes, SelectedNoメソッドを作成します。 ButtonResult値を引数としてAction RequestCloseを呼び出しています。 private void SelectedOk() { result = new DialogResult(ButtonResult.OK); RequestClose(result); } private void SelectedCancel() { result = new DialogResult(ButtonResult.Cancel); RequestClose(result); } private void SelectedYes() { result = new DialogResult(ButtonResult.Yes); RequestClose(result); } private void SelectedNo() { result = new DialogResult(ButtonResult.No); } ダイアログが呼び出された時のパラメータ取得 OnDialogOpenedがViewが表示されるときに使用されます。この引数のIDialogParameters parametersを利用して、タイトル、メッセージ、ボタンの種類を取得します ついでに、ビープ音も鳴らせるようにしておきました。 public bool CanCloseDialog() => true; public void OnDialogClosed() { } public void OnDialogOpened(IDialogParameters parameters) { messageContent = new MessageContent(); messageContent = parameters.GetValue<MessageContent>("MessageContent"); Title = messageContent.Title; Message = messageContent.Message; MessageDialogValue = messageContent.MessageDialogValue; // ビープ音を鳴らします SystemSounds.Asterisk.Play(); } 以上でView, ViewModelまでは完成です 次にこのメッセージボックスを呼び出し・表示させるためのコードを作っていきます 呼び出し用のサービス作成 インターフェース作成 Module.MessageBoxプロジェクトのServiceフォルダ内に次のようにインターフェースを作成します public interface IMessageDialogService { ButtonResult ShowMessage(string message, string title, MessageDialogType messageDialogType); } インターフェースの実装 次に、作成したインターフェースの実装クラス(MessageDialogService)を作成します。 同じようにServiceフォルダ内の作成します。 ちなみに、注意ですが、ここでは記事のためにフォルダを減らしてシンプルにしています。インターフェースと実装クラスを別にしたほうがいい場合はそうしてください。 インターフェースを継承すると初期状態はこんな感じになると思います。 public class MessageDialogService : IMessageDialogService { public ButtonResult ShowMessage(string message, string title, MessageDialogType messageDialogType) { throw new NotImplementedException(); } } コンストラクタ このなかでPrismのIDialogServiceを使用できるようにしたいので、コンストラクタを宣言します。依存オブジェクトの注入ですがここでは説明は割愛します。とりあえずこうすれば使えるよというぐらいでとらえておきましょう。 private readonly IDialogService _dialogService; public MessageDialogService(IDialogService dialogService) { _dialogService = dialogService; } ShowMessageのコーディング メッセージボックスの呼び出し時に実際に処理が行われる部分で、メッセージを表示後、閉じたときのボタンが戻る処理となります public ButtonResult ShowMessage(string message, string title, MessageDialogType messageDialogType) { MessageContent messageContent = new MessageContent() { Message = message, Title = title, MessageDialogValue = messageDialogType }; IDialogResult dialogResult = null; _dialogService.ShowDialog(nameof(MessageDialog), new DialogParameters { { "MessageContent", messageContent } }, result => dialogResult = result); return dialogResult.Result; } サービスをApp.xamlに登録する 最後に、作成したサービスが使用できるように、App.xamlのRegisterTypesで以下のように登録します protected override void RegisterTypes(IContainerRegistry containerRegistry) { // Message Dialog Service containerRegistry.Register<IMessageDialogService, MessageDialogService>(); } ソリューション・プロジェクト構造の確認 ここまでで以下のようなソリューションとなっていれば大丈夫です。 もし動かないときは、どこかが違っているかもしれないので確認してください。 動かしてみた 以上でメッセージボックスは完成したので、実際に動くか確認してみたいと思います。 MainWindowのデザインを以下のようにして <Grid> <StackPanel> <Button Content="OK" Command="{Binding ShowOKMessageBox}" Margin="10"/> <Button Content="はい・いいえ" Command="{Binding ShowYesNoMessageBox}" Margin="10"/> <Button Content="OK・キャンセル" Command="{Binding ShowOKCancelMessageBox}" Margin="10"/> </StackPanel> </Grid> MainWindowViewModelをこんな感じでコーディング public class MainWindowViewModel : BindableBase { private string _title = "Prism Application"; public string Title { get { return _title; } set { SetProperty(ref _title, value); } } private readonly IMessageDialogService _messageDialogService; public ICommand ShowOKMessageBox { get; } public ICommand ShowYesNoMessageBox { get; } public ICommand ShowOKCancelMessageBox { get; } public MainWindowViewModel(IMessageDialogService messageDialogService) { _messageDialogService = messageDialogService; ShowOKMessageBox = new DelegateCommand(ShowOkMessage); ShowYesNoMessageBox = new DelegateCommand(ShowYesNoMessage); ShowOKCancelMessageBox = new DelegateCommand(ShowOkCancalMesssage); } private void ShowOkCancalMesssage() { _messageDialogService.ShowMessage("OK・キャンセルになっていますよ", "OK・キャンセルタイトル", MessageDialogType.OkCancel); } private void ShowYesNoMessage() { _messageDialogService.ShowMessage("はい・いいえになっていますよ", "はい・いいえタイトル", MessageDialogType.YesNo); } private void ShowOkMessage() { _messageDialogService.ShowMessage("OKになっていますよ", "OKタイトル", MessageDialogType.OkOnly); } } これで実行するとちゃんとメッセージが表示されました さいごに メッセージの表示位置やボタンの表示位置などデザイン部分は、データテンプレートをいろいろといじってみて、好みのものに調整していただければと思います。 僕は、MaterialDesignとか使って、スタイル設定して最終的にこんな感じで表示されるようにしてアプリを作成しています
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

フィールドとかプロパティとかメソッドとか、そういうのをはっきりさせたいだけ (3/?)

smileBASICから始まり pythonを学校で習い luaで遊んで C#を知る 私は今、人生の岐路にいる if ~ elseif ~ else ~の処理直前と言ったところか とにかく、プログラミングを本気でやるのなら... このくだりもうやったな 前回のまとめ + コメント 備忘録ならもう最初に前回のまとめした方が見返しやすいのではと思ったので、 これから最初は前回のまとめをする。 前回まとめ書いたか書いてないかに関わらず。 ところでタイトルを変えた。 なぜなら動的という単語のプログラミング的な意味の認知が、 そもそも間違っていたからである。 あと、この記事では、感覚で覚えたあやふやなものを、最低限、言葉にできるくらいはっきりさせるというのが目的だ。 完全で完璧な理解までとはいかなくてもよい。 そう、はっきりさせたいのだ。 (結構「理解した」という単語を使ったが、それは正確ではない、他にいい表現が引き出せなかったのだ。) 最初の(あやふやな状態の)認識 動的 -> インスタンスを作成しないとアクセスできないこと 静的 -> インスタンスを作成しなくてもアクセスできること プロパティ -> public <型> <名前> { get; set; }のやつ。この単語自体はVisual Studioで知った インスタンス -> クラス内の動的変数のこと メソッド -> 関数。 この単語もVisual Studioで知った public -> どのファイルからでもアクセスできる private -> アクセスできない 多分継承すればアクセスできるはず internal -> publicとは違うんだろうけど詳しくは分からない。アクセスできるっぽかったから protected -> これを使ったことはないし、知らないが、見たことはあるので挙げてみた abstract -> 継承元のクラスにつけるやつ。確か抽象クラスって名前だった virtual -> overrideしたいメソッドにつけるやつ static -> 静的にしたいときにつけるやつ sealed -> 継承禁止 override -> override 継承クラスにおけるメソッド、プロパティの書き換え =>演算子 -> プロパティにおける=の役割 (readonly -> 読み取り専用) (const -> 定数) 結構やばかった 第二回C#備忘録を投稿して少し経った後にコメントがきた。 ありがとうございます...。 勉強になるのはもちろんだが、自分のはっきりさせたことが本当に正しかったのか、 確かめる手段でもあるからだ。 そして、案の定、やばい認識をしていた。 そう、動的と静的、そして非静的の意味だ。 前回 動的 = 非静的 という認識をしたが違った。 私は初めて、動的と静的という単語を知った時、これはtrueかfalseのように、 完全な対義語だと思っていた。 (今までは classType = Dynamic か classType = Staticみたいだと思っていたのだが、) (本来は classType.Dynamic = false と classType.Static = false のような感じであった。) とりあえずその部分を、自分の言葉で書き直してみる。 動的と静的と非静的 一般に、「動的か静的」のどちらか一方に定まるのは、言語の種類である。 PythonやLuaなど、場合によって変数の型が変化する言語を動的な言語と言う。 hoge = "hoge hoge" #string型 hoge = list(hoge) #list型に変更される print(hoge) # >>> ["h", "o", "g", "e", " ", "h", "o", "g", "e"] 動的な言語に対し、 C#のような変数の宣言時に、変数の型を宣言し、その変数の型を変えることができない言語を静的な言語と言う。 string hoge = "hoge hoge"; hoge = hoge.Split(" ".ToCharArray()); //型 'string[]' を 'string' に暗黙的に変換できません そういえばそうだ。Pythonの本に「暗黙的な型変換」という単語があったな。 これらの静的に対し、動的と静的の一方に必ずしも定まらないのが、 クラスとメンバだ。(そういえばなんでメンバーじゃなくてメンバなんだろ) 上記のような、言語的な静的 = static ではない。 どうやら公式リファレンスの クラスのインスタンスの作成数に関係なく、静的メンバーのコピーは 1 つしか存在しません。 これ。 これが全てを物語っているらしい。 のだが、私はこのメモリがよくわかっていない。だからこの表現をなるべく避けていたのだが... おそらくこれは、コンパイルの仕組みまで話を展開していかないといけないであろう。 なんとなくは分かるのだ。あやふや。 感覚的には言いたいことがわかるのだが... ここら辺を言葉として出力するには、まだ私の知識が足りないのであろう。 例えば定数はコンパイル時に固定される...みたいな...? 定数とコンパイルの話をどこかで見かけた。 残りの1つは()を見逃すという、プログラミングしてる人としてそれちょっとどうなの、みたいな感じのことをしてしまった。 getとsetは勝手に消えません!! dynamic修飾子 実はどこかでdynamicを見かけたことがある。 当時は 静的と動的が 相反する概念だと思っていたので、なぜdynamicがあるのか分からなかった。 だからこんな感じで書いてエラーでて???ってなっていた。 dynamic int testValue = 1; 我ながらあっぱれだ。 非静的というのは静的でも動的でもないことである。 貼ってあったリンクをみていたらこんなのも見つけた。 static dynamic GetX(dynamic obj) { return obj.X; } これが(クラス、変数的な)動的と静的が必ずしも一方に決まるわけではないという証明になるだろう。 余談だがvarとは全然違う。そこは「理解している」。 本題 さて、静的を理解するためにコンパイルとかもっと奥深い場所に乗り込もうとしたのだが、 どうしたものか。 プロパティという小石に躓き、転んでけがしてしまったのである。 前回で 役割は分かったのだが、 記事を書いた後にプロパティについて調べまくったのだ。 主に使い方を。 そうして出てきた記事は 「プロパティって素晴らしい!」「プロパティを使いましょう!」 とプロパティの長所を説明するばかりで、 参った。 あれ?これじゃあフィールドいらなくね? フィールドいらなくね? まずプロパティの基本形はこうだ。 private int _itemType; public int itemType { get { return _itemType; } set { _itemType = value } } // 私にとって1番しっくりきた書き方が見つかった! これだけはフィールドいらなくね?とはならないだろう。 問題は次の省略形だ。 public int itemType { get; set; } 省略形はデータを保持するためのフィールドを必要としない。 そして、「みんな!プロパティすげぇよ!便利だよ!使えよ!」 ここまで来たらもう、フィールドいらないじゃん。 フィールドいらなくね...? 参った。 なんとなくModのプロジェクトを開いた。 目についたことがある。+=という複合演算子(だっけか?)だ。 なんか...フィールドじゃなきゃいけない物を見出せそうだったのだが、 それは本当に、感覚...いや、勘レベルのものだった。 うーむ... つまり、私はプロパティでは向いていない場合を知りたいのである。 このままだと、私はフィールドを使わなくなってしまう!! プロパティプロパティプロパティプロパティプロパティプロパティプロパティプロパティプロパティプロパティプロパティ どう?ゲシュタルト崩壊しそうじゃない? 正直 今の私 そんなかんじ~ 公式リファレンスにチュートリアルがあった。 そこにはpublicのプロパティは存在しなかった。 publicってたのはプロパティであった。 他の記事を見た。 プロパティの素晴らしさが書かれていた。 さらに他の記事を見た。 プロパティの素晴らしさが書かれていた。 さらにさらに他の記事を見た。 プロパティの素晴らしさが書かれていた。 さらにさらにさ(ry プロパティの短所... ファーーーーーーーーー! あったのだ。プロパティの短所が書かれている記事が。 そこには「低速」の文字が。 つまりUpdate()のような、毎フレーム呼び出すようなものには 向いていないのだろうか...? わからない。たすけて。 以上 第三回C#たすけて備忘録
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む