apeescape2.com
  • メイン
  • 製品ライフサイクル
  • エンジニアリング管理
  • 革新
  • 仕事の未来
バックエンド

WPFアプリケーションのReactiveUIとMVVMパターン

リアクティブプログラミングは非同期です プログラミングパラダイム に関係する データストリーム そして変化の伝播。 –ウィキペディア

その文を読んだ後でも、私が最初に読んだときと同じようになってしまう可能性があります。その関連性を理解することにはほど遠いのです。基本的な概念にもう少し専念すれば、その重要性をすぐに理解できます。基本的に、リアクティブプログラミングは、最初は「ステロイドのイベント駆動型プログラミング」と考えることができます。イベントハンドラーをストリームとして描き、ハンドラーの各起動をストリーム内の新しいデータと考えます。一言で言えば、最終的にはリアクティブプログラミングです。

リアクティブプログラミングについてもう少し詳しく説明する前に、理解しておく必要のある概念がいくつかあります。 オブザーバブル これまで話してきたストリームへのアクセスを提供するオブジェクトです。それらの目的は、ストリーム内のデータへのウィンドウを提供することです。そのウィンドウが開かれると、次のことができます 見て を使用して選択した方法でデータに 演算子 その上で、アプリケーションをいつどのように決定するか 反応する ストリームに。最後に、 オブザーバー 結果のストリームで、ストリームによって新しいデータムが発行されるたびに発生するアクションを定義します。



つまり、ユーザーがボタンをクリックしたり、アプリがHTTP応答を受信したり、例外から回復したりするなど、アプリケーションが何が起こっているかに反応する方法をより細かく制御できるということです。リアクティブプログラミング(多くあります)を使用する利点を理解し始めると、元に戻すことはほとんどできなくなります。これは、アプリが行うことのほとんどが、特定の不測の事態に特定の方法で反応するためです。

さて、これはこの新しいアプローチにマイナス面がないという意味ではありません。まず第一に、その学習曲線はかなり急になる可能性があります。開発者(ジュニア、シニア、アーキテクトなど)が、最初に何を書くべきか、コードが実行される順序、またはエラーをデバッグする方法を理解するのに苦労している様子を直接見てきました。これらの概念を最初に紹介するときの私の推奨事項は、多くの例を示すことです。開発者は、物事がどのように機能し、使用されるのかを理解し始めると、そのコツをつかむでしょう。

2010年に初めてWindowsPresentation Foundation(WPF)を使用する前は、デスクトップアプリ(主にVisual Basic 6、Java Swing、およびWindowsフォーム)を使用して10年以上働いていました。基本的に、WPFは次の目的で作成されました。 .NETの最初のデスクトップ開発フレームワークであるWindowsフォームに取って代わります。

WPFフォームとWindowsフォームの主な違いはかなりありますが、最も重要な違いは次のとおりです。

  • WPFは、より堅牢で徹底的にテストされた新しい開発パラダイムを使用します。
  • WPFを使用すると、UIの設計とコーディングを強力に分離できます。
  • WPFを使用すると、UIをさまざまにカスタマイズおよび制御できます。

WPFとその機能を学び始めたら、絶対に気に入りました。 MVVMパターンの実装がどれほど簡単で、プロパティバインディングがどれほどうまく機能するか信じられませんでした。リアクティブプログラミングとそのWPFでの使用法に出くわすまで、その作業方法を改善するものは何も見つからないと思いました。

この投稿では、MVVMパターンでリアクティブプログラミングを使用したWPFアプリの非常に単純な実装を示し、RESTAPIにアクセスできることを望んでいます。

アプリケーションは次のことができるようになります。

  • 車とその場所を追跡する
  • シミュレートされたソースから取得した情報を取得します
  • この情報をBingMapsWPFコントロールでユーザーに表示します

建築学、建築物、建築様式

RESTful Web API Core2サービスを使用するWPFクライアントを構築します。

クライアント側:

  • WPF
  • ReactiveUI
  • 依存性注入
  • MVVMパターン
  • 修理
  • Bing MapsWPFコントロール
  • テスト目的でのみ使用します 郵便配達員

サーバー側:

  • .NET C#WebAPIコア2
  • 依存性注入
  • JWT認証

必要なもの:

  • Visual Studio 2017コミュニティ(またはお持ちのエディション)

バックエンド

クイックスタート

ASP.NET CoreWebアプリケーションを使用して新しいVisualStudioソリューションを開始します。

ウェブとアプリのトリッキーな方法

wpf reactui:新しいビジュアルスタジオASP.NET CoreWebアプリケーション

WPFアプリのバックエンドとしてのみ使用するため、APIとして構成します。

wpf reactui:APIとして構成する

最終的には、次のような構造のVSソリューションになります。

ドキュメントのデザインを計画するときに、デザイナーが知っておく必要があるのは次のうちどれですか?

wpf reactui:VSソリューションの例

これまでのところ、RESTAPIバックエンドを開始するために必要なものはすべて揃っています。プロジェクトを実行すると、IIS ExpressでホストされているWebサイトを指すWebブラウザー(Visual Studioで設定されているもの)が読み込まれ、JSONオブジェクトを使用したREST呼び出しへの応答が表示されます。

次に、RESTサービスのJWT認証を設定します。

startup.csの終わりにファイルに、次の行を追加します。

static readonly byte[] JwtKey = Encoding.ASCII.GetBytes(@'this is a test key'); private void LoadJwtAuthorization(IServiceCollection services) { services.AddAuthentication(x => { x.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme; x.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme; }) .AddJwtBearer(x => { x.Events = new JwtBearerEvents { OnTokenValidated = context => { var userId = int.Parse(context.Principal.Identity.Name); if (userId == 0) { //Handle user validation against DB context.Fail('Unauthorized'); } return Task.CompletedTask; } }; x.RequireHttpsMetadata = false; x.SaveToken = true; x.TokenValidationParameters = new TokenValidationParameters { ValidateIssuerSigningKey = true, IssuerSigningKey = new SymmetricSecurityKey(JwtKey), ValidateIssuer = false, ValidateAudience = false }; }); }

また、ConfigureServicesの内部メソッド、AddMvcの前に作成したメソッドを呼び出しますメソッドが呼び出されます。

public void ConfigureServices(IServiceCollection services) { LoadJwtAuthorization(services); services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1); }

最後に、Configureを調整しますメソッドなので、次のようになります。

public void Configure(IApplicationBuilder app, IHostingEnvironment env) { if (env.IsDevelopment()) { app.UseDeveloperExceptionPage(); } app.UseAuthentication(); app.UseMvc(); }

これまで、コントローラーで使用されるJWT認証が定義されている場合はそれを確立しました。次に、説明した認証を使用するようにコントローラーを調整します。

ValuesControllerに、AuthorizeAttributeを追加しますだからそれはこれに似ています:

[Route('api/[controller]')] [ApiController] [Authorize] public class ValuesController : ControllerBase { ... }

ここで、サービスを実行しようとすると、次のような401Unauthorizedエラーが発生します。

Postmanの不正なエラー

そのため、ユーザーを認証するメソッドを追加する必要があります。ここでは簡単にするために、同じValuesControllerで実行します。クラス。

[AllowAnonymous] [HttpPost('authenticate')] public IActionResult Authenticate([FromBody]JObject userInfo) { var username = userInfo['username'].ToString(); var password = userInfo['password'].ToString(); //We would validate against the DB if (username != 'user' || password != '123') { return BadRequest(new { message = 'Username or password is incorrect' }); } // return basic user info (without password) and token to store on the front-end return Ok(CreateUserToken(1)); } private string CreateUserToken(int userId) { var tokenHandler = new JwtSecurityTokenHandler(); var tokenDescriptor = new SecurityTokenDescriptor { Subject = new ClaimsIdentity(new[] { new Claim(ClaimTypes.Name, userId.ToString()) }), Expires = DateTime.UtcNow.AddDays(7), SigningCredentials = new SigningCredentials(new SymmetricSecurityKey(Startup.JwtKey), SecurityAlgorithms.HmacSha256Signature) }; var token = tokenHandler.CreateToken(tokenDescriptor); return tokenHandler.WriteToken(token); }

これで、匿名アクセスを使用するメソッドが作成されました。つまり、認証されていないクライアントも含め、すべてのクライアントがJSONオブジェクトを含むPOSTメッセージを使用してメソッドを呼び出すことができ、ユーザー名の文字列とパスワードの文字列を渡すことができます。

Postmanで確認すると、次のようになります。

WPFリアクティブ:認証

ご覧のとおり、認証メソッドの結果は、APIに対して実行するすべての呼び出しのトークンとして使用する必要がある文字列そのものでした。

トークンがメッセージのヘッダーに含まれると、検証が行われ、正しいパラメーターが渡されると、サービスはメソッドを実行してその値を返します。たとえば、valuesコントローラーを呼び出してトークンを渡すと、以前と同じ結果が得られます。

WPFリアクティブ:認証2

次に、追跡している現在の車の緯度と経度を取得するメソッドを作成します。繰り返しになりますが、簡単にするために、これはダミーのメソッドであり、最初はランダムな場所に戻り、メソッドが呼び出されるたびに一定の距離だけ車を動かし始めます。

まず、Get(int id)を調整しますValuesControllerのメソッドこのように見えるようにするクラス:

[HttpGet('{id}')] public ActionResult Get(int id) { var location = LocationHelper.GetCurrentLocation(id); dynamic jsonObject = new JObject(); jsonObject.Latitude = location.latitude; jsonObject.Longitude = location.longitude; return jsonObject.ToString(); }

次に、新しいLocationHelperを追加します追跡されている車の現在および将来の場所を処理するクラス。

public static class LocationHelper { private static readonly Random Randomizer = new Random(); private const double PositionDelta = 0.0001d; internal static (double latitude, double longitude) GetCurrentLocation(int id) { if (!Locations.ContainsKey(id)) { Locations.Add(id, default((double latitude, double longitude))); } //This method updates the last known location for the car and simulates its movement UpdateLocation(id); return Locations[id]; } private static void UpdateLocation(int id) { (double latitude, double longitude)loc = Locations[id]; //If the default value is found, randomly assign a starting point. if (loc.latitude == default(double) && loc.longitude == default(double)) { loc = Locations[id] = GetRandomStartingPoint(); } if (Randomizer.Next(2) > 0) { //In this scenario we simulate an updated latitude loc.latitude = loc.latitude + PositionDelta; } else { //Simulated longitude change loc.longitude = loc.longitude + PositionDelta; } Locations[id] = loc; } private static (double latitude, double longitude) GetRandomStartingPoint() { //Set inside the continental US return (Randomizer.Next(31, 49), Randomizer.Next(-121, -75)); } private static readonly Dictionary Locations = new Dictionary(); }

バックエンドは以上です。

フロントエンド:

次に、新しいWPFアプリを作成します。作成すると、VisualStudioは次の構造の新しいプロジェクトをソリューションに追加します。

WPFアプリの構造

Bing Mapsコントロール:

Bing MapsでWPFコントロールを使用するには、SDK(上記参照)をインストールし、WPFアプリケーションへの参照として追加する必要があります。インストールした場所によっては、DLLが別のパスにある場合があります。デフォルトの場所にインストールし、次のように追加しました。

手順1:WPFプロジェクトの[参照]セクションを右クリックして、[

手順1:WPFプロジェクトの[参照]セクションを右クリックし、[参照の追加]をクリックします。

手順2:Bing Maps WPFControlインストールのパスを参照します。

手順2:Bing Maps WPFControlインストールのパスを参照します。

Webフォントと印刷フォント

ステップ3:[OK]をクリックして、プロジェクトに追加します。

ステップ3:[OK]をクリックして、プロジェクトに追加します。

次に、reactiveui、reactiveui-wpfのnugetパッケージを追加しますおよびrefit WPFプロジェクトに追加します。これにより、リアクティブプログラミングを使用してビューモデルを作成したり、RESTAPIを使用したりできます。

手順1:WPFプロジェクトの[参照]セクションを右クリックし、[NuGetパッケージの管理]をクリックします。

手順1:WPFプロジェクトの[参照]セクションを右クリックし、[NuGetパッケージの管理]をクリックします。

手順2:[参照]タブで、[reactiveui]を検索し、[インストール]をクリックして、[reactiveui-wpf]を検索し、[インストール]をクリックします。最後に、[refit]を検索して、[インストール]をクリックします。

手順2:[参照]タブで、[reactiveui]を検索し、[インストール]をクリックして、[reactiveui-wpf]を検索し、[インストール]をクリックします。最後に、[refit]を検索して、[インストール]をクリックします。

ViewModelを作成します。 MainViewModel.csという名前の新しいクラスを追加します次のようにします。

public class MainViewModel : ReactiveObject { #region Private Members private readonly ITrackingService _service; private readonly ISubject _locationUpdate; #endregion #region Methods public MainViewModel() { _service = Locator.Current.GetService(); _locationUpdate = new Subject(); UpdateCar = ReactiveCommand.Create(() => { var parsedCorrectly = int.TryParse(NewCarToFollow, out int newCar); NewCarToFollow = null; if (!parsedCorrectly) { MessageBox.Show('There was an error reading the number of the car to follow. Please, review it.', 'Car Tracking Service', MessageBoxButton.OK, MessageBoxImage.Warning); return; } FollowedCar = newCar; }, canExecute: this.WhenAnyValue(x => x.NewCarToFollow).Select(x => !string.IsNullOrWhiteSpace(x))); /*This Scheduled method is where we get the location for the car being followed every 500 ms. We call the service with the car id, our JWT Token, and transform the result to a ValueTuple (double latitude, double longitude) to pass to our Subject's OnNext method so it can be received by the view */ Scheduler.Default.SchedulePeriodic(TimeSpan.FromMilliseconds(500), () => _service.GetLocation(FollowedCar, App.GetToken()) .Select(jo => ( latitude: double.Parse(jo['Latitude'].ToString()), longitude: double.Parse(jo['Longitude'].ToString()) )).Subscribe(newLocation => _locationUpdate.OnNext(newLocation))); } #endregion #region Properties private string _newCarToFollow; public string NewCarToFollow { get => _newCarToFollow; set => this.RaiseAndSetIfChanged(ref _newCarToFollow, value); } private int _followedCar = 1; public int FollowedCar { get => _followedCar; set => this.RaiseAndSetIfChanged(ref _followedCar, value); } public IObservable LocationUpdate => _locationUpdate; private ReactiveCommand _updateCar; public ReactiveCommand UpdateCar { get => _updateCar; set => this.RaiseAndSetIfChanged(ref _updateCar, value); } #endregion }

ビューにViewModelがあることを知らせるため添付して使用できるようにするには、MainView.xaml.csにいくつかの変更を加える必要があります。ファイル。

public partial class MainWindow : IViewFor { public MainWindow() { InitializeComponent(); ViewModel = Locator.CurrentMutable.GetService(); /*Our ViewModel exposes an IObservable with a parameter of type ValueTuple (double latitude, double longitude) and it gets called every time the ViewModel updates the car's location from the REST API.*/ ViewModel.LocationUpdate .ObserveOn(RxApp.MainThreadScheduler) .Subscribe(SetLocation); } private void SetLocation((double latitude, double longitude) newLocation) { //New location for the tracked vehicle. var location = new Location(newLocation.latitude, newLocation.longitude); //Remove previous pin myMap.Children.Clear(); //Center pin and keep same Zoom Level myMap.SetView(location, myMap.ZoomLevel); var pin = new Pushpin { Location = location, Background = Brushes.Green }; //Add new pin to the map myMap.Children.Add(pin); } /// /// Allows the ViewModel to be used on the XAML via a dependency property /// public static readonly DependencyProperty ViewModelProperty = DependencyProperty.Register('ViewModel', typeof(MainViewModel), typeof(MainWindow), new PropertyMetadata(default(MainViewModel))); /// /// Implementation for the IViewFor interface /// object IViewFor.ViewModel { get => ViewModel; set => ViewModel = (MainViewModel)value; } /// /// Regular property to use the ViewModel from this class /// public MainViewModel ViewModel { get => (MainViewModel)GetValue(ViewModelProperty); set => SetValue(ViewModelProperty, value); } }

次に、MainWindow.xamlを変更しますこのように見えるようにするファイル:

Car to follow

CredentialsProviderを調整することが重要です独自のBingMapsキーを持つプロパティ。

.netのWebAPI

REST APIにアクセスできるようにするために、refitを使用します。必要なのは、使用するAPIメソッドを説明するインターフェースを作成することだけです。そのため、次のコンテンツを含むITrackingServiceという新しいインターフェイスを作成します。

public interface ITrackingService { [Post('/api/values/authenticate')] IObservable Authenticate([Body] JObject user); [Get('/api/values/{id}')] IObservable GetLocation(int id, [Header('Authorization')] string authorization); }

最後に、Appを変更します依存性注入を含めるクラス(reactiveuiへの参照を含めたときに追加されたSplatを使用)、ServerUriを設定します(REST APIを実行するときに取得するポートに変更する必要があります)、アプリケーションの最初の段階でログインをシミュレートします。

public partial class App : Application { protected override void OnStartup(StartupEventArgs e) { base.OnStartup(e); SetDependencyInjection(); LogIn(); } private void SetDependencyInjection() { Locator.CurrentMutable.RegisterLazySingleton(() => RestService.For(ServerUri), typeof(ITrackingService)); Locator.CurrentMutable.RegisterLazySingleton(() => new MainViewModel(), typeof(MainViewModel)); } private static string Token; private const string ServerUri = 'http://localhost:54587'; private void LogIn() { try { var userInfo = new JObject { ['username'] = 'user', ['password'] = '123' }; Token = Locator.Current.GetService() .Authenticate(userInfo) .Wait(); } catch { MessageBox.Show('There was an error validating the user. Is the service up?'); Shutdown(); } } internal static string GetToken() { return $'Bearer {Token}'; } }

最後に、アプリケーションを実行すると、500ミリ秒ごとにREST APIから座標が取得され、移動中の車のリアルタイムシミュレーションを確認できます。ユーザーは、追跡している車を他のIDに変更することもでき、そのための新しいデータセットが作成されます。

この小さな例が、WPFでリアクティブプログラミングを使用してRESTAPIをアクセス可能な方法で処理するための基本を示していることを願っています。

これからいつでもソースプロジェクト全体をダウンロードできます リポジトリ 。

この例を続けるには、理解を深めるのに役立つ可能性のあるいくつかの領域があります。

  • ログインウィンドウを作成し、ユーザーがログインおよびログアウトできるようにします。
  • データベースからのユーザーデータを検証します。
  • さまざまなユーザーロールを作成し、REST APIで特定のメソッドを制限して、特定のロールを持つユーザーのみがそれらにアクセスできるようにします。
  • すべての演算子とその動作を通過するリアクティブプログラミングの詳細を理解する Rxマーブル 。 Rx Marblesは、ストリームと対話し、ストリーム内のデータポイントに演算子を適用できる優れたアプリケーションです。

結論

リアクティブプログラミングは、このパラダイムに固有の通常の問題に遭遇することなく、イベント駆動型プログラミングを使用する制御された方法を実現しようと努める場合に有益であることがわかります。新しい開発にそれを使用することは、十分にサポートされているオープンソースライブラリへの参照をいくつか追加するのと同じくらい簡単です。ただし、最も重要なことは、既存のコードベースに組み込むことは進歩的であり、それを実装していないコンポーネントとの下位互換性を損なうべきではないということです。この記事ではWPFのリアクティブプログラミングについて説明しましたが、ほとんどの主要な言語とフレームワークへの移植があり、リアクティブプログラミングをあらゆる種類の開発者にとって良い冒険にしています。

演習として、次に、次のことを行う必要があります。

  • プロジェクトの動作を次のように拡張します
    • ユーザー、車、場所のデータベースを追加する
    • データベースから車の場所を取得し、ユーザーに表示します。ユーザーが一定期間にわたって車の動きを探索できるようにする
    • ユーザー権限の追加。管理者ユーザーが新しい車とユーザーを作成し、通常のユーザーに読み取り専用アクセスを許可できるようにします。 JWT認証にロールを追加します。
  • の.NETリアクティブ拡張機能のソースコードを確認する https://github.com/dotnet/reactive

基本を理解する

JWTとは何ですか?

JSON Web Tokenは、2者間の情報のセットを表す安全な方法であるJSONオブジェクトです。

なぜJWTベースの認証を使用するのですか?

JWTは、安全性と堅牢性のために認証を使用する最新のアプリケーションの最も一般的な代替手段の1つです。

オブザーバブルの用途は何ですか?

これらは、イベント駆動型および非同期プログラミングに整然と、多くのオーバーヘッドなしで取り組む簡単な方法を提供します。

依存性注入の利点は何ですか?

新しいオブジェクトの構築は、コードベースの1つのポイントで定義でき、それ以降は、オブジェクトを使用できるようになると想定できます。

WPFはプログラミング言語ですか?

いいえ。WindowsPresentationFoundationは、WindowsベースのアプリケーションでユーザーインターフェイスをレンダリングするためのMicrosoftによるグラフィカルサブシステムです。

WPFの用途は何ですか?

これは、Windows用のデスクトップアプリケーションを設計する方法の1つです。その点で、Windowsフォームの代替と考えることができます。

WPFは学ぶ価値がありますか?

WPFは、新しいデスクトップアプリケーションを開発するためにMicrosoftが推奨する方法です。 10年間の安定性を備えたこのツールは、美しくモダンなアプリケーションを作成するために使用される柔軟なツールセットであり、Windows環境では他に類を見ない関心の分離(ルックアンドフィールとデザイン)を可能にします。

最高のUXブック:10の本質的かつ非伝統的な読み物

Uxデザイン

最高のUXブック:10の本質的かつ非伝統的な読み物
ウェブサイトの再設計を検討する理由-ヒントと推奨事項

ウェブサイトの再設計を検討する理由-ヒントと推奨事項

Uxデザイン

人気の投稿
教師あり機械学習アルゴリズムの調査
教師あり機械学習アルゴリズムの調査
AnsiblePlaybookでElasticStackを自動的に更新する
AnsiblePlaybookでElasticStackを自動的に更新する
マイクロインタラクションによるより良いUX
マイクロインタラクションによるより良いUX
JavaScriptでテスト可能なコードを書く:簡単な概要
JavaScriptでテスト可能なコードを書く:簡単な概要
Railsヘルパーの使用方法:ブートストラップカルーセルのデモンストレーション
Railsヘルパーの使用方法:ブートストラップカルーセルのデモンストレーション
 
生体認証セキュリティ–パスワードなしの認証または流行の鍵?
生体認証セキュリティ–パスワードなしの認証または流行の鍵?
シニアテクニカルリクルーター
シニアテクニカルリクルーター
ScalaとJava:なぜScalaを学ぶ必要があるのですか?
ScalaとJava:なぜScalaを学ぶ必要があるのですか?
ピークフローと線形割り当て問題
ピークフローと線形割り当て問題
図像学の超簡単ガイド
図像学の超簡単ガイド
人気の投稿
  • システムをカスタマイズする際に考慮すべき重要な原則ではないものは次のうちどれですか
  • グラス・スティーガル法の廃止は金融危機を引き起こしませんでした
  • 人工知能と経済
  • 諮問委員会の設置方法
  • モジュールを使用すると、プログラマーはチームで作業できなくなります。
  • C ++の不和ボット
カテゴリー
Uxデザイン 投資家と資金調達 リモートの台頭 分散チーム アジャイル プロジェクト管理 計画と予測 財務プロセス トレンド データサイエンスとデータベース

© 2021 | 全著作権所有

apeescape2.com