ViewModels

Die ViewModels beinhalten die Funktionen und Eigenschaften der einzelnen Views. Bei der Namensnennung ist der Name des ViewModels identisch zur View, so bleibt das Projekt übersichtlicher. Des Weiteren sind alle ViewModels und Views in logische Unterordner organisiert. Alle ViewModels nutzen DependencyInjection, hier wird beim Erstellen der aktuelle IDispatcher und IServiceProvider injiziert, welche später für weitere…

Image Description

Die ViewModels beinhalten die Funktionen und Eigenschaften der einzelnen Views. Bei der Namensnennung ist der Name des ViewModels identisch zur View, so bleibt das Projekt übersichtlicher.

public DashboardPageViewModel() { ... }
public DashboardPage() { ... }

Des Weiteren sind alle ViewModels und Views in logische Unterordner organisiert.

  • ViewModels: Alle ViewModels der Hauptansichten, welche im AppShell angewählt werden können
  • ViewModels\Modals: Alle ViewModels für modale Seiten, zu welchen navigiert werden kann
  • ViewModels\Pages: Alle ViewModels weitere statische Seiten
  • ViewModels\Settings: Alle ViewModels für die einzelnen Einstellungesseiten, welche über die SettingsPage aufgerufen werden können

Alle ViewModels nutzen DependencyInjection, hier wird beim Erstellen der aktuelle IDispatcher und IServiceProvider injiziert, welche später für weitere Funktionen verwendet werden können.

#region Ctor
public DashboardPageViewModel(IDispatcher dispatcher, IServiceProvider provider) : base(dispatcher, provider)
{
    Dispatcher = dispatcher;
    OnDataChanged = Refresh;
}
#endregion

Base- & AppViewModel

Unser Template bietet zwei Basisklassen. von welchen alle weiteren ViewModels erben sollten. Dabei unterscheiden sich beide Klassen wie folgt.

BaseViewModel

Das BaseViewModel bietet alle grundlegenden und notwendigen Eigenschaften und Funktionen, welche ein ViewModel benötigt, um richtig zu funktionieren. Einfache Seiten, wie die Einstellungsseiten, können von dieser Klasse erben, da kein Zugriff auf die App-Inhalte nötig ist.

AppViewModel

Das AppViewModel hingegen, welches ebenfalls vom BaseViewModel erbt, beinhaltet zusätzlich noch allen appspezifischen Eigenschaften und Funktionen. Das könnten zum Beispiel, bei einer Aktien-App, die Depots sein. Somit sollten alle weiteren ViewModels, welche Inhalte der App anzeigen und verarbeiten, auch vom AppViewModel erben.

Beispiel

Als Beispiel, das AppViewModel unserer „Aktien-Monitor-App“. Die einzelnen Funktionen wurden gekürzt, um die Datei übersichtlicher zu halten.

namespace AppBasement.ViewModels
{
    [QueryProperty(nameof(PrimaryDepot), "primarydepot")]
    [QueryProperty(nameof(PrimaryWatchlist), "primarywatchlist")]
    [QueryProperty(nameof(SelectedDepot), "depot")]
    [QueryProperty(nameof(CurrentStock), "stock")]
    [QueryProperty(nameof(SelectedWatchlist), "watchlist")]
    [QueryProperty(nameof(SelectedMarketplace), "marketplace")]
    public partial class AppViewModel : BaseViewModel
    {

        #region QueryParameter
        [ObservableProperty]
        protected Depot primaryDepot;
        partial void OnPrimaryDepotChanged(Depot value)
        {
            // Binding this directly seems to cause issues with the SfCircularGauge content...
            TotalWorth = value?.TotalWorth ?? 0;
            TotalCosts = value?.TotalCosts ?? 0;
            Growth = value?.Growth ?? 0;
            if (value != null)
            {
                value.DepotChanged -= _primaryDepot_DepotChanged;
                value.DepotChanged += _primaryDepot_DepotChanged;
            }
        }

        [ObservableProperty]
        protected Depot selectedDepot;

        [ObservableProperty]
        protected WatchList primaryWatchlist;

        [ObservableProperty]
        protected WatchList selectedWatchlist;

        [ObservableProperty]
        protected Marketplace selectedMarketplace;

        [ObservableProperty]
        Stock currentStock;
        #endregion

        #region Properties

        #region Actions
        public Func<Task> OnDataChanged;
        #endregion

        #region States

        [ObservableProperty]
        bool hasDepot = false;

        [ObservableProperty]
        bool hasWatchlist = false;

        [ObservableProperty]
        bool isLoadingDepots = false;

        [ObservableProperty]
        bool isLoadingWatchlists = false;

        [ObservableProperty]
        double totalWorth = 0;

        [ObservableProperty]
        double totalCosts = 0;

        [ObservableProperty]
        double growth = 0;

        #endregion

        #region Forms

        [ObservableProperty]
        bool keepFormOpen = false;

        #endregion

        #region ModalPopups

        [ObservableProperty]
        bool tutorialShown = false;

        [ObservableProperty]
        bool changeInfosShown = false;

        #endregion

        #endregion

        #region Collections
        [ObservableProperty]
        [NotifyPropertyChangedFor(nameof(HasDepot))]
        ObservableCollection<Depot> depots = new();
        partial void OnDepotsChanged(ObservableCollection<Depot> value)
        {
            HasDepot = value?.Count > 0;
        }

        [ObservableProperty]
        [NotifyPropertyChangedFor(nameof(HasWatchlist))]
        ObservableCollection<WatchList> watchlists = new();
        partial void OnWatchlistsChanged(ObservableCollection<WatchList> value)
        {
            HasWatchlist = value?.Count > 0;
        }

        [ObservableProperty]
        ObservableCollection<Marketplace> marketplaces = new();

        #endregion

        #region Constructor
        public AppViewModel(IDispatcher dispatcher) : base(dispatcher)
        {
            Dispatcher = dispatcher;
            LoadSettings();
        }
        #endregion

        #region Events
        public void Pages_Loaded(object sender, EventArgs e)
        {
            Task.Run(OnLoadedAsync);
        }

        protected void _primaryDepot_DepotChanged(object sender, DepotChangedEventArgs e)
        {
            try
            {
                if (sender is Depot primary)
                {
                    TotalWorth = primary.TotalWorth;
                    TotalCosts = primary.TotalCosts;
                    Growth = primary.Growth;
                }
            }
            catch (Exception exc)
            {
                // Log Error
                EventManager.Instance.LogError(exc);
            }
        }

        public void Depots_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
        {
            OnPropertyChanged(nameof(Depots));
#if CloudSync
            if (SettingsApp.Cloud_EnableSync)
            {
                SettingsApp.SyncToCloud(CloudSettingsIdentifier.Customers);
            }
#endif
        }
        public void Stocks_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
        {
            OnPropertyChanged(nameof(Depots));
#if CloudSync
            if (SettingsApp.Cloud_EnableSync)
            {
                SettingsApp.SyncToCloud(CloudSettingsIdentifier.Customers);
            }
#endif
        }

        protected void CurrentDepot_DepotChanged(object sender, DepotChangedEventArgs e)
        {
            OnPropertyChanged(nameof(Depots));
#if CloudSync
            if (SettingsApp.Cloud_EnableSync)
            {
                SettingsApp.SyncToCloud(CloudSettingsIdentifier.Customers);
            }
#endif
        }
        #endregion

        #region Commands

        #region Database
        [RelayCommand]
        protected async Task LoadDepotsFromDatabase()
        {
            try
            {
                // Something to be done here...
            }
            catch (Exception exc)
            {
                // Log error
                EventManager.Instance.LogError(exc);
            }
            IsBusy = false;
        }

        [RelayCommand]
        protected async Task LoadWatchlistsFromDatabase()
        {
            try
            {
                // Something to be done here...
            }
            catch (Exception exc)
            {
                // Log error
                EventManager.Instance.LogError(exc);
            }
            IsBusy = false;
        }
        #endregion

        #region ShellNavigation
        [RelayCommand]
        protected async Task NewItem(object parameter)
        {
            try
            {
                // Something to be done here...
            }
            catch (Exception exc)
            {
                // Log error
                EventManager.Instance.LogError(exc);
            }
            IsBusy = false;
        }

        [RelayCommand]
        protected async Task EditItem(object parameter)
        {
            try
            {
                // Something to be done here...
            }
            catch (Exception exc)
            {
                // Log error
                EventManager.Instance.LogError(exc);
            }
            IsBusy = false;
        }

        [RelayCommand]
        protected async Task ViewItem(object parameter)
        {
            try
            {
                // Something to be done here...
            }
            catch (Exception exc)
            {
                // Log error
                EventManager.Instance.LogError(exc);
            }
            IsBusy = false;
        }

        [RelayCommand]
        protected async Task DeleteItem(object parameter)
        {
            try
            {
                // Something to be done here...
            }
            catch (Exception exc)
            {
                // Log error
                EventManager.Instance.LogError(exc);
            }
            IsBusy = false;
        }

        [RelayCommand]
        protected async Task ExportItem(object parameter)
        {
            try
            {
                // Something to be done here...
            }
            catch (Exception exc)
            {
                // Log error
                EventManager.Instance.LogError(exc);
            }
            IsBusy = false;
        }

        [RelayCommand]
        protected async Task AddTransactionToItem(object parameter)
        {
            try
            {
                // Something to be done here...
            }
            catch (Exception exc)
            {
                // Log error
                EventManager.Instance.LogError(exc);
            }
            IsBusy = false;
        }

        [RelayCommand]
        protected async Task AddDividendToItem(object parameter)
        {
            try
            {
                // Something to be done here...
            }
            catch (Exception exc)
            {
                // Log error
                EventManager.Instance.LogError(exc);
            }
            IsBusy = false;
        }

        [RelayCommand]
        protected async Task AddStocksToDepot(object parameter)
        {
            try
            {
                // Something to be done here...
            }
            catch (Exception exc)
            {
                // Log error
                EventManager.Instance.LogError(exc);
            }
        }

        [RelayCommand]
        protected async Task ShowStockDetails(object parameter)
        {
            try
            {
                // Something to be done here...
            }
            catch (Exception exc)
            {
                // Log error
                EventManager.Instance.LogError(exc);
            }
            IsBusy = false;
        }

        [RelayCommand]
        protected async Task RefreshStockRate(object parameter)
        {
            try
            {
                // Something to be done here...
            }
            catch (Exception exc)
            {
                // Log error
                EventManager.Instance.LogError(exc);
            }
            IsBusy = false;
        }

        [RelayCommand]
        protected async Task ShowDepotDetails(object parameter)
        {
            try
            {
                // Something to be done here...
            }
            catch (Exception exc)
            {
                // Log error
                EventManager.Instance.LogError(exc);
            }
            IsBusy = false;
        }

        [RelayCommand]
        protected async Task MakeDepotPrimary(object parameter)
        {
            try
            {
                // Something to be done here...
            }
            catch (Exception exc)
            {
                // Log error
                EventManager.Instance.LogError(exc);
            }
            IsBusy = false;
        }
        #endregion

        #region ListView
        [RelayCommand]
        protected async Task ActionFromHold(object parameter)
        {
            try
            {
                // Something to be done here...
            }
            catch (Exception exc)
            {
                // Log error
                EventManager.Instance.LogError(exc);
            }
        }

        [RelayCommand]
        protected async Task ActionFromTap(object parameter)
        {
            try
            {
                // Something to be done here...
            }
            catch (Exception exc)
            {
                // Log error
                EventManager.Instance.LogError(exc);
            }
        }

        [RelayCommand]
        protected async Task ActionFromDoubleTap(object parameter)
        {
            try
            {
                // Something to be done here...
            }
            catch (Exception exc)
            {
                // Log error
                EventManager.Instance.LogError(exc);
            }
        }

        [RelayCommand]
        protected void ActionWhileSwiping(object parameter)
        {
            try
            {
                // Something to be done here...
            }
            catch (Exception exc)
            {
                // Log error
                EventManager.Instance.LogError(exc);
            }
        }

        [RelayCommand]
        protected async Task ActionFromSwipeGesture(object parameter)
        {
            try
            {
                // Something to be done here...
            }
            catch (Exception exc)
            {
                // Log error
                EventManager.Instance.LogError(exc);
            }
            IsBusy = false;
        }

        #endregion

        #region Wiki
        [RelayCommand]
        protected async Task OpenWikiUri(object paramter)
        {
            try
            {
                // Something to be done here...
            }
            catch (Exception exc)
            {
                // Log Error
                EventManager.Instance.LogError(exc);
            }
        }
        #endregion

        #endregion

        #region Methods

        #region DataLoading
        /// <summary>
        /// Loads the data needed for this input form modal.
        /// Attention! This code runs not on the UI Thread!
        /// </summary>
        /// <returns></returns>
        protected async Task OnLoadDataAsync()
        {
            try
            {
                // Something to be done here...
            }
            catch (Exception exc)
            {
                // Log error
                EventManager.Instance.LogError(exc);
            }
        }
        protected async Task OnLoadWatchlistsAsync()
        {
            try
            {
                // Something to be done here...
            }
            catch (Exception exc)
            {
                // Log error
                EventManager.Instance.LogError(exc);
            }
        }

        /// <summary>
        /// Updates the needed information from the static AppData.
        /// Important: This must run on the UIThread!
        /// </summary>
        protected void UpdateFromStaticAppData()
        {
            try
            {
                // Something to be done here...
            }
            catch (Exception exc)
            {
                // Log error
                EventManager.Instance.LogError(exc);
            }
        }

        /// <summary>
        /// Updates the needed information from the static AppData.
        /// Important: This must run on the UIThread!
        /// </summary>
        protected void UpdateWatchlistFromStaticAppData()
        {
            try
            {
                // Something to be done here...
            }
            catch (Exception exc)
            {
                // Log error
                EventManager.Instance.LogError(exc);
            }
        }

        protected async Task UpdateStockRateAsync(Stock stock, int daysBack = 7)
        {
            await UpdateStockRatesAsync(new() { stock }, daysBack);
        }
        protected async Task UpdateStockRatesAsync(List<Stock> stocks, int daysBack = 7)
        {
            try
            {
                // Something to be done here...
            }
            catch (Exception exc)
            {
                //Log error
                EventManager.Instance.LogError(exc);
            }
        }
        #endregion

        #endregion

        #region Lifecycle
        public async Task OnLoadedAsync()
        {
            try
            {
                // Something to be done here...
                EventManager.Instance.LogInfo(new()
                {
                    Message = $"{AppInfo.Current.Name}: {nameof(OnLoadedAsync)} called from {nameof(AppViewModel)}.",
                    SourceName = nameof(OnLoadedAsync),
                });
            }
            catch (Exception exc)
            {
                // Log error
                EventManager.Instance.LogError(exc);
            }
        }

        public async Task OnAppearing()
        {
            try
            {
                await Task.Delay(1);
                EventManager.Instance.LogInfo(new()
                {
                    Message = $"{AppInfo.Current.Name}: {nameof(OnAppearing)} called from {nameof(AppViewModel)}.",
                    SourceName = nameof(OnAppearing),
                });
            }
            catch (Exception exc)
            {
                // Log error
                EventManager.Instance.LogError(exc);
            }
        }
        #endregion
    }
}

War dieser Artikel hilfreich für Sie?

Ja Nein

Verwandte Artikel