From 32b0a0199ff244cf3c3c8d431c35fb4cea09d65d Mon Sep 17 00:00:00 2001 From: Ben Date: Sat, 25 Jul 2015 19:02:42 +0100 Subject: [PATCH] Implemented async saving and loading --- Filtration.Interface/IDocument.cs | 6 +- Filtration.Interface/IEditableDocument.cs | 8 ++- .../TestItemFilterScriptRepository.cs | 8 +-- .../TestItemFilterPersistenceService.cs | 4 +- .../Providers/ThemeProvider.cs | 24 ++++--- .../Services/ThemePersistenceService.cs | 42 ++++++----- .../ViewModels/ThemeEditorViewModel.cs | 23 +++--- Filtration/App.xaml.cs | 2 +- Filtration/Filtration.csproj | 5 ++ .../ItemFilterScriptRepository.cs | 9 +-- Filtration/Resources/loading_spinner.gif | Bin 0 -> 48552 bytes .../Services/ItemFilterPersistenceService.cs | 31 +++++--- Filtration/Services/UpdateCheckService.cs | 4 +- .../AvalonDockWorkspaceViewModel.cs | 7 +- .../ViewModels/ItemFilterScriptViewModel.cs | 65 +++++++++++------ Filtration/ViewModels/MainWindowViewModel.cs | 66 +++++++++++++----- Filtration/ViewModels/StartPageViewModel.cs | 5 +- .../ToolPanes/SectionBrowserViewModel.cs | 13 ++++ .../AvalonDock/AvalonDockWorkspaceView.xaml | 3 +- Filtration/Views/MainWindow.xaml | 27 ++++++- Filtration/Views/MainWindow.xaml.cs | 11 ++- Filtration/packages.config | 1 + 22 files changed, 245 insertions(+), 119 deletions(-) create mode 100644 Filtration/Resources/loading_spinner.gif diff --git a/Filtration.Interface/IDocument.cs b/Filtration.Interface/IDocument.cs index e27b2ec..8f86095 100644 --- a/Filtration.Interface/IDocument.cs +++ b/Filtration.Interface/IDocument.cs @@ -1,9 +1,11 @@ -namespace Filtration.Interface +using System.Threading.Tasks; + +namespace Filtration.Interface { public interface IDocument { bool IsScript { get; } bool IsTheme { get; } - void Close(); + Task Close(); } } diff --git a/Filtration.Interface/IEditableDocument.cs b/Filtration.Interface/IEditableDocument.cs index 7102812..aebdd71 100644 --- a/Filtration.Interface/IEditableDocument.cs +++ b/Filtration.Interface/IEditableDocument.cs @@ -1,9 +1,11 @@ -namespace Filtration.Interface +using System.Threading.Tasks; + +namespace Filtration.Interface { public interface IEditableDocument : IDocument { bool IsDirty { get; } - void Save(); - void SaveAs(); + Task SaveAsync(); + Task SaveAsAsync(); } } diff --git a/Filtration.Tests/Repositories/TestItemFilterScriptRepository.cs b/Filtration.Tests/Repositories/TestItemFilterScriptRepository.cs index 8ac2d25..d17ec67 100644 --- a/Filtration.Tests/Repositories/TestItemFilterScriptRepository.cs +++ b/Filtration.Tests/Repositories/TestItemFilterScriptRepository.cs @@ -18,7 +18,7 @@ namespace Filtration.Tests.Repositories var mockPersistenceService = new Mock(); - mockPersistenceService.Setup(p => p.LoadItemFilterScript(testInputPath)).Verifiable(); + mockPersistenceService.Setup(p => p.LoadItemFilterScriptAsync(testInputPath)).Verifiable(); var mockItemFilterScriptViewModel = new Mock(); @@ -28,7 +28,7 @@ namespace Filtration.Tests.Repositories var repository = new ItemFilterScriptRepository(mockPersistenceService.Object, mockItemFilterScriptViewModelFactory.Object); // Act - var result = repository.LoadScriptFromFile(testInputPath); + var result = repository.LoadScriptFromFileAsync(testInputPath); // Assert mockPersistenceService.Verify(); @@ -42,7 +42,7 @@ namespace Filtration.Tests.Repositories var testInputPath = "C:\\TestPath.filter"; var mockPersistenceService = new Mock(); - mockPersistenceService.Setup(p => p.LoadItemFilterScript(testInputPath)).Throws(); + mockPersistenceService.Setup(p => p.LoadItemFilterScriptAsync(testInputPath)).Throws(); var mockItemFilterScriptViewModelFactory = new Mock(); @@ -51,7 +51,7 @@ namespace Filtration.Tests.Repositories // Act // Assert - Assert.Throws(() => repository.LoadScriptFromFile(testInputPath)); + Assert.Throws(() => repository.LoadScriptFromFileAsync(testInputPath)); } [Test] diff --git a/Filtration.Tests/Services/TestItemFilterPersistenceService.cs b/Filtration.Tests/Services/TestItemFilterPersistenceService.cs index 3d8c9e1..c665004 100644 --- a/Filtration.Tests/Services/TestItemFilterPersistenceService.cs +++ b/Filtration.Tests/Services/TestItemFilterPersistenceService.cs @@ -28,7 +28,7 @@ namespace Filtration.Tests.Services var service = new ItemFilterPersistenceService(mockFileSystemService.Object, mockItemFilterScriptTranslator.Object); // Act - var script = service.LoadItemFilterScript(TestInputPath); + var script = service.LoadItemFilterScriptAsync(TestInputPath); // Assert mockFileSystemService.Verify(); @@ -53,7 +53,7 @@ namespace Filtration.Tests.Services var service = new ItemFilterPersistenceService(mockFileSystemService.Object, mockItemFilterScriptTranslator.Object); // Act - service.SaveItemFilterScript(testScript); + service.SaveItemFilterScriptAsync(testScript); // Assert mockFileSystemService.Verify(); diff --git a/Filtration.ThemeEditor/Providers/ThemeProvider.cs b/Filtration.ThemeEditor/Providers/ThemeProvider.cs index 0a0afde..4600200 100644 --- a/Filtration.ThemeEditor/Providers/ThemeProvider.cs +++ b/Filtration.ThemeEditor/Providers/ThemeProvider.cs @@ -1,6 +1,7 @@ using System.Collections.ObjectModel; using System.IO; using System.Linq; +using System.Threading.Tasks; using AutoMapper; using Filtration.ObjectModel; using Filtration.ObjectModel.ThemeEditor; @@ -13,9 +14,9 @@ namespace Filtration.ThemeEditor.Providers { IThemeEditorViewModel NewThemeForScript(ItemFilterScript script); IThemeEditorViewModel MasterThemeForScript(ItemFilterScript script); - IThemeEditorViewModel LoadThemeFromFile(string filePath); - Theme LoadThemeModelFromFile(string filePath); - void SaveTheme(IThemeEditorViewModel themeEditorViewModel, string filePath); + Task LoadThemeFromFile(string filePath); + Task LoadThemeModelFromFile(string filePath); + Task SaveThemeAsync(IThemeEditorViewModel themeEditorViewModel, string filePath); } internal class ThemeProvider : IThemeProvider @@ -54,23 +55,26 @@ namespace Filtration.ThemeEditor.Providers return themeViewModel; } - public IThemeEditorViewModel LoadThemeFromFile(string filePath) + public async Task LoadThemeFromFile(string filePath) { - var model = _themePersistenceService.LoadTheme(filePath); + var model = await _themePersistenceService.LoadThemeAsync(filePath); var viewModel = Mapper.Map(model); viewModel.FilePath = filePath; return viewModel; } - public Theme LoadThemeModelFromFile(string filePath) + public async Task LoadThemeModelFromFile(string filePath) { - return _themePersistenceService.LoadTheme(filePath); + return await _themePersistenceService.LoadThemeAsync(filePath); } - public void SaveTheme(IThemeEditorViewModel themeEditorViewModel, string filePath) + public async Task SaveThemeAsync(IThemeEditorViewModel themeEditorViewModel, string filePath) { - var theme = Mapper.Map(themeEditorViewModel); - _themePersistenceService.SaveTheme(theme, filePath); + await Task.Run(() => + { + var theme = Mapper.Map(themeEditorViewModel); + _themePersistenceService.SaveThemeAsync(theme, filePath); + }); } } } diff --git a/Filtration.ThemeEditor/Services/ThemePersistenceService.cs b/Filtration.ThemeEditor/Services/ThemePersistenceService.cs index a2543de..d0a6ce0 100644 --- a/Filtration.ThemeEditor/Services/ThemePersistenceService.cs +++ b/Filtration.ThemeEditor/Services/ThemePersistenceService.cs @@ -1,4 +1,5 @@ using System.IO; +using System.Threading.Tasks; using System.Xml.Serialization; using Filtration.ObjectModel.ThemeEditor; @@ -6,35 +7,42 @@ namespace Filtration.ThemeEditor.Services { internal interface IThemePersistenceService { - Theme LoadTheme(string filePath); - void SaveTheme(Theme theme, string filePath); + Task LoadThemeAsync(string filePath); + Task SaveThemeAsync(Theme theme, string filePath); } internal class ThemePersistenceService : IThemePersistenceService { - public Theme LoadTheme(string filePath) + public async Task LoadThemeAsync(string filePath) { - var xmlSerializer = new XmlSerializer(typeof(Theme)); + Theme loadedTheme = null; - Theme loadedTheme; - - using (Stream reader = new FileStream(filePath, FileMode.Open)) + await Task.Run(() => { - loadedTheme = (Theme)xmlSerializer.Deserialize(reader); - } - - loadedTheme.FilePath = filePath; + var xmlSerializer = new XmlSerializer(typeof (Theme)); + + using (Stream reader = new FileStream(filePath, FileMode.Open)) + { + loadedTheme = (Theme) xmlSerializer.Deserialize(reader); + } + + loadedTheme.FilePath = filePath; + }); + return loadedTheme; } - public void SaveTheme(Theme theme, string filePath) + public async Task SaveThemeAsync(Theme theme, string filePath) { - var xmlSerializer = new XmlSerializer(typeof(Theme)); - - using (Stream writer = new FileStream(filePath, FileMode.Create)) + await Task.Run(() => { - xmlSerializer.Serialize(writer, theme); - } + var xmlSerializer = new XmlSerializer(typeof (Theme)); + + using (Stream writer = new FileStream(filePath, FileMode.Create)) + { + xmlSerializer.Serialize(writer, theme); + } + }); } } } diff --git a/Filtration.ThemeEditor/ViewModels/ThemeEditorViewModel.cs b/Filtration.ThemeEditor/ViewModels/ThemeEditorViewModel.cs index aab12ce..c5115a6 100644 --- a/Filtration.ThemeEditor/ViewModels/ThemeEditorViewModel.cs +++ b/Filtration.ThemeEditor/ViewModels/ThemeEditorViewModel.cs @@ -1,5 +1,6 @@ using System; using System.IO; +using System.Threading.Tasks; using System.Windows; using System.Windows.Forms; using System.Windows.Media; @@ -55,7 +56,7 @@ namespace Filtration.ThemeEditor.ViewModels AddThemeComponentCommand = new RelayCommand(OnAddThemeComponentCommand, t => IsMasterTheme); DeleteThemeComponentCommand = new RelayCommand(OnDeleteThemeComponentCommand, t => IsMasterTheme && SelectedThemeComponent != null); - CloseCommand = new RelayCommand(OnCloseCommand); + CloseCommand = new RelayCommand(async () => await OnCloseCommand()); var icon = new BitmapImage(); icon.BeginInit(); @@ -123,19 +124,19 @@ namespace Filtration.ThemeEditor.ViewModels } } - public void Save() + public async Task SaveAsync() { if (IsMasterTheme) return; if (_filenameIsFake) { - SaveAs(); + await SaveAsAsync(); return; } try { - _themeProvider.SaveTheme(this, FilePath); + await _themeProvider.SaveThemeAsync(this, FilePath); //RemoveDirtyFlag(); } catch (Exception e) @@ -149,7 +150,7 @@ namespace Filtration.ThemeEditor.ViewModels } } - public void SaveAs() + public async Task SaveAsAsync() { if (IsMasterTheme) return; @@ -167,7 +168,7 @@ namespace Filtration.ThemeEditor.ViewModels try { FilePath = saveDialog.FileName; - _themeProvider.SaveTheme(this, FilePath); + await _themeProvider.SaveThemeAsync(this, FilePath); _filenameIsFake = false; Title = Filename; //RemoveDirtyFlag(); @@ -185,14 +186,16 @@ namespace Filtration.ThemeEditor.ViewModels } } - public void OnCloseCommand() + public async Task OnCloseCommand() { - Close(); + await Close(); } - public void Close() +#pragma warning disable 1998 + public async Task Close() +#pragma warning restore 1998 { - Messenger.Default.Send(new ThemeClosedMessage { ClosedViewModel = this }); + Messenger.Default.Send(new ThemeClosedMessage {ClosedViewModel = this}); } private void OnAddThemeComponentCommand(ThemeComponentType themeComponentType) diff --git a/Filtration/App.xaml.cs b/Filtration/App.xaml.cs index 4be2f28..87ea227 100644 --- a/Filtration/App.xaml.cs +++ b/Filtration/App.xaml.cs @@ -17,7 +17,7 @@ using NLog; namespace Filtration { - public partial class App : Application + public partial class App { private IWindsorContainer _container; private static readonly Logger _logger = LogManager.GetCurrentClassLogger(); diff --git a/Filtration/Filtration.csproj b/Filtration/Filtration.csproj index 998baa6..428357b 100644 --- a/Filtration/Filtration.csproj +++ b/Filtration/Filtration.csproj @@ -94,6 +94,10 @@ + + ..\packages\WpfAnimatedGif.1.4.13\lib\net\WpfAnimatedGif.dll + True + False ..\packages\WPFToolkit.3.5.50211.1\lib\WPFToolkit.dll @@ -365,6 +369,7 @@ Settings.settings True + ResXFileCodeGenerator Resources.Designer.cs diff --git a/Filtration/Repositories/ItemFilterScriptRepository.cs b/Filtration/Repositories/ItemFilterScriptRepository.cs index cc0c0c5..e119daa 100644 --- a/Filtration/Repositories/ItemFilterScriptRepository.cs +++ b/Filtration/Repositories/ItemFilterScriptRepository.cs @@ -1,4 +1,5 @@ -using Filtration.ObjectModel; +using System.Threading.Tasks; +using Filtration.ObjectModel; using Filtration.Services; using Filtration.ViewModels; @@ -6,7 +7,7 @@ namespace Filtration.Repositories { internal interface IItemFilterScriptRepository { - IItemFilterScriptViewModel LoadScriptFromFile(string path); + Task LoadScriptFromFileAsync(string path); IItemFilterScriptViewModel NewScript(); string GetItemFilterScriptDirectory(); void SetItemFilterScriptDirectory(string path); @@ -24,9 +25,9 @@ namespace Filtration.Repositories _itemFilterScriptViewModelFactory = itemFilterScriptViewModelFactory; } - public IItemFilterScriptViewModel LoadScriptFromFile(string path) + public async Task LoadScriptFromFileAsync(string path) { - var loadedScript = _itemFilterPersistenceService.LoadItemFilterScript(path); + var loadedScript = await _itemFilterPersistenceService.LoadItemFilterScriptAsync(path); var newViewModel = _itemFilterScriptViewModelFactory.Create(); newViewModel.Initialise(loadedScript, false); diff --git a/Filtration/Resources/loading_spinner.gif b/Filtration/Resources/loading_spinner.gif new file mode 100644 index 0000000000000000000000000000000000000000..dfaad2a6882668243fb5ca173300e498354dcc8a GIT binary patch literal 48552 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

d41+^SUHP=Q}RDD|D(deKU;`>ds( zz0rh(W7deFsvBcn*R()g%BZrT1Fz;wf`HYMJ|W{x$oM-MtW4y4zc3JJ>2B+J8L0XC zgc!BOOz~uyN@J)4CnZqFRFIT5W*+4!99YFBznX5?-RoI$;U%*wVwhc}KP^}mKR18yXS$CJb+y`>^c8Ff# z;6z!g#L4KDI7eV_3d6Q2N2OO1gZUJ9xgfLA`L=`O^@S42b{75L8B(ePle;b6ZO^`b z6izr?w*COkV<7EGY@gwdv<8XY?oNa&VFf0EjqOu;7poNB16_vGd^+J=NiWhqNuYeIcOS%1CO7d zpygMI0?^M_@UHCZ+T-h~4_l-1V-`ZBsnn(dCZgyPDLd7)n;1FW@BV?2{G8L&E%nGH3LBAZj z`n-*FCWTSH4Fd#5K3(#pE05qGLaw!f=$*j4Fld&E&mY#PU}QwAiHnF+DD^VxvcZc6 z_d4Adc4+T4~NL1o#X?uV|%Z7~k`0I}8p z6TxWvvG7-!;SOw2Xn-eN7HZ1|6(>Y^sp~mr1v_|RY$Z{Ive*zo<+ZF3t)W=^%hMhc ztfQorqiLjnV2m9;^hZl%5*cDB=VH$m-I)cw-sUouh10e1cPvNSsN?J;W2+`{C0HG4 zb!`4nu**_xRZCPY8|1#6%f2b}0wo8{I7GK&A?KK2*X{UcNNltTY5)d-wYgyladB8E zg;I3yRD#M_Oe;Swf*ki8A9)*tnXwA^ISoCgeoFgjXIeqx;wYXJ5 z@Q(n0PZ)G}NSziNe<%pg*H^1d5^l=4TO0F5}&DerMIR33-#aZI5Ja;3vj>c~Sg1>INF^VE(2MXu zSST@pmM!ZpHl?^FT~0o0SS7lhE#n$HJjOYL)-1)6Oh}o`^p;PLq0jVu6qm~GysZw) zsL1pq;(P>O_+PFI&0#I-PWo*aOAY&ATxOCpW#3R*5*vZ`7+$Ucy+VARWR}oIncE&j z*&~~YdYow_lNw8(yJ+U~&RpPw(DOBQ!f+5JNyzR?1^*P$wzMLzHVC#a=)MT&UpBYd zFpu~i1bf)-@+Qa=0*6nuW4p|;JvawLA^4#=R2S=P&K|W}mXh9{7ld@LhDQWeJaG&{ zT$n>ITs5pqV7TH~LK%(}G?s_YNR0_}m4zc16x5|=l8Ptu8wG=!By*p=Au^}~QU(c> zC;5c{M?r*Onc(wtp(2h-N>iLA+enlK(N;XW@Rl%~D_i&KNgPZ+hxT^c7HU&+;1b{bWA@m9ae znZBUw3XqT;&s(UsBI{zR=trX%@6_|FKTVY_W2vgke-r$-y&f@V+j&@u45@>+ zgsfRKB*xX7me&h&)_uJZlzP$tj<@2Ts<>5EcQeFKI;RmhSI>P|e?O#7rqfmKP7~se z>4CqIdQ}5dzsc+XSTfthQqhz?*kq*DsO0)wC8zm{h{2;+1M9h_W22{Z7q?ohb9Cm6 zOjnI`pK<=Lj10c)U=#jd2g@a_P%-KH|2o*(4G8f3p$*#H*(oL?Rx@1Qmfb9+|%e z@yj`v;`B9%&w)Hv8}>MJm!Rv=rTTm}ZrK~Geu6e6yXo)w{kd)Fg_Ra}HXX|!!6rMW%CjR-2_NJXhyxaQNO z;<65vg0doklFCZPSz%vBFv7Lc@xvx(M(craMNPTa%+ESMP$xU-HSN!e`uJ+1TZ66C zTvqN!{?W>&P-BZj{rjoDlM zRCQ~`WWVS+-91gKy8www8a!t?d(lhTYE;z^nVOT-=u)xT?z|F^Uj7DAo^w-e$@{D} znaA+k%OQlCWrNaI(frN`mGe*UzSaI*y&-8OD8wN<~#S^w{>0rEj0G zO@goNk@)+jPkOF~^`8w*-_!J&vAXqtphhV5PJ>}%ru|PYs#M0D{$t;Gs-l!+QfO>? zL4j-4tw7(jzn-%flP}?5POQz*GA2X`n2n*qqrT$RJOJjOLH8vRU6FX;N^U?8@Jj&P zGvHf^Dy_dUr{#SKgm6K9&{~~OlJcTQqV>heub?PvrVo{-oidzMq4cNQ*a-SQEAS;m ze{$4VZFKrOpf*GeMxSwpEqx_TtrU2qMcN^ycKws5e=$)^sD_eX!fFaV;qOSW zyW6iaVM|_g)V5mmlFEpF1mv6H0O;0`3WFQUUv&8?>{M1hm5CJ1&y|4?rialr)y2F` z8wNP2Kj

UpF4%eF;8{g`I6#+{THXp_l|z-Uu@? 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%0A_&Om?5x*;y^DG$Ru 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 LoadItemFilterScriptAsync(string filePath); + Task SaveItemFilterScriptAsync(ItemFilterScript script); string DefaultPathOfExileDirectory(); } @@ -58,20 +59,30 @@ namespace Filtration.Services Settings.Default.DefaultFilterDirectory = path; } - public ItemFilterScript LoadItemFilterScript(string filePath) + public async Task LoadItemFilterScriptAsync(string filePath) { - var script = - _itemFilterScriptTranslator.TranslateStringToItemFilterScript( + ItemFilterScript loadedScript = null; + await Task.Run(() => + { + loadedScript = _itemFilterScriptTranslator.TranslateStringToItemFilterScript( _fileSystemService.ReadFileAsString(filePath)); + }); - script.FilePath = filePath; - return script; + if (loadedScript != null) + { + loadedScript.FilePath = filePath; + } + + return loadedScript; } - public void SaveItemFilterScript(ItemFilterScript script) + public async Task SaveItemFilterScriptAsync(ItemFilterScript script) { - _fileSystemService.WriteFileFromString(script.FilePath, - _itemFilterScriptTranslator.TranslateItemFilterScriptToString(script)); + await Task.Run(() => + { + _fileSystemService.WriteFileFromString(script.FilePath, + _itemFilterScriptTranslator.TranslateItemFilterScriptToString(script)); + }); } } } diff --git a/Filtration/Services/UpdateCheckService.cs b/Filtration/Services/UpdateCheckService.cs index 8f01cda..cb0c0ed 100644 --- a/Filtration/Services/UpdateCheckService.cs +++ b/Filtration/Services/UpdateCheckService.cs @@ -7,7 +7,7 @@ namespace Filtration.Services { internal interface IUpdateCheckService { - Task GetUpdateData(); + Task GetUpdateDataAsync(); } internal class UpdateCheckService : IUpdateCheckService @@ -20,7 +20,7 @@ namespace Filtration.Services _httpService = httpService; } - public async Task GetUpdateData() + public async Task GetUpdateDataAsync() { var updateXml = await _httpService.GetContentAsync(UpdateDataUrl); return (DeserializeUpdateData(updateXml)); diff --git a/Filtration/ViewModels/AvalonDockWorkspaceViewModel.cs b/Filtration/ViewModels/AvalonDockWorkspaceViewModel.cs index 7d73e95..ee87215 100644 --- a/Filtration/ViewModels/AvalonDockWorkspaceViewModel.cs +++ b/Filtration/ViewModels/AvalonDockWorkspaceViewModel.cs @@ -39,9 +39,9 @@ namespace Filtration.ViewModels private readonly ReadOnlyObservableCollection _readOnlyOpenDocuments; public AvalonDockWorkspaceViewModel(ISectionBrowserViewModel sectionBrowserViewModel, - IBlockGroupBrowserViewModel blockGroupBrowserViewModel, - IStartPageViewModel startPageViewModel, - IBlockOutputPreviewViewModel blockOutputPreviewViewModel) + IBlockGroupBrowserViewModel blockGroupBrowserViewModel, + IStartPageViewModel startPageViewModel, + IBlockOutputPreviewViewModel blockOutputPreviewViewModel) { _sectionBrowserViewModel = sectionBrowserViewModel; _blockGroupBrowserViewModel = blockGroupBrowserViewModel; @@ -122,7 +122,6 @@ namespace Filtration.ViewModels } private List _tools; - public IEnumerable Tools { diff --git a/Filtration/ViewModels/ItemFilterScriptViewModel.cs b/Filtration/ViewModels/ItemFilterScriptViewModel.cs index 54c1711..92000b2 100644 --- a/Filtration/ViewModels/ItemFilterScriptViewModel.cs +++ b/Filtration/ViewModels/ItemFilterScriptViewModel.cs @@ -4,6 +4,7 @@ using System.Collections.ObjectModel; using System.ComponentModel; using System.IO; using System.Linq; +using System.Threading.Tasks; using System.Windows; using System.Windows.Data; using System.Windows.Forms; @@ -104,7 +105,7 @@ namespace Filtration.ViewModels ToggleShowAdvancedCommand = new RelayCommand(OnToggleShowAdvancedCommand); ClearFilterCommand = new RelayCommand(OnClearFilterCommand, () => BlockFilterPredicate != null); - CloseCommand = new RelayCommand(OnCloseCommand); + CloseCommand = new RelayCommand(async () => await OnCloseCommand()); DeleteBlockCommand = new RelayCommand(OnDeleteBlockCommand, () => SelectedBlockViewModel != null); MoveBlockToTopCommand = new RelayCommand(OnMoveBlockToTopCommand, () => SelectedBlockViewModel != null); MoveBlockUpCommand = new RelayCommand(OnMoveBlockUpCommand, () => SelectedBlockViewModel != null); @@ -351,20 +352,21 @@ namespace Filtration.ViewModels ContentId = "ScriptContentId"; } - public void Save() + public async Task SaveAsync() { if (!ValidateScript()) return; if (!CheckForUnusedThemeComponents()) return; if (_filenameIsFake) { - SaveAs(); + await SaveAsAsync(); return; } + Messenger.Default.Send(new NotificationMessage("ShowLoadingBanner")); try { - _persistenceService.SaveItemFilterScript(Script); + await _persistenceService.SaveItemFilterScriptAsync(Script); RemoveDirtyFlag(); } catch (Exception e) @@ -377,9 +379,15 @@ namespace Filtration.ViewModels _messageBoxService.Show("Save Error", "Error saving filter file - " + e.Message, MessageBoxButton.OK, MessageBoxImage.Error); } + finally + { + Messenger.Default.Send(new NotificationMessage("HideLoadingBanner")); + } + + return; } - public void SaveAs() + public async Task SaveAsAsync() { if (!ValidateScript()) return; if (!CheckForUnusedThemeComponents()) return; @@ -394,12 +402,14 @@ namespace Filtration.ViewModels var result = saveDialog.ShowDialog(); if (result != DialogResult.OK) return; + + Messenger.Default.Send(new NotificationMessage("ShowLoadingBanner")); var previousFilePath = Script.FilePath; try { Script.FilePath = saveDialog.FileName; - _persistenceService.SaveItemFilterScript(Script); + await _persistenceService.SaveItemFilterScriptAsync(Script); _filenameIsFake = false; Title = Filename; RemoveDirtyFlag(); @@ -415,6 +425,10 @@ namespace Filtration.ViewModels MessageBoxImage.Error); Script.FilePath = previousFilePath; } + finally + { + Messenger.Default.Send(new NotificationMessage("HideLoadingBanner")); + } } private bool CheckForUnusedThemeComponents() @@ -480,12 +494,12 @@ namespace Filtration.ViewModels return false; } - private void OnCloseCommand() + private async Task OnCloseCommand() { - Close(); + await Close(); } - public void Close() + public async Task Close() { if (!IsDirty) { @@ -499,20 +513,20 @@ namespace Filtration.ViewModels switch (result) { case MessageBoxResult.Yes: - { - Save(); - CloseScript(); - break; - } + { + await SaveAsync(); + CloseScript(); + break; + } case MessageBoxResult.No: - { - CloseScript(); - break; - } + { + CloseScript(); + break; + } case MessageBoxResult.Cancel: - { - return; - } + { + return; + } } } } @@ -777,6 +791,7 @@ namespace Filtration.ViewModels IsDirty = true; SelectedBlockViewModel = vm; RaisePropertyChanged("ItemFilterSectionViewModels"); + Messenger.Default.Send(new NotificationMessage("SectionsChanged")); } private void OnExpandAllBlocksCommand() @@ -808,9 +823,17 @@ namespace Filtration.ViewModels if (result == MessageBoxResult.Yes) { + var isSection = targetBlockViewModel.Block is ItemFilterSection; + Script.ItemFilterBlocks.Remove(targetBlockViewModel.Block); ItemFilterBlockViewModels.Remove(targetBlockViewModel); IsDirty = true; + + if (isSection) + { + Messenger.Default.Send(new NotificationMessage("SectionsChanged")); + } + } SelectedBlockViewModel = null; } diff --git a/Filtration/ViewModels/MainWindowViewModel.cs b/Filtration/ViewModels/MainWindowViewModel.cs index b2f7648..83ee8a3 100644 --- a/Filtration/ViewModels/MainWindowViewModel.cs +++ b/Filtration/ViewModels/MainWindowViewModel.cs @@ -3,6 +3,7 @@ using System.Diagnostics; using System.IO; using System.Linq; using System.Reflection; +using System.Threading.Tasks; using System.Windows; using System.Windows.Forms; using System.Windows.Media; @@ -34,7 +35,7 @@ namespace Filtration.ViewModels { RelayCommand OpenScriptCommand { get; } RelayCommand NewScriptCommand { get; } - bool CloseAllDocuments(); + Task CloseAllDocuments(); } internal class MainWindowViewModel : FiltrationViewModelBase, IMainWindowViewModel @@ -51,6 +52,7 @@ namespace Filtration.ViewModels private readonly IUpdateCheckService _updateCheckService; private readonly IUpdateAvailableViewModel _updateAvailableViewModel; private readonly IMessageBoxService _messageBoxService; + private bool _showLoadingBanner; public MainWindowViewModel(IItemFilterScriptRepository itemFilterScriptRepository, IItemFilterScriptTranslator itemFilterScriptTranslator, @@ -76,11 +78,11 @@ namespace Filtration.ViewModels NewScriptCommand = new RelayCommand(OnNewScriptCommand); CopyScriptCommand = new RelayCommand(OnCopyScriptCommand, () => ActiveDocumentIsScript); - OpenScriptCommand = new RelayCommand(OnOpenScriptCommand); - OpenThemeCommand = new RelayCommand(OnOpenThemeCommand); + OpenScriptCommand = new RelayCommand(async () => await OnOpenScriptCommand()); + OpenThemeCommand = new RelayCommand(async () => await OnOpenThemeCommand()); - SaveCommand = new RelayCommand(OnSaveDocumentCommand, ActiveDocumentIsEditable); - SaveAsCommand = new RelayCommand(OnSaveAsCommand, ActiveDocumentIsEditable); + SaveCommand = new RelayCommand(async () => await OnSaveDocumentCommand(), ActiveDocumentIsEditable); + SaveAsCommand = new RelayCommand(async () => await OnSaveAsCommand(), ActiveDocumentIsEditable); CloseCommand = new RelayCommand(OnCloseDocumentCommand, ActiveDocumentIsEditable); CopyBlockCommand = new RelayCommand(OnCopyBlockCommand, () => ActiveDocumentIsScript && ActiveScriptHasSelectedBlock); @@ -104,7 +106,7 @@ namespace Filtration.ViewModels ReplaceColorsCommand = new RelayCommand(OnReplaceColorsCommand, () => ActiveDocumentIsScript); CreateThemeCommand = new RelayCommand(OnCreateThemeCommand, () => ActiveDocumentIsScript); - ApplyThemeToScriptCommand = new RelayCommand(OnApplyThemeToScriptCommand, () => ActiveDocumentIsScript); + ApplyThemeToScriptCommand = new RelayCommand(async () => await OnApplyThemeToScriptCommand(), () => ActiveDocumentIsScript); EditMasterThemeCommand = new RelayCommand(OnEditMasterThemeCommand, () => ActiveDocumentIsScript); AddTextColorThemeComponentCommand = new RelayCommand(OnAddTextColorThemeComponentCommand, () => ActiveDocumentIsTheme && ActiveThemeIsEditable); @@ -166,7 +168,19 @@ namespace Filtration.ViewModels } case "OpenScript": { +#pragma warning disable 4014 OnOpenScriptCommand(); +#pragma warning restore 4014 + break; + } + case "ShowLoadingBanner": + { + ShowLoadingBanner = true; + break; + } + case "HideLoadingBanner": + { + ShowLoadingBanner = false; break; } } @@ -221,7 +235,7 @@ namespace Filtration.ViewModels try { - var result = await _updateCheckService.GetUpdateData(); + var result = await _updateCheckService.GetUpdateDataAsync(); if (result.LatestVersionMajorPart >= assemblyVersion.FileMajorPart && result.LatestVersionMinorPart > assemblyVersion.FileMinorPart) @@ -279,6 +293,16 @@ namespace Filtration.ViewModels } } + public bool ShowLoadingBanner + { + get { return _showLoadingBanner; } + private set + { + _showLoadingBanner = value; + RaisePropertyChanged(); + } + } + public bool ActiveDocumentIsScript { get { return _avalonDockWorkspaceViewModel.ActiveDocument != null && _avalonDockWorkspaceViewModel.ActiveDocument.IsScript; } @@ -362,7 +386,8 @@ namespace Filtration.ViewModels var aboutWindow = new AboutWindow(); aboutWindow.ShowDialog(); } - private void OnOpenScriptCommand() + + private async Task OnOpenScriptCommand() { var openFileDialog = new OpenFileDialog { @@ -374,12 +399,14 @@ namespace Filtration.ViewModels IItemFilterScriptViewModel loadedViewModel; + Messenger.Default.Send(new NotificationMessage("ShowLoadingBanner")); try { - loadedViewModel = _itemFilterScriptRepository.LoadScriptFromFile(openFileDialog.FileName); + loadedViewModel = await _itemFilterScriptRepository.LoadScriptFromFileAsync(openFileDialog.FileName); } catch(IOException e) { + Messenger.Default.Send(new NotificationMessage("HideLoadingBanner")); if (_logger.IsErrorEnabled) { _logger.Error(e); @@ -390,10 +417,11 @@ namespace Filtration.ViewModels return; } + Messenger.Default.Send(new NotificationMessage("HideLoadingBanner")); _avalonDockWorkspaceViewModel.AddDocument(loadedViewModel); } - private void OnOpenThemeCommand() + private async Task OnOpenThemeCommand() { var filePath = ShowOpenThemeDialog(); @@ -406,7 +434,7 @@ namespace Filtration.ViewModels try { - loadedViewModel = _themeProvider.LoadThemeFromFile(filePath); + loadedViewModel = await _themeProvider.LoadThemeFromFile(filePath); } catch (IOException e) { @@ -423,7 +451,7 @@ namespace Filtration.ViewModels _avalonDockWorkspaceViewModel.AddDocument(loadedViewModel); } - private void OnApplyThemeToScriptCommand() + private async Task OnApplyThemeToScriptCommand() { var filePath = ShowOpenThemeDialog(); if (string.IsNullOrEmpty(filePath)) @@ -435,7 +463,7 @@ namespace Filtration.ViewModels try { - loadedTheme = _themeProvider.LoadThemeModelFromFile(filePath); + loadedTheme = await _themeProvider.LoadThemeModelFromFile(filePath); } catch (IOException e) { @@ -490,14 +518,14 @@ namespace Filtration.ViewModels } } - private void OnSaveDocumentCommand() + private async Task OnSaveDocumentCommand() { - ((IEditableDocument)_avalonDockWorkspaceViewModel.ActiveDocument).Save(); + await ((IEditableDocument)_avalonDockWorkspaceViewModel.ActiveDocument).SaveAsync(); } - private void OnSaveAsCommand() + private async Task OnSaveAsCommand() { - ((IEditableDocument)_avalonDockWorkspaceViewModel.ActiveDocument).SaveAs(); + await ((IEditableDocument)_avalonDockWorkspaceViewModel.ActiveDocument).SaveAsAsync(); } private void OnReplaceColorsCommand() @@ -629,14 +657,14 @@ namespace Filtration.ViewModels _avalonDockWorkspaceViewModel.ActiveThemeViewModel.SelectedThemeComponent); } - public bool CloseAllDocuments() + public async Task CloseAllDocuments() { var openDocuments = _avalonDockWorkspaceViewModel.OpenDocuments.OfType().ToList(); foreach (var document in openDocuments) { var docCount = _avalonDockWorkspaceViewModel.OpenDocuments.OfType().Count(); - document.Close(); + await document.Close(); if (_avalonDockWorkspaceViewModel.OpenDocuments.OfType().Count() == docCount) { return false; diff --git a/Filtration/ViewModels/StartPageViewModel.cs b/Filtration/ViewModels/StartPageViewModel.cs index b5d97df..e66e2fd 100644 --- a/Filtration/ViewModels/StartPageViewModel.cs +++ b/Filtration/ViewModels/StartPageViewModel.cs @@ -1,4 +1,5 @@ -using Filtration.Common.ViewModels; +using System.Threading.Tasks; +using Filtration.Common.ViewModels; using Filtration.Interface; using GalaSoft.MvvmLight.CommandWpf; using GalaSoft.MvvmLight.Messaging; @@ -25,7 +26,7 @@ namespace Filtration.ViewModels public bool IsScript { get { return false; } } public bool IsTheme { get { return false; } } - public void Close() + public Task Close() { throw new System.NotImplementedException(); } diff --git a/Filtration/ViewModels/ToolPanes/SectionBrowserViewModel.cs b/Filtration/ViewModels/ToolPanes/SectionBrowserViewModel.cs index 894dc5b..c411a3f 100644 --- a/Filtration/ViewModels/ToolPanes/SectionBrowserViewModel.cs +++ b/Filtration/ViewModels/ToolPanes/SectionBrowserViewModel.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Windows.Media.Imaging; +using GalaSoft.MvvmLight.Messaging; namespace Filtration.ViewModels.ToolPanes { @@ -23,6 +24,18 @@ namespace Filtration.ViewModels.ToolPanes icon.UriSource = new Uri("pack://application:,,,/Filtration;component/Resources/Icons/add_section_icon.png"); icon.EndInit(); IconSource = icon; + + Messenger.Default.Register(this, message => + { + switch (message.Notification) + { + case "SectionsChanged": + { + OnActiveDocumentChanged(this, EventArgs.Empty); + break; + } + } + }); } public const string ToolContentId = "SectionBrowserTool"; diff --git a/Filtration/Views/AvalonDock/AvalonDockWorkspaceView.xaml b/Filtration/Views/AvalonDock/AvalonDockWorkspaceView.xaml index 932a7a1..6f78e2c 100644 --- a/Filtration/Views/AvalonDock/AvalonDockWorkspaceView.xaml +++ b/Filtration/Views/AvalonDock/AvalonDockWorkspaceView.xaml @@ -10,9 +10,10 @@ xmlns:converters="clr-namespace:Filtration.Converters" xmlns:xcad="http://schemas.xceed.com/wpf/xaml/avalondock" xmlns:themeEditorViews="clr-namespace:Filtration.ThemeEditor.Views;assembly=Filtration.ThemeEditor" + xmlns:gif="http://wpfanimatedgif.codeplex.com" mc:Ignorable="d" d:DataContext="{d:DesignInstance d:Type=viewModels:AvalonDockWorkspaceViewModel}" - d:DesignHeight="300" d:DesignWidth="300"> + d:DesignHeight="300" d:DesignWidth="700"> diff --git a/Filtration/Views/MainWindow.xaml b/Filtration/Views/MainWindow.xaml index fbbb3f7..43b3e2d 100644 --- a/Filtration/Views/MainWindow.xaml +++ b/Filtration/Views/MainWindow.xaml @@ -8,6 +8,7 @@ xmlns:viewModels="clr-namespace:Filtration.ViewModels" xmlns:viewsAvalonDock="clr-namespace:Filtration.Views.AvalonDock" xmlns:views="clr-namespace:Filtration.Views" + xmlns:gif="http://wpfanimatedgif.codeplex.com" mc:Ignorable="d" d:DataContext="{d:DesignInstance Type=viewModels:MainWindowViewModel}" Title="{Binding WindowTitle}" Height="762" Width="1126" IsIconVisible="True" @@ -18,7 +19,7 @@ - + @@ -118,6 +119,30 @@ + + + + + + + + + + + + + + + + + + + Working... + + + + + diff --git a/Filtration/Views/MainWindow.xaml.cs b/Filtration/Views/MainWindow.xaml.cs index 0109b31..414ab6b 100644 --- a/Filtration/Views/MainWindow.xaml.cs +++ b/Filtration/Views/MainWindow.xaml.cs @@ -1,7 +1,6 @@ -using System; -using System.ComponentModel; +using System.ComponentModel; +using System.Threading.Tasks; using System.Windows; -using Filtration.Annotations; using Filtration.ViewModels; namespace Filtration.Views @@ -13,7 +12,7 @@ namespace Filtration.Views internal partial class MainWindow : IMainWindow { - private IMainWindowViewModel _mainWindowViewModel; + private readonly IMainWindowViewModel _mainWindowViewModel; public MainWindow(IMainWindowViewModel mainWindowViewModel) { @@ -40,11 +39,11 @@ namespace Filtration.Views private void MainWindow_OnClosing(object sender, CancelEventArgs e) { - var allDocumentsClosed = _mainWindowViewModel.CloseAllDocuments(); - + var allDocumentsClosed = _mainWindowViewModel.CloseAllDocuments().Result; if (!allDocumentsClosed) { e.Cancel = true; + } } } diff --git a/Filtration/packages.config b/Filtration/packages.config index 38a0c67..d897cf0 100644 --- a/Filtration/packages.config +++ b/Filtration/packages.config @@ -11,5 +11,6 @@ + \ No newline at end of file