Create entries and groups put them in Edit mode (as before)

Search now uses KeePassLib search
Code cleanup
This commit is contained in:
Geoffroy BONNEVILLE
2020-04-15 12:02:30 +02:00
parent b66e79f97c
commit 0063ef1977
14 changed files with 112 additions and 58 deletions

View File

@@ -93,6 +93,7 @@
<Compile Include="Group\Commands\DeleteEntry\DeleteEntryCommand.cs" /> <Compile Include="Group\Commands\DeleteEntry\DeleteEntryCommand.cs" />
<Compile Include="Group\Commands\DeleteGroup\DeleteGroupCommand.cs" /> <Compile Include="Group\Commands\DeleteGroup\DeleteGroupCommand.cs" />
<Compile Include="Group\Queries\GetGroup\GetGroupQuery.cs" /> <Compile Include="Group\Queries\GetGroup\GetGroupQuery.cs" />
<Compile Include="Group\Queries\SearchEntries\SearchEntriesQuery.cs" />
<Compile Include="Parameters\Commands\SetCipher\SetCipherCommand.cs" /> <Compile Include="Parameters\Commands\SetCipher\SetCipherCommand.cs" />
<Compile Include="Parameters\Commands\SetCompression\SetCompressionCommand.cs" /> <Compile Include="Parameters\Commands\SetCompression\SetCompressionCommand.cs" />
<Compile Include="Parameters\Commands\SetHasRecycleBin\SetHasRecycleBinCommand.cs" /> <Compile Include="Parameters\Commands\SetHasRecycleBin\SetHasRecycleBinCommand.cs" />

View File

@@ -1,4 +1,5 @@
using System.Threading.Tasks; using System.Collections.Generic;
using System.Threading.Tasks;
using ModernKeePass.Domain.Dtos; using ModernKeePass.Domain.Dtos;
using ModernKeePass.Domain.Entities; using ModernKeePass.Domain.Entities;
using ModernKeePass.Domain.Enums; using ModernKeePass.Domain.Enums;
@@ -28,6 +29,8 @@ namespace ModernKeePass.Application.Common.Interfaces
void UpdateCredentials(Credentials credentials); void UpdateCredentials(Credentials credentials);
void CloseDatabase(); void CloseDatabase();
EntryEntity GetEntry(string id);
GroupEntity GetGroup(string id);
Task AddEntry(string parentGroupId, string entryId); Task AddEntry(string parentGroupId, string entryId);
Task MoveEntry(string parentGroupId, string entryId, int index); Task MoveEntry(string parentGroupId, string entryId, int index);
Task AddGroup(string parentGroupId, string groupId); Task AddGroup(string parentGroupId, string groupId);
@@ -40,7 +43,7 @@ namespace ModernKeePass.Application.Common.Interfaces
GroupEntity CreateGroup(string parentGroupId, string name, bool isRecycleBin = false); GroupEntity CreateGroup(string parentGroupId, string name, bool isRecycleBin = false);
void SortEntries(string groupId); void SortEntries(string groupId);
void SortSubGroups(string groupId); void SortSubGroups(string groupId);
EntryEntity GetEntry(string id);
GroupEntity GetGroup(string id); IEnumerable<EntryEntity> Search(string groupId, string text);
} }
} }

View File

@@ -0,0 +1,34 @@
using System.Collections.Generic;
using System.Linq;
using AutoMapper;
using MediatR;
using ModernKeePass.Application.Common.Interfaces;
using ModernKeePass.Application.Entry.Models;
using ModernKeePass.Domain.Exceptions;
namespace ModernKeePass.Application.Group.Queries.SearchEntries
{
public class SearchEntriesQuery : IRequest<IEnumerable<EntryVm>>
{
public string GroupId { get; set; }
public string SearchText { get; set; }
public class SearchEntriesQueryHandler : IRequestHandler<SearchEntriesQuery, IEnumerable<EntryVm>>
{
private readonly IDatabaseProxy _database;
private readonly IMapper _mapper;
public SearchEntriesQueryHandler(IDatabaseProxy database, IMapper mapper)
{
_database = database;
_mapper = mapper;
}
public IEnumerable<EntryVm> Handle(SearchEntriesQuery message)
{
if (!_database.IsOpen) throw new DatabaseClosedException();
return _database.Search(message.GroupId, message.SearchText).Select(e => _mapper.Map<EntryVm>(e));
}
}
}
}

View File

@@ -1,4 +1,5 @@
using System; using System;
using System.Collections.Generic;
using System.Drawing; using System.Drawing;
using System.Linq; using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
@@ -9,6 +10,7 @@ using ModernKeePass.Domain.Entities;
using ModernKeePass.Domain.Enums; using ModernKeePass.Domain.Enums;
using ModernKeePass.Domain.Interfaces; using ModernKeePass.Domain.Interfaces;
using ModernKeePassLib; using ModernKeePassLib;
using ModernKeePassLib.Collections;
using ModernKeePassLib.Cryptography.KeyDerivation; using ModernKeePassLib.Cryptography.KeyDerivation;
using ModernKeePassLib.Interfaces; using ModernKeePassLib.Interfaces;
using ModernKeePassLib.Keys; using ModernKeePassLib.Keys;
@@ -311,6 +313,21 @@ namespace ModernKeePass.Infrastructure.KeePass
_pwDatabase.MasterKey = CreateCompositeKey(credentials); _pwDatabase.MasterKey = CreateCompositeKey(credentials);
} }
public IEnumerable<EntryEntity> Search(string groupId, string text)
{
var pwGroup = _pwDatabase.RootGroup.FindGroup(BuildIdFromString(groupId), true);
var searchResults = new PwObjectList<PwEntry>();
pwGroup.SearchEntries(new SearchParameters
{
ComparisonMode = StringComparison.OrdinalIgnoreCase,
SearchInTitles = true,
//SearchInUserNames = true,
SearchString = text
}, searchResults);
return searchResults.Select(e => _mapper.Map<EntryEntity>(e));
}
private CompositeKey CreateCompositeKey(Credentials credentials) private CompositeKey CreateCompositeKey(Credentials credentials)
{ {
var compositeKey = new CompositeKey(); var compositeKey = new CompositeKey();

View File

@@ -58,7 +58,7 @@ namespace ModernKeePass.Actions
ToastNotificationHelper.ShowMovedToast(Entity, resource.GetResourceValue("EntityDeleting"), text); ToastNotificationHelper.ShowMovedToast(Entity, resource.GetResourceValue("EntityDeleting"), text);
Entity.MarkForDelete(resource.GetResourceValue("RecycleBinTitle")); Entity.MarkForDelete(resource.GetResourceValue("RecycleBinTitle"));
Command.Execute(null); Command.Execute(null);
}, null).GetAwaiter().GetResult(); }, null).Wait();
return null; return null;
} }

View File

@@ -15,7 +15,7 @@ namespace ModernKeePass.Common
messageDialog.Commands.Add(new UICommand(actionButtonText, actionCommand)); messageDialog.Commands.Add(new UICommand(actionButtonText, actionCommand));
// Show the message dialog // Show the message dialog
await messageDialog.ShowAsync().AsTask(); await messageDialog.ShowAsync().AsTask().ConfigureAwait(false);
} }
public static async Task ShowErrorDialog(Exception exception) public static async Task ShowErrorDialog(Exception exception)
@@ -25,7 +25,7 @@ namespace ModernKeePass.Common
var messageDialog = CreateBasicDialog(exception.Message, exception.StackTrace, "OK"); var messageDialog = CreateBasicDialog(exception.Message, exception.StackTrace, "OK");
// Show the message dialog // Show the message dialog
await messageDialog.ShowAsync().AsTask();; await messageDialog.ShowAsync().AsTask().ConfigureAwait(false);
} }
public static async Task ShowNotificationDialog(string title, string message) public static async Task ShowNotificationDialog(string title, string message)
@@ -33,7 +33,7 @@ namespace ModernKeePass.Common
var dialog = CreateBasicDialog(title, message, "OK"); var dialog = CreateBasicDialog(title, message, "OK");
// Show the message dialog // Show the message dialog
await dialog.ShowAsync().AsTask();; await dialog.ShowAsync().AsTask().ConfigureAwait(false);
} }
private static MessageDialog CreateBasicDialog(string title, string message, string dismissActionText, UICommandInvokedHandler cancelCommand = null) private static MessageDialog CreateBasicDialog(string title, string message, string dismissActionText, UICommandInvokedHandler cancelCommand = null)

View File

@@ -0,0 +1,8 @@
namespace ModernKeePass.Models
{
public class NavigationItem
{
public string Id { get; set; }
public bool IsNew { get; set; }
}
}

View File

@@ -181,12 +181,6 @@ namespace ModernKeePass.ViewModels
set { SetProperty(ref _isEditMode, value); } set { SetProperty(ref _isEditMode, value); }
} }
public bool IsVisible
{
get { return _isVisible; }
set { SetProperty(ref _isVisible, value); }
}
public bool IsRevealPassword public bool IsRevealPassword
{ {
get { return _isRevealPassword; } get { return _isRevealPassword; }
@@ -205,21 +199,18 @@ namespace ModernKeePass.ViewModels
private bool _isEditMode; private bool _isEditMode;
private bool _isRevealPassword; private bool _isRevealPassword;
private double _passwordLength = 25; private double _passwordLength = 25;
private bool _isVisible = true;
private bool _isSelected; private bool _isSelected;
public EntryDetailVm() { } public EntryDetailVm() { }
internal EntryDetailVm(string entryId, bool isNewEntry = false) : this(entryId, App.Services.GetRequiredService<IMediator>(), isNewEntry) { } internal EntryDetailVm(string entryId) : this(entryId, App.Services.GetRequiredService<IMediator>()) { }
public EntryDetailVm(string entryId, IMediator mediator, bool isNewEntry = false) public EntryDetailVm(string entryId, IMediator mediator)
{ {
_mediator = mediator; _mediator = mediator;
_entry = _mediator.Send(new GetEntryQuery {Id = entryId}).GetAwaiter().GetResult(); _entry = _mediator.Send(new GetEntryQuery {Id = entryId}).GetAwaiter().GetResult();
_parent = _mediator.Send(new GetGroupQuery {Id = _entry.ParentGroupId}).GetAwaiter().GetResult(); _parent = _mediator.Send(new GetGroupQuery {Id = _entry.ParentGroupId}).GetAwaiter().GetResult();
History = _entry.History; History = _entry.History;
_isEditMode = isNewEntry;
if (isNewEntry) GeneratePassword().GetAwaiter().GetResult();
IsSelected = true; IsSelected = true;
SaveCommand = new RelayCommand(async () => await SaveChanges(), () => Database.IsDirty); SaveCommand = new RelayCommand(async () => await SaveChanges(), () => Database.IsDirty);

View File

@@ -23,6 +23,7 @@ using ModernKeePass.Application.Group.Commands.SortEntries;
using ModernKeePass.Application.Group.Commands.SortGroups; using ModernKeePass.Application.Group.Commands.SortGroups;
using ModernKeePass.Application.Group.Models; using ModernKeePass.Application.Group.Models;
using ModernKeePass.Application.Group.Queries.GetGroup; using ModernKeePass.Application.Group.Queries.GetGroup;
using ModernKeePass.Application.Group.Queries.SearchEntries;
using ModernKeePass.Common; using ModernKeePass.Common;
using ModernKeePass.Domain.AOP; using ModernKeePass.Domain.AOP;
using ModernKeePass.Domain.Enums; using ModernKeePass.Domain.Enums;
@@ -35,22 +36,7 @@ namespace ModernKeePass.ViewModels
public ObservableCollection<EntryVm> Entries { get; } public ObservableCollection<EntryVm> Entries { get; }
public ObservableCollection<GroupVm> Groups { get; } public ObservableCollection<GroupVm> Groups { get; }
public IEnumerable<EntryVm> SubEntries
{
get
{
var subEntries = new List<EntryVm>();
subEntries.AddRange(Entries);
foreach (var group in Groups)
{
subEntries.AddRange(group.Entries);
}
return subEntries;
}
}
public bool IsNotRoot => Database.RootGroupId != _group.Id; public bool IsNotRoot => Database.RootGroupId != _group.Id;
public IOrderedEnumerable<IGrouping<char, EntryVm>> EntriesZoomedOut => from e in Entries public IOrderedEnumerable<IGrouping<char, EntryVm>> EntriesZoomedOut => from e in Entries
@@ -83,12 +69,6 @@ namespace ModernKeePass.ViewModels
} }
} }
public bool IsMenuClosed
{
get { return _isMenuClosed; }
set { SetProperty(ref _isMenuClosed, value); }
}
public IEnumerable<GroupVm> BreadCrumb { get; } public IEnumerable<GroupVm> BreadCrumb { get; }
public ICommand SaveCommand { get; } public ICommand SaveCommand { get; }
@@ -103,14 +83,13 @@ namespace ModernKeePass.ViewModels
private readonly GroupVm _parent; private readonly GroupVm _parent;
private bool _isEditMode; private bool _isEditMode;
private EntryVm _reorderedEntry; private EntryVm _reorderedEntry;
private bool _isMenuClosed = true;
public GroupDetailVm() {} public GroupDetailVm() {}
internal GroupDetailVm(string groupId) : this(groupId, App.Services.GetRequiredService<IMediator>()) internal GroupDetailVm(string groupId) : this(groupId, App.Services.GetRequiredService<IMediator>())
{ } { }
public GroupDetailVm(string groupId, IMediator mediator, bool isEditMode = false) public GroupDetailVm(string groupId, IMediator mediator)
{ {
_mediator = mediator; _mediator = mediator;
_group = _mediator.Send(new GetGroupQuery { Id = groupId }).GetAwaiter().GetResult(); _group = _mediator.Send(new GetGroupQuery { Id = groupId }).GetAwaiter().GetResult();
@@ -119,7 +98,6 @@ namespace ModernKeePass.ViewModels
_parent = _mediator.Send(new GetGroupQuery { Id = _group.ParentGroupId }).GetAwaiter().GetResult(); _parent = _mediator.Send(new GetGroupQuery { Id = _group.ParentGroupId }).GetAwaiter().GetResult();
BreadCrumb = new List<GroupVm> {_parent}; BreadCrumb = new List<GroupVm> {_parent};
} }
_isEditMode = isEditMode;
SaveCommand = new RelayCommand(async () => await SaveChanges(), () => Database.IsDirty); SaveCommand = new RelayCommand(async () => await SaveChanges(), () => Database.IsDirty);
SortEntriesCommand = new RelayCommand(async () => await SortEntriesAsync(), () => IsEditMode); SortEntriesCommand = new RelayCommand(async () => await SortEntriesAsync(), () => IsEditMode);
@@ -152,6 +130,11 @@ namespace ModernKeePass.ViewModels
await _mediator.Send(new RemoveGroupCommand {ParentGroup = _parent, Group = _group }); await _mediator.Send(new RemoveGroupCommand {ParentGroup = _parent, Group = _group });
} }
public async Task<IEnumerable<EntryVm>> Search(string queryText)
{
return await _mediator.Send(new SearchEntriesQuery {GroupId = Id, SearchText = queryText});
}
private async Task SaveChanges() private async Task SaveChanges()
{ {
await _mediator.Send(new SaveDatabaseCommand()); await _mediator.Send(new SaveDatabaseCommand());

View File

@@ -10,6 +10,7 @@ using ModernKeePass.Common;
using ModernKeePass.Domain.AOP; using ModernKeePass.Domain.AOP;
using ModernKeePass.Domain.Dtos; using ModernKeePass.Domain.Dtos;
using ModernKeePass.Domain.Interfaces; using ModernKeePass.Domain.Interfaces;
using ModernKeePass.Models;
using ModernKeePass.Views; using ModernKeePass.Views;
namespace ModernKeePass.ViewModels namespace ModernKeePass.ViewModels
@@ -128,7 +129,7 @@ namespace ModernKeePass.ViewModels
Title = database.Name, Title = database.Name,
PageType = typeof(GroupDetailPage), PageType = typeof(GroupDetailPage),
Destination = referenceFrame, Destination = referenceFrame,
Parameter = database.RootGroupId, Parameter = new NavigationItem { Id = database.RootGroupId },
Group = "Databases", Group = "Databases",
SymbolIcon = Symbol.ProtectedDocument SymbolIcon = Symbol.ProtectedDocument
}); });

View File

@@ -2,6 +2,7 @@
using Windows.UI.Xaml.Controls; using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Navigation; using Windows.UI.Xaml.Navigation;
using ModernKeePass.Common; using ModernKeePass.Common;
using ModernKeePass.Models;
using ModernKeePass.ViewModels; using ModernKeePass.ViewModels;
// Pour en savoir plus sur le modèle d'élément Page Détail de l'élément, consultez la page http://go.microsoft.com/fwlink/?LinkId=234232 // Pour en savoir plus sur le modèle d'élément Page Détail de l'élément, consultez la page http://go.microsoft.com/fwlink/?LinkId=234232
@@ -39,11 +40,15 @@ namespace ModernKeePass.Views
/// Le paramètre de navigation est disponible dans la méthode LoadState /// Le paramètre de navigation est disponible dans la méthode LoadState
/// en plus de l'état de page conservé durant une session antérieure. /// en plus de l'état de page conservé durant une session antérieure.
protected override void OnNavigatedTo(NavigationEventArgs e) protected override async void OnNavigatedTo(NavigationEventArgs e)
{ {
NavigationHelper.OnNavigatedTo(e); NavigationHelper.OnNavigatedTo(e);
var args = e.Parameter as string; var args = e.Parameter as NavigationItem;
if (args != null) DataContext = new EntryDetailVm(args); if (args != null)
{
DataContext = new EntryDetailVm(args.Id) { IsEditMode = args.IsNew };
await Model.GeneratePassword();
}
} }
protected override async void OnNavigatedFrom(NavigationEventArgs e) protected override async void OnNavigatedFrom(NavigationEventArgs e)

View File

@@ -48,7 +48,7 @@
<ColumnDefinition Width="{StaticResource ExpandedMenuGridLength}" x:Name="LeftListViewColumn" /> <ColumnDefinition Width="{StaticResource ExpandedMenuGridLength}" x:Name="LeftListViewColumn" />
<ColumnDefinition Width="*" /> <ColumnDefinition Width="*" />
</Grid.ColumnDefinitions> </Grid.ColumnDefinitions>
<userControls:HamburgerMenuUserControl x:Uid="GroupsLeftListView" ItemsSource="{Binding Groups}" SelectionChanged="groups_SelectionChanged" ButtonClicked="CreateGroup_ButtonClick" ResizeTarget="{Binding ElementName=LeftListViewColumn}" IsOpen="True" /> <userControls:HamburgerMenuUserControl x:Uid="GroupsLeftListView" ItemsSource="{Binding Groups}" SelectionChanged="groups_SelectionChanged" ButtonClicked="CreateGroup_ButtonClick" ResizeTarget="{Binding ElementName=LeftListViewColumn}" IsOpen="True" IsButtonVisible="Visible" />
<Grid Grid.Column="1"> <Grid Grid.Column="1">
<Grid.ColumnDefinitions> <Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" /> <ColumnDefinition Width="Auto" />

View File

@@ -7,6 +7,7 @@ using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Navigation; using Windows.UI.Xaml.Navigation;
using ModernKeePass.Common; using ModernKeePass.Common;
using ModernKeePass.Events; using ModernKeePass.Events;
using ModernKeePass.Models;
using ModernKeePass.ViewModels; using ModernKeePass.ViewModels;
using EntryVm = ModernKeePass.Application.Entry.Models.EntryVm; using EntryVm = ModernKeePass.Application.Entry.Models.EntryVm;
@@ -53,9 +54,9 @@ namespace ModernKeePass.Views
DataContext = new GroupDetailVm(args.RootGroupId); DataContext = new GroupDetailVm(args.RootGroupId);
else else
{ {
var id = e.Parameter as string; var navigationItem = e.Parameter as NavigationItem;
if (id != null) if (navigationItem != null)
DataContext = new GroupDetailVm(id); DataContext = new GroupDetailVm(navigationItem.Id) { IsEditMode = navigationItem.IsNew };
} }
} }
@@ -77,7 +78,7 @@ namespace ModernKeePass.Views
return; return;
default: default:
var group = listView?.SelectedItem as Application.Group.Models.GroupVm; var group = listView?.SelectedItem as Application.Group.Models.GroupVm;
Frame.Navigate(typeof(GroupDetailPage), group?.Id); Frame.Navigate(typeof(GroupDetailPage), new NavigationItem { Id = group?.Id });
break; break;
} }
} }
@@ -90,7 +91,7 @@ namespace ModernKeePass.Views
return; return;
default: default:
var entry = GridView.SelectedItem as EntryVm; var entry = GridView.SelectedItem as EntryVm;
Frame.Navigate(typeof(EntryDetailPage), entry?.Id); Frame.Navigate(typeof(EntryDetailPage), new NavigationItem { Id = entry?.Id });
break; break;
} }
} }
@@ -105,11 +106,19 @@ namespace ModernKeePass.Views
} }
private async void CreateEntry_ButtonClick(object sender, RoutedEventArgs e) private async void CreateEntry_ButtonClick(object sender, RoutedEventArgs e)
{ {
Frame.Navigate(typeof(EntryDetailPage), await Model.AddNewEntry()); Frame.Navigate(typeof(EntryDetailPage), new NavigationItem
{
Id = await Model.AddNewEntry(),
IsNew = true
});
} }
private async void CreateGroup_ButtonClick(object sender, RoutedEventArgs e) private async void CreateGroup_ButtonClick(object sender, RoutedEventArgs e)
{ {
Frame.Navigate(typeof(GroupDetailPage), await Model.AddNewGroup()); Frame.Navigate(typeof(GroupDetailPage), new NavigationItem
{
Id = await Model.AddNewGroup(),
IsNew = true
});
} }
private void GridView_DragItemsStarting(object sender, DragItemsStartingEventArgs e) private void GridView_DragItemsStarting(object sender, DragItemsStartingEventArgs e)
@@ -118,10 +127,10 @@ namespace ModernKeePass.Views
e.Data.RequestedOperation = DataPackageOperation.Move; e.Data.RequestedOperation = DataPackageOperation.Move;
} }
private void SearchBox_OnSuggestionsRequested(SearchBox sender, SearchBoxSuggestionsRequestedEventArgs args) private async void SearchBox_OnSuggestionsRequested(SearchBox sender, SearchBoxSuggestionsRequestedEventArgs args)
{ {
var imageUri = RandomAccessStreamReference.CreateFromUri(new Uri("ms-appdata://Assets/ModernKeePass-SmallLogo.scale-80.png")); var imageUri = RandomAccessStreamReference.CreateFromUri(new Uri("ms-appdata://Assets/ModernKeePass-SmallLogo.scale-80.png"));
var results = Model.SubEntries.Where(e => e.Title.IndexOf(args.QueryText, StringComparison.OrdinalIgnoreCase) >= 0).Take(5); var results = (await Model.Search(args.QueryText)).Take(5);
foreach (var result in results) foreach (var result in results)
{ {
args.Request.SearchSuggestionCollection.AppendResultSuggestion(result.Title, result.ParentGroupName, result.Id, imageUri, string.Empty); args.Request.SearchSuggestionCollection.AppendResultSuggestion(result.Title, result.ParentGroupName, result.Id, imageUri, string.Empty);
@@ -130,7 +139,7 @@ namespace ModernKeePass.Views
private void SearchBox_OnResultSuggestionChosen(SearchBox sender, SearchBoxResultSuggestionChosenEventArgs args) private void SearchBox_OnResultSuggestionChosen(SearchBox sender, SearchBoxResultSuggestionChosenEventArgs args)
{ {
Frame.Navigate(typeof(EntryDetailPage), args.Tag); Frame.Navigate(typeof(EntryDetailPage), new NavigationItem { Id = args.Tag });
} }
private void GroupDetailPage_OnSizeChanged(object sender, SizeChangedEventArgs e) private void GroupDetailPage_OnSizeChanged(object sender, SizeChangedEventArgs e)

View File

@@ -102,6 +102,7 @@
<Compile Include="Converters\IconToSymbolConverter.cs" /> <Compile Include="Converters\IconToSymbolConverter.cs" />
<Compile Include="DependencyInjection.cs" /> <Compile Include="DependencyInjection.cs" />
<Compile Include="Extensions\ColorExtensions.cs" /> <Compile Include="Extensions\ColorExtensions.cs" />
<Compile Include="Models\NavigationItem.cs" />
<Compile Include="TemplateSelectors\SelectableDataTemplateSelector.cs" /> <Compile Include="TemplateSelectors\SelectableDataTemplateSelector.cs" />
<Compile Include="ViewModels\Items\SettingsSaveVm.cs" /> <Compile Include="ViewModels\Items\SettingsSaveVm.cs" />
<Compile Include="Views\MainPageFrames\DonatePage.xaml.cs"> <Compile Include="Views\MainPageFrames\DonatePage.xaml.cs">
@@ -556,6 +557,7 @@
<Name>Infrastructure</Name> <Name>Infrastructure</Name>
</ProjectReference> </ProjectReference>
</ItemGroup> </ItemGroup>
<ItemGroup />
<PropertyGroup Condition=" '$(VisualStudioVersion)' == '' or '$(VisualStudioVersion)' &lt; '12.0' "> <PropertyGroup Condition=" '$(VisualStudioVersion)' == '' or '$(VisualStudioVersion)' &lt; '12.0' ">
<VisualStudioVersion>12.0</VisualStudioVersion> <VisualStudioVersion>12.0</VisualStudioVersion>
</PropertyGroup> </PropertyGroup>