ReactiveUI ونمط MVVM في تطبيقات WPF

نشرت: 2022-03-11

البرمجة التفاعلية هي نموذج برمجة غير متزامن يهتم بتدفقات البيانات وانتشار التغيير. - ويكيبيديا

بمجرد قراءة هذه الجملة ، قد ينتهي بك الأمر بنفس الطريقة التي انتهيت بها عندما قرأتها لأول مرة: لا يوجد مكان أقرب إلى فهم مدى ملاءمتها. مزيد من التفاني في المفاهيم الأساسية وستفهم بسرعة أهميتها. في الأساس ، يمكنك التفكير في البرمجة التفاعلية في البداية على النحو التالي: "البرمجة المدفوعة بالأحداث على المنشطات." تخيل معالج الحدث على أنه تدفق ، وفكر في كل عملية إطلاق للمعالج كمرجع جديد في الدفق. باختصار ، ما ينتهي بك الأمر هو البرمجة التفاعلية.

هناك بعض المفاهيم التي قد ترغب في فهمها قبل الخوض في البرمجة التفاعلية أكثر قليلاً. الأشياء المرصودة هي الأشياء التي تمنحك الوصول إلى التدفقات التي تحدثنا عنها. والغرض منها هو منحك نافذة على البيانات الموجودة في الدفق. بمجرد فتح هذه النافذة ، يمكنك إلقاء نظرة على البيانات بأي طريقة تختارها باستخدام عوامل التشغيل عليها ، وبالتالي تحديد متى وكيف يتفاعل تطبيقك مع البث. أخيرًا ، يمكنك تحديد المراقبون في الدفق الناتج لتحديد الإجراء الذي سيحدث في كل مرة يتم فيها إصدار بيانات جديدة بواسطة الدفق.

من الناحية العملية ، هذا يعني فقط أنك تتمتع بمزيد من التحكم في الطريقة التي يتفاعل بها تطبيقك مع ما يحدث ، سواء كان المستخدم ينقر على زر ، أو يتلقى تطبيقك استجابة HTTP ، أو يتعافى من الاستثناءات. بمجرد أن تبدأ في رؤية فوائد استخدام البرمجة التفاعلية (التي يوجد منها الكثير) ، لن تتمكن من العودة. هذا ببساطة لأن معظم الأشياء التي يقوم بها التطبيق تتفاعل بطريقة معينة مع حدث معين.

الآن ، هذا لا يعني أنه لا توجد سلبيات لهذا النهج الجديد. بادئ ذي بدء ، يمكن أن يكون منحنى التعلم حادًا جدًا. لقد رأيت بنفسي كيف يكافح المطورون (صغار وكبار ومهندسون معماريون ، من بين آخرين) لمعرفة ما يفترض أن يكتبوه أولاً ، وفي أي ترتيب يتم تنفيذ التعليمات البرمجية الخاصة بهم ، أو كيفية تصحيح الأخطاء. توصيتي عند تقديم هذه المفاهيم لأول مرة هي عرض الكثير من الأمثلة. عندما يبدأ المطورون في رؤية الكيفية التي من المفترض أن تعمل بها الأشياء وكيف يتم استخدامها ، فإنهم سيتعودون عليها.

لقد كنت أعمل مع تطبيقات سطح المكتب لأكثر من 10 سنوات (معظمها Visual Basic 6 و Java Swing و Windows Forms) قبل أن أستخدم Windows Presentation Foundation (WPF) لأول مرة في عام 2010. بشكل أساسي ، تم إنشاء WPF من أجل يحل محل Windows Forms ، وهو أول إطار عمل لتطوير سطح المكتب من .NET.

الاختلافات الرئيسية بين WPF و Windows Forms كبيرة ، ولكن أهمها هي:

  • يستخدم WPF نماذج تطوير جديدة أكثر قوة وتم اختبارها بدقة.
  • باستخدام WPF ، يمكنك الحصول على فصل قوي بين التصميم والتشفير لواجهة المستخدم.
  • يسمح WPF بالكثير من التخصيص والتحكم في واجهة المستخدم الخاصة بك.

بمجرد أن بدأت في تعلم WPF وقدراته ، أحببته تمامًا! لم أصدق مدى سهولة تنفيذ نموذج MVVM ومدى نجاح ربط الخصائص. لم أكن أعتقد أنني سأجد أي شيء لتحسين طريقة العمل هذه حتى عثرت على البرمجة التفاعلية واستخدامها مع WPF:

في هذا المنشور ، آمل أن أكون قادرًا على إظهار تنفيذ بسيط جدًا لتطبيق WPF باستخدام البرمجة التفاعلية بنمط MVVM والوصول إلى واجهة برمجة تطبيقات REST.

سيكون التطبيق قادرًا على:

  • تتبع السيارات ومواقعها
  • خذ المعلومات المأخوذة من مصدر محاكاة
  • اعرض هذه المعلومات على المستخدم في عنصر تحكم Bing Maps WPF

العمارة

ستقوم ببناء عميل WPF الذي يستهلك خدمة RESTful Web API Core 2.

جانب العميل:

  • WPF
  • رد الفعل
  • حقن التبعية
  • نمط MVVM
  • التجديد
  • تحكم WPF في خرائط Bing
  • لأغراض الاختبار فقط ، سنستخدم ساعي البريد

جانب الخادم:

  • NET C # Web API Core 2
  • حقن التبعية
  • مصادقة JWT

ماذا ستحتاج:

  • مجتمع Visual Studio 2017 (أو أي إصدار قد يكون لديك)

النهاية الخلفية

بداية سريعة

ابدأ حل Visual Studio جديد باستخدام تطبيق ويب ASP.NET Core.

رد فعل wpf: تطبيق ويب ASP.NET Core جديد للاستوديو المرئي

قم بتكوينه ليكون API لأننا سنستخدمه فقط كنهاية خلفية لتطبيق WPF الخاص بنا.

wpf Reactiveui: تكوين كواجهة برمجة تطبيقات

يجب أن ننتهي بحل VS بهيكل مشابه لهذا:

wpf رد الفعل: مثال على الحل

حتى الآن ، لدينا كل ما نحتاجه لبدء الواجهة الخلفية لـ REST API. إذا قمنا بتشغيل مشروعنا ، فسيتم تحميل مستعرض ويب (ذلك الذي قمنا بتعيينه في Visual Studio) مشيرًا إلى موقع ويب مستضاف على IIS Express والذي سيعرض استجابة لمكالمة REST باستخدام كائن JSON.

الآن ، سنقوم بإعداد مصادقة JWT لخدمة REST الخاصة بنا.

في نهاية ملف 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 { ... }

الآن ، إذا حاولنا تشغيل خدمتنا ، فسنحصل على خطأ 401 غير مصرح به مثل هذا:

خطأ غير مصرح به في ساعي البريد

لذلك ، سنحتاج إلى إضافة طريقة لمصادقة مستخدمينا. من أجل التبسيط هنا ، سنفعل ذلك على نفس فئة 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); }

لقد أنشأنا الآن طريقة مع وصول مجهول ، مما يعني أن جميع العملاء ، حتى أولئك الذين لم تتم مصادقتهم ، سيكونون قادرين على الاتصال بها باستخدام رسالة POST تحتوي على كائن JSON وتمرير سلسلة لاسم المستخدم وسلسلة لكلمة المرور الخاصة به.

عندما نراجعها مع Postman ، نحصل على هذا:

رد فعل WPF: المصادقة

كما نرى ، كانت نتيجة طريقة المصادقة هي السلسلة ذاتها التي نحتاج الآن إلى استخدامها كرمز لكل مكالمة نريد إجراؤها لواجهة برمجة التطبيقات.

بمجرد تضمين الرمز المميز في رؤوس الرسالة ، يتم التحقق من الصحة ، وإذا تم تمرير المعلمات الصحيحة ، تقوم الخدمة بتشغيل الطريقة وإرجاع قيمتها. على سبيل المثال ، إذا اتصلنا الآن بوحدة التحكم في القيم وقمنا بتمرير الرمز المميز ، فسنحصل على نفس النتيجة كما في السابق:

رد فعل WPF: المصادقة 2

الآن ، سننشئ طريقة للحصول على خطوط الطول والعرض للسيارة الحالية التي نتتبعها. مرة أخرى ، من أجل التبسيط ، ستكون مجرد طريقة وهمية ستعيد في البداية موقعًا عشوائيًا وتبدأ في تحريك السيارة بمقدار ثابت من المسافة في كل مرة يتم فيها استدعاء الطريقة.

أولاً ، نقوم بضبط طريقة Get(int id) في فئة ValuesController لجعلها تبدو كما يلي:

 [HttpGet("{id}")] public ActionResult<string> 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<int, (double latitude, double longitude)> Locations = new Dictionary<int, (double latitude, double longitude)>(); }

هذا كل شيء في النهاية الخلفية.

الواجهة الأمامية:

سنقوم الآن بإنشاء تطبيق WPF جديد. بمجرد إنشائه ، سيضيف Visual Studio مشروعًا جديدًا بالهيكل التالي إلى حلنا.

هيكل تطبيق WPF

التحكم في خرائط Bing:

لاستخدام عنصر تحكم WPF لخرائط Bing ، سنحتاج إلى تثبيت SDK (المشار إليه أعلاه) وإضافته كمرجع إلى تطبيق WPF الخاص بنا. اعتمادًا على المكان الذي قمت بتثبيته ، قد يكون DLL على مسار مختلف. لقد قمت بتثبيته على الموقع الافتراضي وقمت بإضافته على النحو التالي:

الخطوة 1: انقر بزر الماوس الأيمن فوق قسم المراجع الخاص بمشروع WPF الخاص بك ثم انقر فوق
الخطوة 1: انقر بزر الماوس الأيمن فوق قسم "المراجع" الخاص بمشروع WPF الخاص بك ، ثم انقر فوق "إضافة مرجع".

الخطوة 2: استعرض مسار تثبيت Bing Maps WPF Control.
الخطوة 2: استعرض مسار تثبيت Bing Maps WPF Control.

الخطوة 3: انقر فوق "موافق" لإضافته إلى المشروع.
الخطوة 3: انقر فوق "موافق" لإضافته إلى المشروع.

بعد ذلك ، سنضيف حزم refit من أجل reactiveui ، و reactiveui-wpf ، وتجديده في مشروع WPF الخاص بنا ، مما سيسمح لنا بإنشاء نماذج عرض باستخدام البرمجة التفاعلية بالإضافة إلى استهلاك REST API.

الخطوة 1: انقر بزر الماوس الأيمن فوق قسم المراجع في مشروع WPF الخاص بك وانقر فوق Manage NuGet Packages.
الخطوة 1: انقر بزر الماوس الأيمن فوق قسم المراجع في مشروع WPF الخاص بك وانقر فوق Manage NuGet Packages.

الخطوة 2: في علامة التبويب "استعراض" ، ابحث عن "رد الفعل" ، وانقر فوق "تثبيت" ، وابحث عن "رد الفعل" ، وانقر فوق تثبيت ، وأخيرًا ، ابحث عن "تجديد" وانقر فوق تثبيت.
الخطوة 2: في علامة التبويب "استعراض" ، ابحث عن "رد الفعل" ، وانقر فوق "تثبيت" ، وابحث عن "رد الفعل" ، وانقر فوق تثبيت ، وأخيرًا ، ابحث عن "تجديد" وانقر فوق تثبيت.

سنقوم الآن بإنشاء ViewModel بنا. أضف فئة جديدة تسمى MainViewModel.cs تبدو كما يلي:

 public class MainViewModel : ReactiveObject { #region Private Members private readonly ITrackingService _service; private readonly ISubject<(double latitude, double longitude)> _locationUpdate; #endregion #region Methods public MainViewModel() { _service = Locator.Current.GetService<ITrackingService>(); _locationUpdate = new Subject<(double latitude, double longitude)>(); 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<(double latitude, double longitude)> 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<MainViewModel> { public MainWindow() { InitializeComponent(); ViewModel = Locator.CurrentMutable.GetService<MainViewModel>(); /*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); } /// <summary> /// Allows the ViewModel to be used on the XAML via a dependency property /// </summary> public static readonly DependencyProperty ViewModelProperty = DependencyProperty.Register("ViewModel", typeof(MainViewModel), typeof(MainWindow), new PropertyMetadata(default(MainViewModel))); /// <summary> /// Implementation for the IViewFor interface /// </summary> object IViewFor.ViewModel { get => ViewModel; set => ViewModel = (MainViewModel)value; } /// <summary> /// Regular property to use the ViewModel from this class /// </summary> public MainViewModel ViewModel { get => (MainViewModel)GetValue(ViewModelProperty); set => SetValue(ViewModelProperty, value); } }

بعد ذلك ، سنقوم بتعديل ملف MainWindow.xaml لجعله يبدو كما يلي:

 <Window x:Class="WpfApp.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:wpf="clr-namespace:Microsoft.Maps.MapControl.WPF;assembly=Microsoft.Maps.MapControl.WPF" xmlns:local="clr-namespace:WpfApp" DataContext="{Binding ViewModel,RelativeSource={RelativeSource Self}}" d:DataContext="{d:DesignInstance Type=local:MainViewModel, IsDesignTimeCreatable=True}" mc:Ignorable="d" WindowStartupLocation="CenterScreen" Title="Car Tracker" Height="800" Width="1200"> <Grid> <Grid.RowDefinitions> <RowDefinition Height="Auto"/> <RowDefinition Height="*"/> </Grid.RowDefinitions> <DockPanel> <StackPanel Orientation="Horizontal" Margin="10" DockPanel.Dock="Left"> <Label>Car to follow</Label> <TextBox Width="50" Text="{Binding NewCarToFollow, UpdateSourceTrigger=PropertyChanged}" VerticalAlignment="Center"/> <Button Margin="15,0,0,0" Content="Update Followed Car" Command="{Binding UpdateCar}"/> </StackPanel> <TextBlock Text="{Binding FollowedCar,StringFormat=Following Car: {0}}" Margin="0,0,10,0" HorizontalAlignment="Right" VerticalAlignment="Center" DockPanel.Dock="Right"/> </DockPanel> <wpf:Map x:Name="myMap" ZoomLevel="15" Grid.Row="1" Margin="10" CredentialsProvider="ENTER-YOUR-BING-MAPS-CREDENTIAL-HERE"/> </Grid> </Window>

من المهم ضبط خاصية CredentialsProvider باستخدام مفتاح خرائط Bing الخاص بك.

لتتمكن من الوصول إلى واجهة برمجة تطبيقات REST الخاصة بنا ، سنستخدم ميزة إعادة التجهيز. كل ما نحتاجه هو إنشاء واجهة تصف طرق واجهات برمجة التطبيقات التي سنستخدمها. لذلك ، قمنا بإنشاء واجهة جديدة تسمى ITrackingService بالمحتوى التالي:

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

أخيرًا ، نقوم بتعديل فئة App لتشمل حقن التبعية (باستخدام Splat ، والذي تمت إضافته عندما قمنا بتضمين المرجع إلى reactiveui ) ، وقم بتعيين 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<ITrackingService>(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<ITrackingService>() .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}"; } }

أخيرًا ، عندما نقوم بتشغيل تطبيقنا ، سنكون قادرين على رؤية محاكاة في الوقت الفعلي لسيارة متحركة مع إحداثياتها المأخوذة من REST API كل 500 مللي ثانية. يمكن للمستخدم أيضًا تغيير السيارة التي يتم متابعتها إلى أي معرف آخر ، وسيتم إنشاء مجموعة جديدة من البيانات لها.

آمل أن يكون هذا المثال الصغير قد أظهر أساسيات التعامل مع واجهة برمجة تطبيقات REST مع البرمجة التفاعلية في WPF بطريقة يسهل الوصول إليها.

يمكنك دائمًا تنزيل المشروع المصدر بالكامل من هذا المستودع.

هناك بعض المجالات للمتابعة مع هذا المثال والتي قد تساعدك على زيادة فهمك:

  • أنشئ نافذة تسجيل دخول واسمح للمستخدم بتسجيل الدخول والخروج.
  • تحقق من صحة بيانات المستخدم من قاعدة بيانات.
  • قم بإنشاء أدوار مستخدم مختلفة وقم بتقييد طرق معينة في واجهة برمجة تطبيقات REST بحيث لا يتمكن سوى مستخدم له دور معين من الوصول إليها.
  • افهم المزيد عن البرمجة التفاعلية التي تمر عبر جميع المشغلين وسلوكهم باستخدام Rx Marbles. Rx Marbles هو تطبيق أنيق يسمح لك بالتفاعل مع التدفقات وتطبيق عوامل التشغيل على نقاط البيانات الموجودة فيها.

خاتمة

يمكن أن تكون البرمجة التفاعلية مفيدة عند السعي لتحقيق طريقة مسيطر عليها لاستخدام البرمجة المدفوعة بالأحداث دون الوقوع في المشاكل المعتادة المتأصلة في هذا النموذج. يعد استخدامه للتطورات الجديدة أمرًا بسيطًا مثل إضافة بضع مراجع إلى مكتبات مفتوحة المصدر مدعومة جيدًا. ولكن الأهم من ذلك ، أن دمجها في قواعد التعليمات البرمجية الحالية يمكن أن يكون تقدميًا ويجب ألا يكسر التوافق مع المكونات التي لا تنفذها. تناولت هذه المقالة البرمجة التفاعلية لـ WPF ، ولكن هناك منافذ لمعظم اللغات والأطر الرئيسية مما يجعل البرمجة التفاعلية مغامرة جيدة لأي نوع من المطورين.

كتمرين ، بعد ذلك ، يجب عليك:

  • تمديد سلوك المشروع من خلال
    • إضافة قاعدة بيانات للمستخدمين والسيارات والمواقع
    • الحصول على موقع السيارات من قاعدة البيانات وإظهارها للمستخدم. اسمح للمستخدم باستكشاف تحركات السيارة على مدار فترة زمنية
    • إضافة أذونات المستخدم. اسمح للمستخدمين المشرفين بإنشاء سيارات ومستخدمين جدد ومنح حق الوصول للقراءة فقط للمستخدمين العاديين. أضف الأدوار إلى مصادقة JWT.
  • راجع كود المصدر لملحقات .NET التفاعلية في https://github.com/dotnet/reactive