/// <summary> /// Constructs a new Settings view with the given view model, and with the provided /// AddCategoryControl control. The AddCategoryControl instance is not actually displayed /// within the Settings view at all, but rather the Settings view internally finds its parent /// window and shows this instance of AddCategoryControl within a flyout. This is part of the /// reason why the control is supplied. /// /// The real reason is that presently, there is not a better solution to the problem where /// the AddCategoryControl needs to hold a reference to the Engine instance that it is /// creating categories for. Handing off this instance to this view or view model, then /// passing it up to an internally created instance of AddCategoryControl seems like a worse /// solution than this. Doing it this way, the AddCategoryControl instance is kept neatly /// together with associated objects at the App code behind level. However, XXX TODO, find a /// more elegant solution to this. /// </summary> /// <param name="viewModel"> /// The settings view model. /// </param> /// <param name="addCategoryControl"> /// The user control that generates, by user input, new FilteringCategory instances. /// </param> /// <exception cref="ArgumentException"> /// In the event that either the supplied SettingsViewModel or AddCategoryControl instances /// are null, will throw ArgumentException. /// </exception> public Settings(SettingsViewModel viewModel, AddCategoryControl addCategoryControl) { InitializeComponent(); m_viewModel = viewModel; if (m_viewModel == null) { throw new ArgumentException("Expected valid instance of SettingsViewModel"); } m_addCategoryControl = addCategoryControl; if (m_addCategoryControl == null) { throw new ArgumentException("Expected valid instance of AddCategoryControl"); } DataContext = m_viewModel; // The control handles input, validation and creation of objects independently, so we // simply display the control to the user and listen for any events where a category was // successfully created. m_addCategoryControl.CategoryCreated += OnFilteringCategoryCreated; m_btnDeleteCategory.Click += OnDeleteCategoryClicked; m_btnShowAddCategory.Click += OnShowAddCategoryClicked; m_regexDigitOnlyValidation = new Regex("[^0-9]"); m_regexDollarValueOnlyValidation = new Regex("[^0-9\\.]"); }
/// <summary> /// Inits all the various views for the application, which will be pushed and popped on the /// primary window as requested or required. /// </summary> private void InitViews() { if (m_filteringEngine == null) { throw new Exception("Engine must be initialized prior to initializing views, as views require references to allow user control."); } // We null check, because if the save state load worked, these conditions will be // false and we won't overwrite our restored state. // This collection is initialized here because it has a direct connection to the UI. if (m_filteredApplicationsTable == null) { m_filteredApplicationsTable = new ConcurrentDictionary<string, FilteredAppModel>(); } if (m_modelDashboard == null) { m_modelDashboard = new DashboardModel(m_filteringEngine); } if (m_viewModelDashboard == null) { m_viewModelDashboard = new DashboardViewModel(m_modelDashboard); } if (m_modelStatistics == null) { m_modelStatistics = new StatisticsModel(); } if (m_viewModelStatistics == null) { m_viewModelStatistics = new StatisticsViewModel(m_modelStatistics); } if (m_modelSettings == null) { m_modelSettings = new SettingsModel(); } if (m_viewModelSettings == null) { m_viewModelSettings = new SettingsViewModel(m_modelSettings); } if(m_viewModelWaste == null) { m_viewModelWaste = new WasteViewModel(m_viewModelSettings, m_viewModelDashboard); } // Necessary because we use a background worker. This thread != UI thread. Current.Dispatcher.BeginInvoke( System.Windows.Threading.DispatcherPriority.Normal, (Action)delegate () { m_modelStatistics.FilterCategories = m_filteringCategoriesObservable; m_modelSettings.FilterCategories = m_filteringCategoriesObservable; m_primaryWindow = new MainWindow(); m_viewProgressWait = new ProgressWait(); m_viewStatistics = new Statistics(m_viewModelStatistics); m_viewDashboard = new Dashboard(m_viewModelDashboard); m_viewSettings = new Settings(m_viewModelSettings, new AddCategoryControl(m_filteringEngine)); m_viewWaste = new Waste(m_viewModelWaste); m_primaryWindow.ViewChangeRequest += OnViewChangeRequest; m_viewDashboard.ViewChangeRequest += OnViewChangeRequest; m_viewStatistics.ViewChangeRequest += OnViewChangeRequest; m_viewSettings.ViewChangeRequest += OnViewChangeRequest; m_viewWaste.ViewChangeRequest += OnViewChangeRequest; // Listen for the statistics view requests for app-wide stats deletion. m_viewStatistics.ClearStatisticsRequested += OnClearAllStatsRequest; MainWindow = m_primaryWindow; } ); }
/// <summary> /// Loads the models, if present, from their serialized JSON files. /// </summary> private void LoadProgramState() { var stateOutputDir = AppDomain.CurrentDomain.BaseDirectory + @"User\"; JsonSerializerSettings settings = new JsonSerializerSettings(); settings.Converters.Add(new FilteringCategoryConverter(m_filteringEngine)); settings.Converters.Add(new DashboardConverter(m_filteringEngine)); settings.Converters.Add(new FilteredAppConverter()); // This thread != UI thread. Current.Dispatcher.BeginInvoke( System.Windows.Threading.DispatcherPriority.Normal, (Action)delegate () { if (m_filteringCategoriesObservable == null) { m_filteringCategoriesObservable = new ObservableCollection<CategorizedFilteredRequestsViewModel>(); } } ); // Restore filtering categories. if (File.Exists(stateOutputDir + "FilterCategories.json")) { var filterCatsSerialized = File.ReadAllText(stateOutputDir + "FilterCategories.json"); var filteringCatsList = JsonConvert.DeserializeObject<List<FilteringCategory>>(filterCatsSerialized, settings); foreach (var filteringList in filteringCatsList) { // Ensure lists are up to date. try { filteringList.UpdateAndLoad(); Current.Dispatcher.BeginInvoke( System.Windows.Threading.DispatcherPriority.Normal, (Action)delegate () { m_filteringCategoriesObservable.Add(new CategorizedFilteredRequestsViewModel(filteringList)); } ); } catch (Exception err) { m_logger.Error("Error while updating filtering list: {0}.", err.Message); } } } // Restore dashboard stats. if (File.Exists(stateOutputDir + "Dashboard.json")) { var dashboardSerialized = File.ReadAllText(stateOutputDir + "Dashboard.json"); m_modelDashboard = JsonConvert.DeserializeObject<DashboardModel>(dashboardSerialized, settings); m_viewModelDashboard = new DashboardViewModel(m_modelDashboard); } // Restore settings. if (File.Exists(stateOutputDir + "Settings.json")) { var settingsSerialized = File.ReadAllText(stateOutputDir + "Settings.json"); m_modelSettings = JsonConvert.DeserializeObject<SettingsModel>(settingsSerialized, settings); m_viewModelSettings = new SettingsViewModel(m_modelSettings); } // Restore filtered apps. if (File.Exists(stateOutputDir + "FilteredApps.json")) { var filteredAppsSerialized = File.ReadAllText(stateOutputDir + "FilteredApps.json"); m_filteredApplicationsTable = JsonConvert.DeserializeObject<ConcurrentDictionary<string, FilteredAppModel>>(filteredAppsSerialized, settings); foreach (var entry in m_filteredApplicationsTable) { m_logger.Info(entry.Key); Current.Dispatcher.BeginInvoke( System.Windows.Threading.DispatcherPriority.Normal, (Action)delegate () { m_viewModelDashboard.FilteredApplications.Add(new FilteredAppViewModel(entry.Value)); } ); } } }
/// <summary> /// Constructs a new WasteViewModel instance. /// </summary> /// <param name="settingsViewModel"> /// The SettingsViewModel from which to receive updated user input about waste calculations. /// </param> /// <param name="dashboardViewModel"> /// The m_dashboardViewModelViewModel from which to receive updated blocking statistics. /// </param> /// <exception cref="ArgumentException"> /// In the event that the arguments provided are null, will throw ArgumentException. /// </exception> public WasteViewModel(SettingsViewModel settingsViewModel, DashboardViewModel dashboardViewModel) { m_settingsViewModel = settingsViewModel; m_dashboardViewModel = dashboardViewModel; if (m_settingsViewModel == null) { throw new ArgumentException("Expected valid instance of SettingsViewModel"); } if (m_dashboardViewModel == null) { throw new ArgumentException("Expected valid instance of m_dashboardViewModelViewModel"); } // Simply subscribe to the property changed events of these view models, and replicate // the same change event for mirrored data members. m_dashboardViewModel.PropertyChanged += OnLinkedPropertiesChanged; m_settingsViewModel.PropertyChanged += OnLinkedPropertiesChanged; }