
واجهات المستخدم لتطبيقات المؤسسة الحديثة معقدة للغاية. غالبًا ما تحتاج ، كمطور ، إلى تنفيذ التنقل داخل التطبيق أو التحقق من صحة إدخال المستخدم أو إظهار أو إخفاء الشاشات بناءً على تفضيلات المستخدم. للحصول على أفضل UX ، يجب أن يكون تطبيقك قادرًا على حفظ الحالة على القرص عند تعليق التطبيق واستعادة الحالة عند استئناف التطبيق.
يوفر ReactiveUI تسهيلات تسمح لك بالاستمرار في حالة التطبيق من خلال إجراء تسلسل لشجرة طراز العرض عند إيقاف تشغيل التطبيق أو تعليقه. تختلف أحداث التعليق لكل منصة. يستخدم ReactiveUI حدث Exit
لـ WPF و ActivityPaused
لـ Xamarin.Android و DidEnterBackground
for Xamarin.iOS و OnLaunched
لـ OnLaunched
.
في هذا البرنامج التعليمي ، سنقوم ببناء نموذج تطبيقي يوضح استخدام ميزة ReactiveUI Suspension مع Avalonia - إطار عمل واجهة المستخدم الرسومية المستندة إلى .NET Core XAML. من المتوقع أن تكون على دراية بنمط MVVM ومع الامتدادات التفاعلية قبل قراءة هذه الملاحظة. يجب أن تعمل الخطوات الموضحة في البرنامج التعليمي إذا كنت تستخدم نظام التشغيل Windows 10 أو Ubuntu 18 وتثبيت .NET SDK. لنبدأ!
تمهيد المشروع
لرؤية توجيه ReactiveUI قيد التنفيذ ، نقوم بإنشاء مشروع .NET Core جديد يستند إلى قوالب تطبيق Avalonia. ثم نقوم بتثبيت حزمة Avalonia.ReactiveUI
. توفر الحزمة خطافات دورة حياة Avalonia خاصة بالمنصة ، وبنية توجيه وتفعيل . تذكر تثبيت .NET Core و git قبل تنفيذ الأوامر أدناه.
git clone https://github.com/AvaloniaUI/avalonia-dotnet-templates git --git-dir ./avalonia-dotnet-templates/.git checkout 9263c6b dotnet new --install ./avalonia-dotnet-templates dotnet new avalonia.app -o ReactiveUI.Samples.Suspension cd ./ReactiveUI.Samples.Suspension dotnet add package Avalonia.ReactiveUI dotnet add package Avalonia.Desktop dotnet add package Avalonia
لنقم بتشغيل التطبيق والتأكد من أنه يعرض نافذة تعرض "مرحبًا بك في Avalonia!"
# Use .NET Core version which you have installed. # It can be netcoreapp2.0, netcoreapp2.1 and so on. dotnet run --framework netcoreapp3.0

تثبيت أفالونيا معاينة يبني من MyGet
يتم نشر أحدث حزم Avalonia إلى MyGet في كل مرة يتم فيها دفع التزام جديد إلى الفرع master
لمستودع Avalonia على GitHub. لاستخدام أحدث الحزم من MyGet في تطبيقنا ، سنقوم بإنشاء ملف nuget.config
. ولكن قبل القيام بذلك ، نقوم بإنشاء ملف sln
للمشروع الذي تم إنشاؤه مسبقًا ، باستخدام .NET Core CLI :
dotnet new sln # Ctrl+C dotnet sln ReactiveUI.Samples.Suspension.sln add ReactiveUI.Samples.Suspension.csproj
الآن نقوم بإنشاء ملف nuget.config
بالمحتوى التالي:
<?xml version="1.0" encoding="utf-8"?> <configuration> <packageSources> <add key="AvaloniaCI" value="https://www.myget.org/F/avalonia-ci/api/v2" /> </packageSources> </configuration>
عادةً ما يتطلب الأمر إعادة تشغيل لإجبار IDE الخاص بنا على اكتشاف الحزم من خلاصة MyGet المضافة حديثًا ، لكن إعادة التحميل يجب أن تساعد أيضًا. بعد ذلك ، نقوم بترقية حزم Avalonia إلى الإصدار الأحدث (أو على الأقل إلى 0.9.1
) باستخدام واجهة المستخدم الرسومية مدير حزمة NuGet ، أو .NET Core CLI:
dotnet add package Avalonia.ReactiveUI --version 0.9.1 dotnet add package Avalonia.Desktop --version 0.9.1 dotnet add package Avalonia --version 0.9.1 cat ReactiveUI.Samples.Suspension.csproj
يجب أن يبدو ملف ReactiveUI.Samples.Suspension.csproj
إلى حد ما كما يلي الآن:
<Project Sdk="Microsoft.NET.Sdk"> <PropertyGroup> <OutputType>WinExe</OutputType> <TargetFramework>netcoreapp3.0</TargetFramework> </PropertyGroup> <ItemGroup> <Compile Update="**\*.xaml.cs"> <DependentUpon>%(Filename)</DependentUpon> </Compile> <AvaloniaResource Include="**\*.xaml"> <SubType>Designer</SubType> </AvaloniaResource> </ItemGroup> <ItemGroup> <PackageReference Include="Avalonia" Version="0.9.1" /> <PackageReference Include="Avalonia.Desktop" Version="0.9.1" /> <PackageReference Include="Avalonia.ReactiveUI" Version="0.9.1" /> </ItemGroup> </Project>
نقوم بإنشاء مجلدين جديدين داخل دليل جذر المشروع ، يدعى Views/
و ViewModels/
على التوالي. بعد ذلك ، قمنا بإعادة تسمية فئة MainView
إلى MainView
إلى المجلد Views/
المجلد. تذكر أن تعيد تسمية المراجع إلى الفصل المحرر في ملف XAML المقابل ، وإلا فلن يتم تجميع المشروع. تذكر أيضًا تغيير مساحة الاسم لـ MainView
إلى ReactiveUI.Samples.Suspension.Views
للحصول على التناسق. بعد ذلك ، نقوم بتحرير ملفين آخرين ، Program.cs
و App.xaml.cs
نضيف مكالمة إلى UseReactiveUI
إلى منشئ تطبيق Avalonia ، OnFrameworkInitializationCompleted
رمز تهيئة التطبيق إلى طريقة OnFrameworkInitializationCompleted
لتتوافق مع إرشادات إدارة تطبيق Avalonia
Program.cs
class Program {
App.xaml.cs
public class App : Application { public override void Initialize() => AvaloniaXamlLoader.Load(this);
قبل محاولة إنشاء المشروع ، نضمن إضافة توجيه using Avalonia.ReactiveUI
الجزء العلوي من ملف Program.cs
. على الأرجح ، قام IDE الخاص بنا باستيراد مساحة الاسم بالفعل ، ولكن إذا لم يحدث ذلك ، فسنحصل على خطأ وقت الترجمة. أخيرًا ، حان الوقت للتأكد من أن التطبيق يجمع ويدير ويظهر نافذة جديدة:
dotnet run --framework netcoreapp2.1

هناك طريقتان عامتان لتنظيم التنقل داخل التطبيق في تطبيق .NET عبر الأنظمة الأساسية - العرض أولاً وعرض الطراز أولاً. يفترض النهج السابق أن طبقة العرض تدير كومة التنقل - على سبيل المثال ، باستخدام فئات الإطار والصفحة الخاصة بالنظام الأساسي. مع النهج الأخير ، تهتم طبقة نموذج العرض بالتنقل عبر تجريد النظام الأساسي. تم تصميم الأدوات ReactiveUI مع الأخذ في الاعتبار طريقة عرض نموذج العرض الأول. يتكون توجيه ReactiveUI من تطبيق IScreen
، والذي يحتوي على حالة التوجيه الحالية ، والعديد من IRoutableViewModel
وعناصر تحكم XAML خاصة RoutedViewHost
تسمى RoutedViewHost
.

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

سنقرر ، ما هي خصائص نموذج العرض التي يجب حفظها عند إيقاف تشغيل التطبيق وأي الخصائص التي يجب إعادة إنشائها. ليست هناك حاجة لحفظ حالة الأمر التفاعلي الذي ينفذ واجهة ICommand
. عادةً ما تتم تهيئة فئة ReactiveCommand<TIn, TOut>
في المُنشئ ، CanExecute
مؤشر CanExecute
عادةً بشكل كامل على خصائص طراز العرض ويتم إعادة حسابه في كل مرة تتغير فيها أي من هذه الخصائص. إنه أمر قابل للنقاش إذا كنت تريد الاحتفاظ بنتائج البحث ، ولكن حفظ استعلام البحث يعد فكرة جيدة.
ViewModels / SearchViewModel.cs
[DataContract] public class SearchViewModel : ReactiveObject, IRoutableViewModel { private readonly ReactiveCommand<Unit, Unit> _search; private string _searchQuery;
نحتفل بفئة نموذج العرض بالكامل باستخدام السمة [DataContract]
، وعلّق على الخصائص التي سنقوم بتسلسلها باستخدام السمة [DataMember]
. هذا يكفي إذا كنا سنستخدم وضع تسلسل الاشتراك. النظر في أوضاع التسلسل ، يعني إلغاء الاشتراك أنه سيتم إجراء تسلسل لجميع الحقول والخصائص العامة ، ما لم تتجاهلها صراحة من خلال التعليق التوضيحي [IgnoreDataMember]
، يعني التقيد عكس ذلك. بالإضافة إلى ذلك ، نقوم بتطبيق واجهة IRoutableViewModel
في فئة طراز العرض الخاصة بنا. هذا مطلوب أثناء استخدام طراز العرض كجزء من مكدس التنقل.
تفاصيل التنفيذ لنموذج عرض تسجيل الدخولViewModels / LoginViewModel.cs
[DataContract] public class LoginViewModel : ReactiveObject, IRoutableViewModel { private readonly ReactiveCommand<Unit, Unit> _login; private string _password; private string _username;
يقوم طرازا العرض بتطبيق واجهة IRoutableViewModel
في شاشة التنقل. حان الوقت الآن لتطبيق واجهة IScreen
. مرة أخرى ، نستخدم سمات [DataContract]
للإشارة إلى الأجزاء التي يجب إجراء تسلسل وتلك التي يجب تجاهلها. في المثال أدناه ، يتم إعلان RoutingState
خاصية RoutingState
بشكل متعمد على أنه عام - وهذا يسمح RoutingState
بتعديل الخاصية عندما يتم إلغاء تسلسلها.
ViewModels / MainViewModel.cs
[DataContract] public class MainViewModel : ReactiveObject, IScreen { private readonly ReactiveCommand<Unit, Unit> _search; private readonly ReactiveCommand<Unit, Unit> _login; private RoutingState _router = new RoutingState(); public MainViewModel() {
في نموذج العرض الرئيسي الخاص بنا ، نقوم بحفظ حقل واحد فقط على القرص - وهو RoutingState
نوع RoutingState
. لا يتعين علينا حفظ حالة الأوامر التفاعلية ، حيث أن توفرها يعتمد تمامًا على الحالة الحالية لجهاز التوجيه ويتغير بشكل تفاعلي. لتتمكن من استعادة جهاز التوجيه إلى الحالة التي كان فيها بالضبط ، نقوم بتضمين معلومات النوع الموسعة الخاصة IRoutableViewModel
عند إجراء تسلسل IRoutableViewModel
التوجيه. سوف نستخدم TypenameHandling.All
إعداد Newtonsoft.Json لتحقيق ذلك لاحقًا. نضع MainViewModel
في ViewModels/
المجلد ، وضبط مساحة الاسم ليكون ReactiveUI.Samples.Suspension.ViewModels
.

التوجيه في تطبيق أفالونيا
في الوقت الحالي ، قمنا بتطبيق نموذج العرض التقديمي الخاص بطلبنا. في وقت لاحق ، يمكن استخراج فئات طراز العرض في مجموعة منفصلة تستهدف .NET Standard ، لذلك يمكن إعادة استخدام الجزء الأساسي من تطبيقنا عبر عدة أطر .NET GUI. الآن حان الوقت لتنفيذ جزء واجهة المستخدم الرسومية الخاص بأفالونيا من تطبيقنا. نقوم بإنشاء ملفين في المجلد Views/
، المسمى SearchView.xaml
و SearchView.xaml.cs
على التوالي. هذان هما الجزءان من طريقة عرض بحث واحدة - الأولى هي واجهة المستخدم الموصوفة بالتعريف في XAML ، والأخرى تحتوي على رمز C # خلف. هذا هو أساسًا طريقة عرض نموذج البحث التي تم إنشاؤها مسبقًا.
يجب أن تشعر باللهجة XAML المستخدمة في Avalonia على الفور للمطورين القادمين من WPF أو UWP أو XF. في المثال أعلاه ، نقوم بإنشاء تخطيط بسيط يحتوي على مربع نص وزر يقوم بتشغيل البحث. نحن نربط الخصائص والأوامر من SearchViewModel
إلى عناصر التحكم المعلنة في SearchView
.
المشاهدات / SearchView.xaml
<UserControl xmlns="https://github.com/avaloniaui" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" d:DataContext="{d:DesignInstance viewModels:SearchViewModel}" xmlns:viewModels="clr-namespace:ReactiveUI.Samples.Suspension.ViewModels" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" x:Class="ReactiveUI.Samples.Suspension.Views.SearchView" xmlns:reactiveUi="http://reactiveui.net" mc:Ignorable="d"> <Grid> <Grid.RowDefinitions> <RowDefinition Height="48" /> <RowDefinition Height="48" /> <RowDefinition Height="48" /> <RowDefinition Height="*" /> </Grid.RowDefinitions> <TextBlock Grid.Row="0" Text="Search view" Margin="5" /> <TextBox Grid.Row="1" Text="{Binding SearchQuery, Mode=TwoWay}" /> <Button Grid.Row="2" Content="Search" Command="{Binding Search}" /> </Grid> </UserControl>
المشاهدات / SearchView.xaml.cs
public sealed class SearchView : ReactiveUserControl<SearchViewModel> { public SearchView() {
قد يجد مطورو WPF و SearchView.xaml
لملف SearchView.xaml
مألوفًا أيضًا. تتم إضافة دعوة إلى WhenActivated
لتنفيذ منطق تنشيط العرض. يتم التخلص من الوسيطة التي يتم التخلص منها باعتبارها الوسيطة الأولى لـ WhenActivated
عند إلغاء تنشيط العرض. إذا كان التطبيق الخاص بك يستخدم الملاحظات الساخنة (مثل خدمات تحديد الموقع ، وأجهزة ضبط الوقت ، ومجمعات الأحداث) ، فسيكون قرارًا حكيمًا إرفاق الاشتراكات WhenActivated
القابل للتصرف عن طريق إضافة مكالمة DisposeWith
، وبالتالي فإن العرض سيتم إلغاء الاشتراك من تلك الملاحظات الساخنة و لن تحدث تسرب الذاكرة.
تفاصيل التنفيذ لعرض تسجيل الدخولالمشاهدات / LoginView.xaml
<UserControl xmlns="https://github.com/avaloniaui" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" d:DataContext="{d:DesignInstance viewModels:LoginViewModel, IsDesignTimeCreatable=True}" xmlns:viewModels="clr-namespace:ReactiveUI.Samples.Suspension.ViewModels" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" x:Class="ReactiveUI.Samples.Suspension.Views.LoginView" xmlns:reactiveUi="http://reactiveui.net" mc:Ignorable="d"> <Grid> <Grid.RowDefinitions> <RowDefinition Height="48" /> <RowDefinition Height="48" /> <RowDefinition Height="48" /> <RowDefinition Height="48" /> <RowDefinition Height="*" /> </Grid.RowDefinitions> <TextBlock Grid.Row="0" Text="Login view" Margin="5" /> <TextBox Grid.Row="1" Text="{Binding Username, Mode=TwoWay}" /> <TextBox Grid.Row="2" PasswordChar="*" Text="{Binding Password, Mode=TwoWay}" /> <Button Grid.Row="3" Content="Login" Command="{Binding Login}" /> </Grid> </UserControl>
المشاهدات / LoginView.xaml.cs
public sealed class LoginView : ReactiveUserControl<LoginViewModel> { public LoginView() { this.WhenActivated(disposables => { }); AvaloniaXamlLoader.Load(this); } }
نقوم بتحرير ملفات Views/MainView.xaml
و Views/MainView.xaml.cs
. نضيف عنصر التحكم RoutedViewHost
من Avalonia.ReactiveUI
مساحة الاسم إلى تخطيط الإطار الرئيسي XAML وربط خاصية Router
RoutedViewHost.Router
بخاصية RoutedViewHost.Router
. نضيف زرين ، أحدهما يفتح صفحة البحث والآخر يفتح صفحة التفويض.
المشاهدات / MainView.xaml
<Window xmlns="https://github.com/avaloniaui" 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" mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450" x:Class="ReactiveUI.Samples.Suspension.Views.MainView" xmlns:reactiveUi="http://reactiveui.net" Title="ReactiveUI.Samples.Suspension"> <Grid> <Grid.RowDefinitions> <RowDefinition Height="*" /> <RowDefinition Height="48" /> </Grid.RowDefinitions> <reactiveUi:RoutedViewHost Grid.Row="0" Router="{Binding Router}"> <reactiveUi:RoutedViewHost.DefaultContent> <TextBlock Text="Default Content" /> </reactiveUi:RoutedViewHost.DefaultContent> </reactiveUi:RoutedViewHost> <Grid Grid.Row="1"> <Grid.ColumnDefinitions> <ColumnDefinition Width="*" /> <ColumnDefinition Width="*" /> <ColumnDefinition Width="*" /> </Grid.ColumnDefinitions> <Button Grid.Column="0" Command="{Binding Search}" Content="Search" /> <Button Grid.Column="1" Command="{Binding Login}" Content="Login" /> <Button Grid.Column="2" Command="{Binding Router.NavigateBack}" Content="Back" /> </Grid> </Grid> </Window>
المشاهدات / MainView.xaml.cs
public sealed class MainView : ReactiveWindow<MainViewModel> { public MainView() { this.WhenActivated(disposables => { }); AvaloniaXamlLoader.Load(this); } }
تطبيق تجريبي توجيه Avalonia و ReactiveUI بسيط جاهز الآن. عندما يضغط المستخدم على أزرار البحث أو تسجيل الدخول ، يتم استدعاء الأمر الذي يؤدي إلى التنقل ويتم تحديث RoutingState
. يلاحظ عنصر التحكم RoutedViewHost
XAML حالة التوجيه ، ويحاول حل تطبيق IViewFor<TViewModel>
Locator.Current
من Locator.Current
. إذا تم IViewFor<TViewModel>
تطبيق IViewFor<TViewModel>
، فسيتم إنشاء مثيل جديد IViewFor<TViewModel>
في نافذة Avalonia.

نقوم بتسجيل IViewFor
و IViewFor
الخاصة IScreen
في طريقة App.OnFrameworkInitializationCompleted
، باستخدام Locator.CurrentMutable
. تسجيل IViewFor
مطلوب للتحكم RoutedViewHost
للعمل. يتيح تسجيل IScreen
إمكانية البحث عن خاصية تهيئة SearchViewModel
و LoginViewModel
أثناء إلغاء التسلسل ، وذلك باستخدام مُنشئ بلا معلمات.
App.xaml.cs
public override void OnFrameworkInitializationCompleted() {
دعنا نطلق تطبيقنا ونتأكد من أداء التوجيه كما ينبغي. إذا حدث خطأ ما في علامة XAML UI ، فسيقوم مترجم Avalonia XamlIl بإبلاغنا بأي أخطاء في وقت الترجمة. علاوة على ذلك ، XamlIl يدعم تصحيح الأخطاء XAML !
dotnet run --framework netcoreapp3.0

حفظ واستعادة حالة التطبيق
لقد حان الوقت الآن لتنفيذ برنامج التشغيل المعلق المسؤول عن حفظ واستعادة حالة التطبيق عندما يكون التطبيق معلقًا ويستأنف. AutoSuspendHelper
فئة AutoSuspendHelper
الخاصة AutoSuspendHelper
الأساسي بتهيئة الأشياء ، فأنت كمطور تحتاج فقط لإنشاء مثيل لها في جذر تكوين التطبيق. أيضًا ، تحتاج إلى تهيئة مصنع RxApp.SuspensionHost.CreateNewAppState
. إذا كان التطبيق لا يحتوي على بيانات محفوظة ، أو إذا كانت البيانات المحفوظة تالفة ، فإن ReactiveUI تستدعي طريقة المصنع هذه لإنشاء مثيل افتراضي لكائن حالة التطبيق.
ثم ، نقوم باستدعاء طريقة RxApp.SuspensionHost.SetupDefaultSuspendResume
، وتمرير نسخة جديدة من ISuspensionDriver
إليها. دعنا ISuspensionDriver
واجهة ISuspensionDriver
باستخدام Newtonsoft.Json وفصول من مساحة اسم System.IO
.
dotnet add package Newtonsoft.Json
Drivers / NewtonsoftJsonSuspensionDriver.cs
public class NewtonsoftJsonSuspensionDriver : ISuspensionDriver { private readonly string _file; private readonly JsonSerializerSettings _settings = new JsonSerializerSettings { TypeNameHandling = TypeNameHandling.All }; public NewtonsoftJsonSuspensionDriver(string file) => _file = file; public IObservable<Unit> InvalidateState() { if (File.Exists(_file)) File.Delete(_file); return Observable.Return(Unit.Default); } public IObservable<object> LoadState() { var lines = File.ReadAllText(_file); var state = JsonConvert.DeserializeObject<object>(lines, _settings); return Observable.Return(state); } public IObservable<Unit> SaveState(object state) { var lines = JsonConvert.SerializeObject(state, _settings); File.WriteAllText(_file, lines); return Observable.Return(Unit.Default); } }
تحتوي الطريقة الموصوفة على عيب - لن تعمل بعض فئات System.IO
مع إطار عمل UWP ، وتعمل تطبيقات UWP في صندوق رمل وتقوم بالأشياء بشكل مختلف. من السهل حل ذلك - كل ما عليك القيام به هو استخدام فصول StorageFolder
و StorageFolder
بدلاً من File
and Directory
عند استهداف UWP. لقراءة مكدس التنقل من القرص ، يجب أن يدعم برنامج التعليق تعليق إلغاء تسلسل كائنات JSON إلى IRoutableViewModel
ملموسة ، ولهذا السبب نستخدم إعداد TypeNameHandling.All
Newtonsoft.Json serializer. نسجل برنامج التعليق في جذر تكوين التطبيق ، في طريقة App.OnFrameworkInitializationCompleted
:
public override void OnFrameworkInitializationCompleted() {
تقوم فئة AutoSuspendHelper
من حزمة Avalonia.ReactiveUI
AutoSuspendHelper
دورة حياة للتطبيق الخاص بك ، لذلك سيكون إطار ReactiveUI على دراية ISuspensionDriver
كتابة حالة التطبيق على القرص ، وذلك باستخدام تطبيق ISuspensionDriver
المتوفر. بعد أن أطلقنا تطبيقنا ، سيقوم برنامج التشغيل المعلق بإنشاء ملف JSON جديد باسم appstate.json
. بعد إجراء تغييرات في واجهة المستخدم (على سبيل المثال ، اكتب إلى حد ما في حقول النص ، أو انقر فوق أي زر) ثم أغلق التطبيق ، appstate.json
ملف appstate.json
مشابه لما يلي:
appstate.jsonلاحظ أن كل كائن JSON في الملف يحتوي على مفتاح $type
مع اسم نوع مؤهل بالكامل ، بما في ذلك مساحة الاسم.
{ "$type": "ReactiveUI.Samples.Suspension.ViewModels.MainViewModel, ReactiveUI.Samples.Suspension", "Router": { "$type": "ReactiveUI.RoutingState, ReactiveUI", "_navigationStack": { "$type": "System.Collections.ObjectModel.ObservableCollection`1[[ReactiveUI.IRoutableViewModel, ReactiveUI]], System.ObjectModel", "$values": [ { "$type": "ReactiveUI.Samples.Suspension.ViewModels.SearchViewModel, ReactiveUI.Samples.Suspension", "SearchQuery": "funny cats" }, { "$type": "ReactiveUI.Samples.Suspension.ViewModels.LoginViewModel, ReactiveUI.Samples.Suspension", "Username": "worldbeater" } ] } } }
إذا قمت بإغلاق التطبيق ثم تشغيله مرة أخرى ، سترى نفس المحتوى على الشاشة كما رأيت من قبل! تعمل الوظيفة الموصوفة على كل نظام أساسي تدعمه ReactiveUI ، بما في ذلك UWP أو WPF أو Xamarin Forms أو Xamarin Native.

المكافأة: يمكن تنفيذ واجهة ISuspensionDriver
باستخدام Akavache - وهو متجر غير متزامن ومستمر لقيمة المفتاح. إذا قمت بتخزين بياناتك في قسم UserAccount
أو قسم Secure
، فسيتم نسخ بياناتك تلقائيًا على السحابة على iOS و UWP وستتاح عبر جميع الأجهزة التي تم تثبيت التطبيق عليها. أيضًا ، يوجد BundleSuspensionDriver
في حزمة ReactiveUI.AndroidSupport
. Xamarin.Essentials يمكن استخدام واجهات برمجة تطبيقات SecureStorage لتخزين البيانات أيضًا. يمكنك أيضًا تخزين حالة التطبيق على خادم بعيد أو في خدمة سحابية مستقلة عن النظام الأساسي.
روابط مفيدة