10 Commits
V1.19 ... V1.20

Author SHA1 Message Date
Geoffroy BONNEVILLE
80a255aa6f Restored stackpanel in breadcrumb 2020-06-26 15:21:06 +02:00
Geoffroy BONNEVILLE
1f06bf3ba7 Root group up button disabled
Going back to home when opening a file from explorer no longer displays open file
Inverted Breadcrumb order to avoid reordering items
Update release notes
Code cleanup
2020-06-26 01:16:44 +02:00
Geoffroy BONNEVILLE
7dcd5a4a57 Removed Breadcrumb service
Breadcrumb control handles breadcrumb status
Layout improvements
Added the ability to delete an entry from the group menu
2020-06-10 13:38:04 +02:00
Geoffroy BONNEVILLE
c62ed584dc New Breadcrumb user control
New Breadcrumb service
WIP icons and back button behavior
2020-06-09 20:18:17 +02:00
d6b17fe696 WIP Return of the Breadcrumb
Entry Title field added as part of the entry page
Code cleanup
2020-06-08 19:17:11 +02:00
f477828628 Changed some observable collections types for HamburgerMenu binding (issue with WinRT 8.1)
Restored CollectionViewSource for recent (issue with WinRT 8.1)
Forbid horizontal scrolling in Main Menu
Fixed an incorrect SetupFocusAction target object binding
2020-06-08 14:16:54 +02:00
Geoffroy BONNEVILLE
4a0bc1cb86 Add ARM debug target 2020-06-08 11:52:12 +02:00
Geoffroy BONNEVILLE
4e7aca5517 CSV Import command created 2020-06-05 19:08:29 +02:00
Geoffroy BONNEVILLE
1f04f941c2 Corrected issue in color picker user control when changing history
Use of commands instead of events in code-behind
Some refactoring
2020-06-04 16:29:26 +02:00
Geoffroy BONNEVILLE
5c1dfa1b0e Update version to 1.20
Clicking new group button while collapsed will expand the menu
Use of space freed by hidden hamburger menu
2020-06-04 15:31:38 +02:00
40 changed files with 675 additions and 356 deletions

View File

@@ -36,6 +36,16 @@
<WarningLevel>4</WarningLevel> <WarningLevel>4</WarningLevel>
<PlatformTarget>x64</PlatformTarget> <PlatformTarget>x64</PlatformTarget>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|ARM' ">
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\ARM\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<PlatformTarget>ARM</PlatformTarget>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' "> <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>pdbonly</DebugType> <DebugType>pdbonly</DebugType>
<Optimize>true</Optimize> <Optimize>true</Optimize>
@@ -81,7 +91,6 @@
<Compile Include="Common\Interfaces\IDatabaseProxy.cs" /> <Compile Include="Common\Interfaces\IDatabaseProxy.cs" />
<Compile Include="Common\Interfaces\IEntityVm.cs" /> <Compile Include="Common\Interfaces\IEntityVm.cs" />
<Compile Include="Common\Interfaces\IFileProxy.cs" /> <Compile Include="Common\Interfaces\IFileProxy.cs" />
<Compile Include="Common\Interfaces\IImportFormat.cs" />
<Compile Include="Common\Interfaces\ICredentialsProxy.cs" /> <Compile Include="Common\Interfaces\ICredentialsProxy.cs" />
<Compile Include="Common\Interfaces\ILogger.cs" /> <Compile Include="Common\Interfaces\ILogger.cs" />
<Compile Include="Common\Interfaces\INotificationService.cs" /> <Compile Include="Common\Interfaces\INotificationService.cs" />
@@ -90,6 +99,7 @@
<Compile Include="Common\Interfaces\ISettingsProxy.cs" /> <Compile Include="Common\Interfaces\ISettingsProxy.cs" />
<Compile Include="Common\Mappings\IMapFrom.cs" /> <Compile Include="Common\Mappings\IMapFrom.cs" />
<Compile Include="Common\Mappings\MappingProfile.cs" /> <Compile Include="Common\Mappings\MappingProfile.cs" />
<Compile Include="Common\Models\BreadcrumbItem.cs" />
<Compile Include="Entry\Commands\AddAttachment\AddAttachmentCommand.cs" /> <Compile Include="Entry\Commands\AddAttachment\AddAttachmentCommand.cs" />
<Compile Include="Entry\Commands\AddHistory\AddHistoryCommand.cs" /> <Compile Include="Entry\Commands\AddHistory\AddHistoryCommand.cs" />
<Compile Include="Entry\Commands\DeleteAttachment\DeleteAttachmentCommand.cs" /> <Compile Include="Entry\Commands\DeleteAttachment\DeleteAttachmentCommand.cs" />
@@ -105,6 +115,8 @@
<Compile Include="Group\Queries\GetAllGroups\GetAllGroupsQuery.cs" /> <Compile Include="Group\Queries\GetAllGroups\GetAllGroupsQuery.cs" />
<Compile Include="Group\Queries\GetGroup\GetGroupQuery.cs" /> <Compile Include="Group\Queries\GetGroup\GetGroupQuery.cs" />
<Compile Include="Group\Queries\SearchEntries\SearchEntriesQuery.cs" /> <Compile Include="Group\Queries\SearchEntries\SearchEntriesQuery.cs" />
<Compile Include="Import\Commands\ImportFromCsv\ImportFromCsvCommand.cs" />
<Compile Include="Import\Commands\ImportFromCsv\ImportFromCsvCommandValidator.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" />
@@ -119,7 +131,7 @@
<Compile Include="Parameters\Queries\GetKeyDerivations\GetKeyDerivationsQuery.cs" /> <Compile Include="Parameters\Queries\GetKeyDerivations\GetKeyDerivationsQuery.cs" />
<Compile Include="Database\Commands\CloseDatabase\CloseDatabaseCommand.cs" /> <Compile Include="Database\Commands\CloseDatabase\CloseDatabaseCommand.cs" />
<Compile Include="Database\Commands\CreateDatabase\CreateDatabaseCommand.cs" /> <Compile Include="Database\Commands\CreateDatabase\CreateDatabaseCommand.cs" />
<Compile Include="Database\Commands\CreateDatabase\CreateDatabaseQueryValidator.cs" /> <Compile Include="Database\Commands\CreateDatabase\CreateDatabaseCommandValidator.cs" />
<Compile Include="Database\Commands\SaveDatabase\SaveDatabaseCommand.cs" /> <Compile Include="Database\Commands\SaveDatabase\SaveDatabaseCommand.cs" />
<Compile Include="Database\Commands\UpdateCredentials\UpdateCredentialsCommand.cs" /> <Compile Include="Database\Commands\UpdateCredentials\UpdateCredentialsCommand.cs" />
<Compile Include="Database\Models\DatabaseVm.cs" /> <Compile Include="Database\Models\DatabaseVm.cs" />

View File

@@ -1,9 +0,0 @@
using System.Collections.Generic;
namespace ModernKeePass.Application.Common.Interfaces
{
public interface IImportFormat
{
List<Dictionary<string, string>> Import(IList<string> fileContents);
}
}

View File

@@ -0,0 +1,12 @@
using ModernKeePass.Domain.Enums;
namespace ModernKeePass.Application.Common.Models
{
public class BreadcrumbItem
{
public string Path { get; set; }
public string Name { get; set; }
public Icon Icon { get; set; }
}
}

View File

@@ -2,9 +2,9 @@
namespace ModernKeePass.Application.Database.Commands.CreateDatabase namespace ModernKeePass.Application.Database.Commands.CreateDatabase
{ {
public class CreateDatabaseQueryValidator : AbstractValidator<CreateDatabaseCommand> public class CreateDatabaseCommandValidator : AbstractValidator<CreateDatabaseCommand>
{ {
public CreateDatabaseQueryValidator() public CreateDatabaseCommandValidator()
{ {
RuleFor(v => v.FilePath) RuleFor(v => v.FilePath)
.NotNull() .NotNull()

View File

@@ -0,0 +1,50 @@
using System.Collections.Generic;
using System.Threading.Tasks;
using MediatR;
using ModernKeePass.Application.Common.Interfaces;
using ModernKeePass.Domain.Enums;
using ModernKeePass.Domain.Exceptions;
namespace ModernKeePass.Application.Import.Commands.ImportFromCsv
{
public class ImportFromCsvCommand : IRequest
{
public string FilePath { get; set; }
public string DestinationGroupId { get; set; }
public bool HasHeaderRow { get; set; }
public char Delimiter { get; set; } = ';';
public Dictionary<int, string> FieldMappings { get; set; }
public class CreateDatabaseCommandHandler : IAsyncRequestHandler<ImportFromCsvCommand>
{
private readonly IDatabaseProxy _database;
private readonly IFileProxy _file;
public CreateDatabaseCommandHandler(IDatabaseProxy database, IFileProxy file)
{
_database = database;
_file = file;
}
public async Task Handle(ImportFromCsvCommand message)
{
if (!_database.IsOpen) throw new DatabaseClosedException();
var fileContents = await _file.ReadTextFile(message.FilePath);
for (var index = message.HasHeaderRow ? 1 : 0; index < fileContents.Count; index++)
{
var line = fileContents[index];
var fields = line.Split(message.Delimiter);
var entry = _database.CreateEntry(message.DestinationGroupId);
for (var i = 0; i < fields.Length; i++)
{
var fieldMapping = message.FieldMappings[i];
await _database.UpdateEntry(entry.Id, fieldMapping, fields[i], fieldMapping == EntryFieldName.Password);
}
}
}
}
}
}

View File

@@ -0,0 +1,17 @@
using FluentValidation;
namespace ModernKeePass.Application.Import.Commands.ImportFromCsv
{
public class ImportFromCsvCommandValidator : AbstractValidator<ImportFromCsvCommand>
{
public ImportFromCsvCommandValidator()
{
RuleFor(v => v.FilePath)
.NotNull()
.NotEmpty();
RuleFor(v => v.DestinationGroupId)
.NotNull()
.NotEmpty();
}
}
}

View File

@@ -36,6 +36,16 @@
<WarningLevel>4</WarningLevel> <WarningLevel>4</WarningLevel>
<PlatformTarget>x64</PlatformTarget> <PlatformTarget>x64</PlatformTarget>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|ARM' ">
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\ARM\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<PlatformTarget>ARM</PlatformTarget>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' "> <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>pdbonly</DebugType> <DebugType>pdbonly</DebugType>
<Optimize>true</Optimize> <Optimize>true</Optimize>

View File

@@ -1,31 +0,0 @@
using System.Collections.Generic;
using ModernKeePass.Application.Common.Interfaces;
namespace ModernKeePass.Infrastructure.File
{
public class CsvImportFormat: IImportFormat
{
private const bool HasHeaderRow = true;
private const char Delimiter = ';';
private const char LineDelimiter = '\n';
public List<Dictionary<string, string>> Import(IList<string> fileContents)
{
var parsedResult = new List<Dictionary<string, string>>();
foreach (var line in fileContents)
{
var fields = line.Split(Delimiter);
var recordItem = new Dictionary<string, string>();
var i = 0;
foreach (var field in fields)
{
recordItem.Add(i.ToString(), field);
i++;
}
parsedResult.Add(recordItem);
}
return parsedResult;
}
}
}

View File

@@ -44,6 +44,16 @@
<WarningLevel>4</WarningLevel> <WarningLevel>4</WarningLevel>
<PlatformTarget>x64</PlatformTarget> <PlatformTarget>x64</PlatformTarget>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|ARM' ">
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\ARM\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<PlatformTarget>ARM</PlatformTarget>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|x86' "> <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|x86' ">
<DebugType>pdbonly</DebugType> <DebugType>pdbonly</DebugType>
<Optimize>true</Optimize> <Optimize>true</Optimize>
@@ -78,7 +88,6 @@
<ItemGroup> <ItemGroup>
<Compile Include="Common\MachineDateTime.cs" /> <Compile Include="Common\MachineDateTime.cs" />
<Compile Include="DependencyInjection.cs" /> <Compile Include="DependencyInjection.cs" />
<Compile Include="File\CsvImportFormat.cs" />
<Compile Include="KeePass\MappingProfiles.cs" /> <Compile Include="KeePass\MappingProfiles.cs" />
<Compile Include="KeePass\IconMapper.cs" /> <Compile Include="KeePass\IconMapper.cs" />
<Compile Include="KeePass\KeePassDatabaseSettingsProxy.cs" /> <Compile Include="KeePass\KeePassDatabaseSettingsProxy.cs" />

View File

@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<Package xmlns="http://schemas.microsoft.com/appx/2010/manifest" xmlns:m2="http://schemas.microsoft.com/appx/2013/manifest"> <Package xmlns="http://schemas.microsoft.com/appx/2010/manifest" xmlns:m2="http://schemas.microsoft.com/appx/2013/manifest">
<Identity Name="wismna.ModernKeePass" Publisher="CN=0719A91A-C322-4EE0-A257-E60733EECF06" Version="1.19.0.12" /> <Identity Name="wismna.ModernKeePass" Publisher="CN=0719A91A-C322-4EE0-A257-E60733EECF06" Version="1.20.0.12" />
<Properties> <Properties>
<DisplayName>ModernKeePass</DisplayName> <DisplayName>ModernKeePass</DisplayName>
<PublisherDisplayName>wismna</PublisherDisplayName> <PublisherDisplayName>wismna</PublisherDisplayName>

View File

@@ -31,6 +31,7 @@ using ModernKeePass.Application.Group.Models;
using ModernKeePass.Common; using ModernKeePass.Common;
using ModernKeePass.Domain.Dtos; using ModernKeePass.Domain.Dtos;
using ModernKeePass.Domain.Exceptions; using ModernKeePass.Domain.Exceptions;
using ModernKeePass.Domain.Interfaces;
using ModernKeePass.Extensions; using ModernKeePass.Extensions;
using ModernKeePass.Models; using ModernKeePass.Models;
using ModernKeePass.ViewModels.ListItems; using ModernKeePass.ViewModels.ListItems;
@@ -39,22 +40,22 @@ namespace ModernKeePass.ViewModels
{ {
public class EntryDetailVm : ViewModelBase public class EntryDetailVm : ViewModelBase
{ {
public bool HasExpired => HasExpirationDate && ExpiryDate < DateTime.Now; public bool HasExpired => HasExpirationDate && ExpiryDate < _dateTime.Now;
public string Id => SelectedItem.Id; public string Id => _current.Id;
public string ParentGroupName => _parent.Title; public GroupVm Parent { get; private set; }
public bool IsRecycleOnDelete public bool IsRecycleOnDelete
{ {
get get
{ {
var database = Database; var database = Database;
return database.IsRecycleBinEnabled && _parent.Id != database.RecycleBinId; return database.IsRecycleBinEnabled && Parent.Id != database.RecycleBinId;
} }
} }
public ObservableCollection<EntryVm> History { get; private set; } public ObservableCollection<IEntityVm> History { get; private set; }
public ObservableCollection<EntryFieldVm> AdditionalFields { get; private set; } public ObservableCollection<EntryFieldVm> AdditionalFields { get; private set; }
public ObservableCollection<Attachment> Attachments { get; private set; } public ObservableCollection<Attachment> Attachments { get; private set; }
@@ -63,37 +64,6 @@ namespace ModernKeePass.ViewModels
/// </summary> /// </summary>
public bool IsCurrentEntry => SelectedIndex == 0; public bool IsCurrentEntry => SelectedIndex == 0;
public EntryVm SelectedItem
{
get { return _selectedItem; }
set
{
Set(() => SelectedItem, ref _selectedItem, value, true);
if (value != null)
{
AdditionalFields =
new ObservableCollection<EntryFieldVm>(
SelectedItem.AdditionalFields.Select(f =>
{
var field = new EntryFieldVm(_cryptography);
field.Initialize(f.Name, f.Value, f.IsProtected);
return field;
}));
Attachments = new ObservableCollection<Attachment>(SelectedItem.Attachments.Select(f => new Attachment
{
Name = f.Key,
Content = f.Value
}));
Attachments.CollectionChanged += (sender, args) =>
{
UpdateDirtyStatus(true);
};
RaisePropertyChanged(string.Empty);
}
}
}
public int SelectedIndex public int SelectedIndex
{ {
get { return _selectedIndex; } get { return _selectedIndex; }
@@ -117,33 +87,33 @@ namespace ModernKeePass.ViewModels
public string Title public string Title
{ {
get { return SelectedItem.Title.Value; } get { return _current.Title.Value; }
set set
{ {
SelectedItem.Title.Value = value; _current.Title.Value = value;
SetFieldValue(SelectedItem.Title.Name, value, false).ConfigureAwait(false).GetAwaiter(); SetFieldValue(_current.Title.Name, value, false).ConfigureAwait(false).GetAwaiter();
} }
} }
public string UserName public string UserName
{ {
get { return SelectedItem.Username.Value; } get { return _current.Username.Value; }
set set
{ {
SelectedItem.Username.Value = value; _current.Username.Value = value;
SetFieldValue(SelectedItem.Username.Name, value, false).ConfigureAwait(false).GetAwaiter(); SetFieldValue(_current.Username.Name, value, false).ConfigureAwait(false).GetAwaiter();
RaisePropertyChanged(nameof(UserName)); RaisePropertyChanged(nameof(UserName));
} }
} }
public string Password public string Password
{ {
get { return _cryptography.UnProtect(SelectedItem.Password.Value).GetAwaiter().GetResult(); } get { return _cryptography.UnProtect(_current.Password.Value).GetAwaiter().GetResult(); }
set set
{ {
var protectedPassword = _cryptography.Protect(value).ConfigureAwait(false).GetAwaiter().GetResult(); var protectedPassword = _cryptography.Protect(value).ConfigureAwait(false).GetAwaiter().GetResult();
SelectedItem.Password.Value = protectedPassword; _current.Password.Value = protectedPassword;
SetFieldValue(SelectedItem.Password.Name, protectedPassword, true).ConfigureAwait(false).GetAwaiter(); SetFieldValue(_current.Password.Name, protectedPassword, true).ConfigureAwait(false).GetAwaiter();
RaisePropertyChanged(nameof(Password)); RaisePropertyChanged(nameof(Password));
} }
@@ -151,65 +121,65 @@ namespace ModernKeePass.ViewModels
public string Url public string Url
{ {
get { return SelectedItem.Url.Value; } get { return _current.Url.Value; }
set set
{ {
SelectedItem.Url.Value = value; _current.Url.Value = value;
SetFieldValue(SelectedItem.Url.Name, value, false).ConfigureAwait(false).GetAwaiter(); SetFieldValue(_current.Url.Name, value, false).ConfigureAwait(false).GetAwaiter();
RaisePropertyChanged(nameof(Url)); RaisePropertyChanged(nameof(Url));
} }
} }
public string Notes public string Notes
{ {
get { return SelectedItem.Notes.Value; } get { return _current.Notes.Value; }
set set
{ {
SelectedItem.Notes.Value = value; _current.Notes.Value = value;
SetFieldValue(SelectedItem.Notes.Name, value, false).ConfigureAwait(false).GetAwaiter(); SetFieldValue(_current.Notes.Name, value, false).ConfigureAwait(false).GetAwaiter();
} }
} }
public Symbol Icon public Symbol Icon
{ {
get { return (Symbol)Enum.Parse(typeof(Symbol), SelectedItem.Icon.ToString()); } get { return (Symbol)Enum.Parse(typeof(Symbol), _current.Icon.ToString()); }
set set
{ {
SelectedItem.Icon = (Icon)Enum.Parse(typeof(Icon), value.ToString()); _current.Icon = (Icon)Enum.Parse(typeof(Icon), value.ToString());
SetFieldValue(EntryFieldName.Icon, SelectedItem.Icon, false).ConfigureAwait(false).GetAwaiter(); SetFieldValue(EntryFieldName.Icon, _current.Icon, false).ConfigureAwait(false).GetAwaiter();
} }
} }
public DateTimeOffset ExpiryDate public DateTimeOffset ExpiryDate
{ {
get { return SelectedItem.ExpirationDate; } get { return _current.ExpirationDate; }
set set
{ {
if (!HasExpirationDate) return; if (!HasExpirationDate) return;
SelectedItem.ExpirationDate = value.Date; _current.ExpirationDate = value.Date;
SetFieldValue(EntryFieldName.ExpirationDate, SelectedItem.ExpirationDate, false).ConfigureAwait(false).GetAwaiter(); SetFieldValue(EntryFieldName.ExpirationDate, _current.ExpirationDate, false).ConfigureAwait(false).GetAwaiter();
} }
} }
public TimeSpan ExpiryTime public TimeSpan ExpiryTime
{ {
get { return SelectedItem.ExpirationDate.TimeOfDay; } get { return _current.ExpirationDate.TimeOfDay; }
set set
{ {
if (!HasExpirationDate) return; if (!HasExpirationDate) return;
SelectedItem.ExpirationDate = SelectedItem.ExpirationDate.Date.Add(value); _current.ExpirationDate = _current.ExpirationDate.Date.Add(value);
SetFieldValue(EntryFieldName.ExpirationDate, SelectedItem.ExpirationDate, false).ConfigureAwait(false).GetAwaiter(); SetFieldValue(EntryFieldName.ExpirationDate, _current.ExpirationDate, false).ConfigureAwait(false).GetAwaiter();
} }
} }
public bool HasExpirationDate public bool HasExpirationDate
{ {
get { return SelectedItem.HasExpirationDate; } get { return _current.HasExpirationDate; }
set set
{ {
SelectedItem.HasExpirationDate = value; _current.HasExpirationDate = value;
SetFieldValue(EntryFieldName.HasExpirationDate, value, false).ConfigureAwait(false).GetAwaiter(); SetFieldValue(EntryFieldName.HasExpirationDate, value, false).ConfigureAwait(false).GetAwaiter();
RaisePropertyChanged(nameof(HasExpirationDate)); RaisePropertyChanged(nameof(HasExpirationDate));
} }
@@ -217,41 +187,34 @@ namespace ModernKeePass.ViewModels
public SolidColorBrush BackgroundColor public SolidColorBrush BackgroundColor
{ {
get { return SelectedItem?.BackgroundColor.ToSolidColorBrush(); } get { return _current?.BackgroundColor.ToSolidColorBrush(); }
set set
{ {
SelectedItem.BackgroundColor = value.ToColor(); _current.BackgroundColor = value.ToColor();
SetFieldValue(EntryFieldName.BackgroundColor, SelectedItem.BackgroundColor, false).ConfigureAwait(false).GetAwaiter(); SetFieldValue(EntryFieldName.BackgroundColor, _current.BackgroundColor, false).ConfigureAwait(false).GetAwaiter();
} }
} }
public SolidColorBrush ForegroundColor public SolidColorBrush ForegroundColor
{ {
get { return SelectedItem?.ForegroundColor.ToSolidColorBrush(); } get { return _current?.ForegroundColor.ToSolidColorBrush(); }
set set
{ {
SelectedItem.ForegroundColor = value.ToColor(); _current.ForegroundColor = value.ToColor();
SetFieldValue(EntryFieldName.ForegroundColor, SelectedItem.ForegroundColor, false).ConfigureAwait(false).GetAwaiter(); SetFieldValue(EntryFieldName.ForegroundColor, _current.ForegroundColor, false).ConfigureAwait(false).GetAwaiter();
} }
} }
public bool IsEditMode
{
get { return IsCurrentEntry && _isEditMode; }
set { Set(() => IsEditMode, ref _isEditMode, value); }
}
public RelayCommand SaveCommand { get; } public RelayCommand SaveCommand { get; }
public RelayCommand<string> MoveCommand { get; } public RelayCommand<string> MoveCommand { get; }
public RelayCommand RestoreCommand { get; } public RelayCommand RestoreCommand { get; }
public RelayCommand DeleteCommand { get; } public RelayCommand DeleteCommand { get; }
public RelayCommand GoBackCommand { get; } public RelayCommand AddAdditionalField { get; }
public RelayCommand GoToParentCommand { get; set; } public RelayCommand<EntryFieldVm> DeleteAdditionalField { get; }
public RelayCommand AddAdditionalField { get; set; } public RelayCommand<Attachment> OpenAttachmentCommand { get; }
public RelayCommand<EntryFieldVm> DeleteAdditionalField { get; set; } public RelayCommand AddAttachmentCommand { get; }
public RelayCommand<Attachment> OpenAttachmentCommand { get; set; } public RelayCommand<Attachment> DeleteAttachmentCommand { get; }
public RelayCommand AddAttachmentCommand { get; set; } public RelayCommand<EntryVm> SetCurrentEntryCommand { get; }
public RelayCommand<Attachment> DeleteAttachmentCommand { get; set; }
private DatabaseVm Database => _mediator.Send(new GetDatabaseQuery()).GetAwaiter().GetResult(); private DatabaseVm Database => _mediator.Send(new GetDatabaseQuery()).GetAwaiter().GetResult();
@@ -262,14 +225,13 @@ namespace ModernKeePass.ViewModels
private readonly INotificationService _notification; private readonly INotificationService _notification;
private readonly IFileProxy _file; private readonly IFileProxy _file;
private readonly ICryptographyClient _cryptography; private readonly ICryptographyClient _cryptography;
private GroupVm _parent; private readonly IDateTime _dateTime;
private EntryVm _selectedItem; private EntryVm _current;
private int _selectedIndex; private int _selectedIndex;
private int _additionalFieldSelectedIndex = -1; private int _additionalFieldSelectedIndex = -1;
private bool _isEditMode;
private bool _isDirty; private bool _isDirty;
public EntryDetailVm(IMediator mediator, INavigationService navigation, IResourceProxy resource, IDialogService dialog, INotificationService notification, IFileProxy file, ICryptographyClient cryptography) public EntryDetailVm(IMediator mediator, INavigationService navigation, IResourceProxy resource, IDialogService dialog, INotificationService notification, IFileProxy file, ICryptographyClient cryptography, IDateTime dateTime)
{ {
_mediator = mediator; _mediator = mediator;
_navigation = navigation; _navigation = navigation;
@@ -278,18 +240,18 @@ namespace ModernKeePass.ViewModels
_notification = notification; _notification = notification;
_file = file; _file = file;
_cryptography = cryptography; _cryptography = cryptography;
_dateTime = dateTime;
SaveCommand = new RelayCommand(async () => await SaveChanges(), () => Database.IsDirty); SaveCommand = new RelayCommand(async () => await SaveChanges(), () => Database.IsDirty);
MoveCommand = new RelayCommand<string>(async destination => await Move(destination), destination => _parent != null && !string.IsNullOrEmpty(destination) && destination != _parent.Id); MoveCommand = new RelayCommand<string>(async destination => await Move(destination), destination => Parent != null && !string.IsNullOrEmpty(destination) && destination != Parent.Id);
RestoreCommand = new RelayCommand(async () => await RestoreHistory()); RestoreCommand = new RelayCommand(async () => await RestoreHistory());
DeleteCommand = new RelayCommand(async () => await AskForDelete()); DeleteCommand = new RelayCommand(async () => await AskForDelete());
GoBackCommand = new RelayCommand(() => _navigation.GoBack());
GoToParentCommand = new RelayCommand(() => GoToGroup(_parent.Id));
AddAdditionalField = new RelayCommand(AddField, () => IsCurrentEntry); AddAdditionalField = new RelayCommand(AddField, () => IsCurrentEntry);
DeleteAdditionalField = new RelayCommand<EntryFieldVm>(async field => await DeleteField(field), field => field != null && IsCurrentEntry); DeleteAdditionalField = new RelayCommand<EntryFieldVm>(async field => await DeleteField(field), field => field != null && IsCurrentEntry);
OpenAttachmentCommand = new RelayCommand<Attachment>(async attachment => await OpenAttachment(attachment)); OpenAttachmentCommand = new RelayCommand<Attachment>(async attachment => await OpenAttachment(attachment));
AddAttachmentCommand = new RelayCommand(async () => await AddAttachment(), () => IsCurrentEntry); AddAttachmentCommand = new RelayCommand(async () => await AddAttachment(), () => IsCurrentEntry);
DeleteAttachmentCommand = new RelayCommand<Attachment>(async attachment => await DeleteAttachment(attachment), _ => IsCurrentEntry); DeleteAttachmentCommand = new RelayCommand<Attachment>(async attachment => await DeleteAttachment(attachment), _ => IsCurrentEntry);
SetCurrentEntryCommand = new RelayCommand<EntryVm>(SetCurrentEntry, entry => entry != null);
MessengerInstance.Register<DatabaseSavedMessage>(this, _ => SaveCommand.RaiseCanExecuteChanged()); MessengerInstance.Register<DatabaseSavedMessage>(this, _ => SaveCommand.RaiseCanExecuteChanged());
MessengerInstance.Register<EntryFieldValueChangedMessage>(this, async message => await SetFieldValue(message.FieldName, message.FieldValue, message.IsProtected)); MessengerInstance.Register<EntryFieldValueChangedMessage>(this, async message => await SetFieldValue(message.FieldName, message.FieldValue, message.IsProtected));
@@ -300,10 +262,11 @@ namespace ModernKeePass.ViewModels
public async Task Initialize(string entryId) public async Task Initialize(string entryId)
{ {
SelectedIndex = 0; SelectedIndex = 0;
SelectedItem = await _mediator.Send(new GetEntryQuery { Id = entryId }); _current = await _mediator.Send(new GetEntryQuery { Id = entryId });
_parent = await _mediator.Send(new GetGroupQuery { Id = SelectedItem.ParentGroupId }); SetCurrentEntry(_current);
History = new ObservableCollection<EntryVm> { SelectedItem }; Parent = await _mediator.Send(new GetGroupQuery { Id = _current.ParentGroupId });
foreach (var entry in SelectedItem.History.Skip(1)) History = new ObservableCollection<IEntityVm> { _current };
foreach (var entry in _current.History.Skip(1))
{ {
History.Add(entry); History.Add(entry);
} }
@@ -316,7 +279,7 @@ namespace ModernKeePass.ViewModels
public async Task AddHistory() public async Task AddHistory()
{ {
if (_isDirty && Database.IsOpen) await _mediator.Send(new AddHistoryCommand { Entry = History[0] }); if (_isDirty && Database.IsOpen) await _mediator.Send(new AddHistoryCommand { Entry = History[0] as EntryVm });
} }
public void GoToGroup(string groupId) public void GoToGroup(string groupId)
@@ -327,7 +290,7 @@ namespace ModernKeePass.ViewModels
private async Task Move(string destination) private async Task Move(string destination)
{ {
await _mediator.Send(new AddEntryCommand { ParentGroupId = destination, EntryId = Id }); await _mediator.Send(new AddEntryCommand { ParentGroupId = destination, EntryId = Id });
await _mediator.Send(new RemoveEntryCommand { ParentGroupId = _parent.Id, EntryId = Id }); await _mediator.Send(new RemoveEntryCommand { ParentGroupId = Parent.Id, EntryId = Id });
GoToGroup(destination); GoToGroup(destination);
} }
@@ -389,7 +352,7 @@ namespace ModernKeePass.ViewModels
_resource.GetResourceValue("EntityDeleteCancelButton"), async isOk => _resource.GetResourceValue("EntityDeleteCancelButton"), async isOk =>
{ {
if (!isOk) return; if (!isOk) return;
await _mediator.Send(new DeleteHistoryCommand { Entry = History[0], HistoryIndex = History.Count - SelectedIndex - 1 }); await _mediator.Send(new DeleteHistoryCommand { Entry = History[0] as EntryVm, HistoryIndex = History.Count - SelectedIndex - 1 });
History.RemoveAt(SelectedIndex); History.RemoveAt(SelectedIndex);
}); });
} }
@@ -397,8 +360,8 @@ namespace ModernKeePass.ViewModels
private async Task RestoreHistory() private async Task RestoreHistory()
{ {
await _mediator.Send(new RestoreHistoryCommand { Entry = History[0], HistoryIndex = History.Count - SelectedIndex - 1 }); await _mediator.Send(new RestoreHistoryCommand { Entry = History[0] as EntryVm, HistoryIndex = History.Count - SelectedIndex - 1 });
History.Insert(0, SelectedItem); History.Insert(0, _current);
} }
private async Task SaveChanges() private async Task SaveChanges()
@@ -420,7 +383,7 @@ namespace ModernKeePass.ViewModels
await _mediator.Send(new DeleteEntryCommand await _mediator.Send(new DeleteEntryCommand
{ {
EntryId = Id, EntryId = Id,
ParentGroupId = SelectedItem.ParentGroupId, ParentGroupId = _current.ParentGroupId,
RecycleBinName = _resource.GetResourceValue("RecycleBinTitle") RecycleBinName = _resource.GetResourceValue("RecycleBinTitle")
}); });
_isDirty = false; _isDirty = false;
@@ -443,13 +406,13 @@ namespace ModernKeePass.ViewModels
var fileInfo = await _file.OpenFile(string.Empty, Domain.Common.Constants.Extensions.Any, false); var fileInfo = await _file.OpenFile(string.Empty, Domain.Common.Constants.Extensions.Any, false);
if (fileInfo == null) return; if (fileInfo == null) return;
var contents = await _file.ReadBinaryFile(fileInfo.Id); var contents = await _file.ReadBinaryFile(fileInfo.Id);
await _mediator.Send(new AddAttachmentCommand { Entry = SelectedItem, AttachmentName = fileInfo.Name, AttachmentContent = contents }); await _mediator.Send(new AddAttachmentCommand { Entry = _current, AttachmentName = fileInfo.Name, AttachmentContent = contents });
Attachments.Add(new Attachment { Name = fileInfo.Name, Content = contents }); Attachments.Add(new Attachment { Name = fileInfo.Name, Content = contents });
} }
private async Task DeleteAttachment(Attachment attachment) private async Task DeleteAttachment(Attachment attachment)
{ {
await _mediator.Send(new DeleteAttachmentCommand { Entry = SelectedItem, AttachmentName = attachment.Name }); await _mediator.Send(new DeleteAttachmentCommand { Entry = _current, AttachmentName = attachment.Name });
Attachments.Remove(attachment); Attachments.Remove(attachment);
} }
@@ -458,5 +421,29 @@ namespace ModernKeePass.ViewModels
SaveCommand.RaiseCanExecuteChanged(); SaveCommand.RaiseCanExecuteChanged();
_isDirty = isDirty; _isDirty = isDirty;
} }
private void SetCurrentEntry(EntryVm entry)
{
_current = entry;
AdditionalFields =
new ObservableCollection<EntryFieldVm>(
entry.AdditionalFields.Select(f =>
{
var field = new EntryFieldVm(_cryptography);
field.Initialize(f.Name, f.Value, f.IsProtected);
return field;
}));
Attachments = new ObservableCollection<Attachment>(entry.Attachments.Select(f => new Attachment
{
Name = f.Key,
Content = f.Value
}));
Attachments.CollectionChanged += (sender, args) =>
{
UpdateDirtyStatus(true);
};
RaisePropertyChanged(string.Empty);
}
} }
} }

View File

@@ -18,6 +18,7 @@ using ModernKeePass.Application.Group.Commands.AddEntry;
using ModernKeePass.Application.Group.Commands.AddGroup; using ModernKeePass.Application.Group.Commands.AddGroup;
using ModernKeePass.Application.Group.Commands.CreateEntry; using ModernKeePass.Application.Group.Commands.CreateEntry;
using ModernKeePass.Application.Group.Commands.CreateGroup; using ModernKeePass.Application.Group.Commands.CreateGroup;
using ModernKeePass.Application.Group.Commands.DeleteEntry;
using ModernKeePass.Application.Group.Commands.DeleteGroup; using ModernKeePass.Application.Group.Commands.DeleteGroup;
using ModernKeePass.Application.Group.Commands.MoveEntry; using ModernKeePass.Application.Group.Commands.MoveEntry;
using ModernKeePass.Application.Group.Commands.MoveGroup; using ModernKeePass.Application.Group.Commands.MoveGroup;
@@ -38,10 +39,12 @@ namespace ModernKeePass.ViewModels
{ {
public ObservableCollection<EntryVm> Entries { get; private set; } public ObservableCollection<EntryVm> Entries { get; private set; }
public ObservableCollection<GroupVm> Groups { get; private set; } public ObservableCollection<IEntityVm> Groups { get; private set; }
public bool IsNotRoot => Database.RootGroupId != _group.Id; public bool IsNotRoot => Database.RootGroupId != _group.Id;
public GroupVm Parent { get; private set; }
public IOrderedEnumerable<IGrouping<char, EntryVm>> EntriesZoomedOut => from e in Entries public IOrderedEnumerable<IGrouping<char, EntryVm>> EntriesZoomedOut => from e in Entries
group e by (e.Title.Value ?? string.Empty).ToUpper().FirstOrDefault() into grp group e by (e.Title.Value ?? string.Empty).ToUpper().FirstOrDefault() into grp
orderby grp.Key orderby grp.Key
@@ -80,11 +83,11 @@ namespace ModernKeePass.ViewModels
} }
} }
public string ParentGroupName => _parent?.Title; public string ParentGroupName => Parent == null ? Database.Name : Parent.Title;
public bool IsRecycleOnDelete => Database.IsRecycleBinEnabled && !IsInRecycleBin; public bool IsRecycleOnDelete => Database.IsRecycleBinEnabled && !IsInRecycleBin;
public bool IsInRecycleBin => _parent != null && _parent.Id == Database.RecycleBinId; public bool IsInRecycleBin => Parent != null && Parent.Id == Database.RecycleBinId;
public RelayCommand SaveCommand { get; } public RelayCommand SaveCommand { get; }
public RelayCommand SortEntriesCommand { get; } public RelayCommand SortEntriesCommand { get; }
@@ -92,9 +95,12 @@ namespace ModernKeePass.ViewModels
public RelayCommand<string> MoveCommand { get; } public RelayCommand<string> MoveCommand { get; }
public RelayCommand CreateEntryCommand { get; } public RelayCommand CreateEntryCommand { get; }
public RelayCommand<string> CreateGroupCommand { get; } public RelayCommand<string> CreateGroupCommand { get; }
public RelayCommand DeleteCommand { get; set; } public RelayCommand DeleteCommand { get; }
public RelayCommand GoBackCommand { get; set; } public RelayCommand GoBackCommand { get; }
public RelayCommand GoToParentCommand { get; set; } public RelayCommand GoToParentCommand { get; }
public RelayCommand<GroupVm> GoToGroupCommand { get; }
public RelayCommand<EntryVm> GoToEntryCommand { get; }
public RelayCommand<EntryVm> DeleteEntryCommand { get; }
private DatabaseVm Database => _mediator.Send(new GetDatabaseQuery()).GetAwaiter().GetResult(); private DatabaseVm Database => _mediator.Send(new GetDatabaseQuery()).GetAwaiter().GetResult();
@@ -104,7 +110,6 @@ namespace ModernKeePass.ViewModels
private readonly IDialogService _dialog; private readonly IDialogService _dialog;
private readonly INotificationService _notification; private readonly INotificationService _notification;
private GroupVm _group; private GroupVm _group;
private GroupVm _parent;
private bool _isEditMode; private bool _isEditMode;
private EntryVm _reorderedEntry; private EntryVm _reorderedEntry;
private GroupVm _reorderedGroup; private GroupVm _reorderedGroup;
@@ -125,7 +130,10 @@ namespace ModernKeePass.ViewModels
CreateGroupCommand = new RelayCommand<string>(async newGroupName => await AddNewGroup(newGroupName), _ => !IsInRecycleBin && Database.RecycleBinId != Id); CreateGroupCommand = new RelayCommand<string>(async newGroupName => await AddNewGroup(newGroupName), _ => !IsInRecycleBin && Database.RecycleBinId != Id);
DeleteCommand = new RelayCommand(async () => await AskForDelete(),() => IsNotRoot); DeleteCommand = new RelayCommand(async () => await AskForDelete(),() => IsNotRoot);
GoBackCommand = new RelayCommand(() => _navigation.GoBack()); GoBackCommand = new RelayCommand(() => _navigation.GoBack());
GoToParentCommand= new RelayCommand(() => GoToGroup(_parent.Id), () => _parent != null); GoToParentCommand = new RelayCommand(() => GoToGroup(Parent.Id), () => Parent != null);
GoToGroupCommand = new RelayCommand<GroupVm>(group => GoToGroup(group.Id), group => group != null);
GoToEntryCommand = new RelayCommand<EntryVm>(entry => GoToEntry(entry.Id), entry => entry != null);
DeleteEntryCommand = new RelayCommand<EntryVm>(async entry => await AskForDeleteEntry(entry), entry => entry != null);
MessengerInstance.Register<DatabaseSavedMessage>(this, _ => SaveCommand.RaiseCanExecuteChanged()); MessengerInstance.Register<DatabaseSavedMessage>(this, _ => SaveCommand.RaiseCanExecuteChanged());
} }
@@ -135,12 +143,13 @@ namespace ModernKeePass.ViewModels
_group = await _mediator.Send(new GetGroupQuery { Id = groupId }); _group = await _mediator.Send(new GetGroupQuery { Id = groupId });
if (!string.IsNullOrEmpty(_group.ParentGroupId)) if (!string.IsNullOrEmpty(_group.ParentGroupId))
{ {
_parent = await _mediator.Send(new GetGroupQuery { Id = _group.ParentGroupId }); Parent = await _mediator.Send(new GetGroupQuery {Id = _group.ParentGroupId});
} }
else Parent = null;
Entries = new ObservableCollection<EntryVm>(_group.Entries); Entries = new ObservableCollection<EntryVm>(_group.Entries);
Entries.CollectionChanged += Entries_CollectionChanged; Entries.CollectionChanged += Entries_CollectionChanged;
Groups = new ObservableCollection<GroupVm>(_group.Groups); Groups = new ObservableCollection<IEntityVm>(_group.Groups);
Groups.CollectionChanged += Groups_CollectionChanged; Groups.CollectionChanged += Groups_CollectionChanged;
} }
@@ -176,7 +185,7 @@ namespace ModernKeePass.ViewModels
public async Task Move(string destinationId) public async Task Move(string destinationId)
{ {
await _mediator.Send(new AddGroupCommand {ParentGroupId = destinationId, GroupId = Id }); await _mediator.Send(new AddGroupCommand {ParentGroupId = destinationId, GroupId = Id });
await _mediator.Send(new RemoveGroupCommand {ParentGroupId = _parent.Id, GroupId = Id }); await _mediator.Send(new RemoveGroupCommand {ParentGroupId = Parent.Id, GroupId = Id });
GoToGroup(destinationId); GoToGroup(destinationId);
} }
@@ -251,7 +260,7 @@ namespace ModernKeePass.ViewModels
private async Task SortGroupsAsync() private async Task SortGroupsAsync()
{ {
await _mediator.Send(new SortGroupsCommand {Group = _group}); await _mediator.Send(new SortGroupsCommand {Group = _group});
Groups = new ObservableCollection<GroupVm>(_group.Groups); Groups = new ObservableCollection<IEntityVm>(_group.Groups);
RaisePropertyChanged(nameof(Groups)); RaisePropertyChanged(nameof(Groups));
SaveCommand.RaiseCanExecuteChanged(); SaveCommand.RaiseCanExecuteChanged();
} }
@@ -285,7 +294,39 @@ namespace ModernKeePass.ViewModels
ParentGroupId = _group.ParentGroupId, ParentGroupId = _group.ParentGroupId,
RecycleBinName = _resource.GetResourceValue("RecycleBinTitle") RecycleBinName = _resource.GetResourceValue("RecycleBinTitle")
}); });
_navigation.GoBack(); _navigation.GoBack();
} }
private async Task AskForDeleteEntry(EntryVm entry)
{
if (IsRecycleOnDelete)
{
await DeleteEntry(entry);
_notification.Show(_resource.GetResourceValue("EntityDeleting"), string.Format(_resource.GetResourceValue("EntryRecycled"), entry.Title));
}
else
{
await _dialog.ShowMessage(
string.Format(_resource.GetResourceValue("EntryDeletingConfirmation"), entry.Title),
_resource.GetResourceValue("EntityDeleting"),
_resource.GetResourceValue("EntityDeleteActionButton"),
_resource.GetResourceValue("EntityDeleteCancelButton"),
async isOk =>
{
if (isOk) await DeleteEntry(entry);
});
}
}
private async Task DeleteEntry(EntryVm entry)
{
await _mediator.Send(new DeleteEntryCommand
{
EntryId = entry.Id,
ParentGroupId = Id,
RecycleBinName = _resource.GetResourceValue("RecycleBinTitle")
});
Entries.Remove(entry);
}
} }
} }

View File

@@ -32,14 +32,8 @@
<ColumnDefinition Width="Auto" /> <ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" /> <ColumnDefinition Width="*" />
</Grid.ColumnDefinitions> </Grid.ColumnDefinitions>
<controls:HamburgerMenuUserControl
x:Name="HamburgerMenu"
x:Uid="HistoryLeftListView"
ItemsSource="{Binding History}"
SelectedIndex="{Binding SelectedIndex, Mode=TwoWay}"
SelectedItem="{Binding SelectedItem, Mode=TwoWay}" />
<Grid Grid.Column="1"> <Grid Grid.Column="1">
<Hub Padding="0"> <Hub x:Name="Hub" Padding="0">
<Hub.Resources> <Hub.Resources>
<Style TargetType="TextBlock" x:Key="EntryTextBlockStyle"> <Style TargetType="TextBlock" x:Key="EntryTextBlockStyle">
<Setter Property="Margin" Value="0,20,0,0"/> <Setter Property="Margin" Value="0,20,0,0"/>
@@ -57,6 +51,15 @@
<Setter Property="FontSize" Value="18"/> <Setter Property="FontSize" Value="18"/>
</Style> </Style>
</StackPanel.Resources> </StackPanel.Resources>
<TextBlock x:Uid="EntryTitle" Style="{StaticResource EntryTextBlockStyle}" />
<local:TextBoxWithButton x:Uid="TitleTextBox" Text="{Binding Title, Mode=TwoWay}" Style="{StaticResource TextBoxWithButtonStyle}" ButtonContent="&#xE16F;" IsEnabled="{Binding IsCurrentEntry}">
<interactivity:Interaction.Behaviors>
<core:EventTriggerBehavior EventName="ButtonClick">
<actions:ClipboardAction Text="{Binding Title}" />
<actions:ToastAction x:Uid="ToastCopyTitle" Title="{Binding Title}" />
</core:EventTriggerBehavior>
</interactivity:Interaction.Behaviors>
</local:TextBoxWithButton>
<TextBlock x:Uid="EntryLogin" Style="{StaticResource EntryTextBlockStyle}" /> <TextBlock x:Uid="EntryLogin" Style="{StaticResource EntryTextBlockStyle}" />
<local:TextBoxWithButton x:Uid="LoginTextBox" Text="{Binding UserName, Mode=TwoWay}" Style="{StaticResource TextBoxWithButtonStyle}" ButtonContent="&#xE16F;" IsEnabled="{Binding IsCurrentEntry}"> <local:TextBoxWithButton x:Uid="LoginTextBox" Text="{Binding UserName, Mode=TwoWay}" Style="{StaticResource TextBoxWithButtonStyle}" ButtonContent="&#xE16F;" IsEnabled="{Binding IsCurrentEntry}">
<interactivity:Interaction.Behaviors> <interactivity:Interaction.Behaviors>
@@ -209,62 +212,28 @@
</HubSection> </HubSection>
</Hub> </Hub>
</Grid> </Grid>
<controls:HamburgerMenuUserControl
Grid.Column="0"
x:Name="HamburgerMenu"
x:Uid="HistoryLeftListView"
ItemsSource="{Binding History}"
SelectedIndex="{Binding SelectedIndex, Mode=TwoWay}">
<interactivity:Interaction.Behaviors>
<core:EventTriggerBehavior EventName="SelectionChanged">
<core:InvokeCommandAction Command="{Binding SetCurrentEntryCommand}" CommandParameter="{Binding SelectedItem, ElementName=HamburgerMenu}" />
</core:EventTriggerBehavior>
</interactivity:Interaction.Behaviors>
</controls:HamburgerMenuUserControl>
</Grid> </Grid>
<!-- Bouton Précédent et titre de la page --> <!-- Bouton Précédent et titre de la page -->
<Grid Grid.Row="0" Background="{ThemeResource AppBarBackgroundThemeBrush}"> <Grid Grid.Row="0" Background="{ThemeResource AppBarBackgroundThemeBrush}">
<Grid.ColumnDefinitions> <Grid.ColumnDefinitions>
<ColumnDefinition Width="{StaticResource MenuWidthGridLength}"/>
<ColumnDefinition Width="{StaticResource MenuWidthGridLength}"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/> <ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/> <ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions> </Grid.ColumnDefinitions>
<Button Grid.Column="0" <controls:BreadcrumbUserControl x:Name="Breadcrumb" Group="{Binding Parent}" />
Command="{Binding GoBackCommand}"
Height="{StaticResource MenuHeight}"
Width="{StaticResource MenuWidth}"
AutomationProperties.Name="Back"
AutomationProperties.AutomationId="BackButton"
AutomationProperties.ItemType="Navigation Button"
Style="{StaticResource NoBorderButtonStyle}">
<SymbolIcon Symbol="Back" />
</Button>
<Button Grid.Column="1"
Height="{StaticResource MenuHeight}"
Width="{StaticResource MenuWidth}"
Command="{Binding GoToParentCommand}"
Style="{StaticResource NoBorderButtonStyle}">
<SymbolIcon Symbol="Up" />
<ToolTipService.ToolTip>
<ToolTip Content="{Binding ParentGroupName}" />
</ToolTipService.ToolTip>
</Button>
<Viewbox Grid.Column="2" MaxHeight="200">
<SymbolIcon Symbol="{Binding Icon}" Width="100" Height="70" />
</Viewbox>
<TextBox Grid.Column="3"
x:Uid="EntryTitle"
x:Name="TitleTextBox"
Text="{Binding Title, Mode=TwoWay}"
Background="Transparent"
IsHitTestVisible="{Binding IsEditMode}"
FontSize="20"
FontWeight="Light"
TextWrapping="NoWrap"
VerticalAlignment="Center">
<interactivity:Interaction.Behaviors>
<core:DataTriggerBehavior Binding="{Binding IsEditMode}" Value="True">
<actions:SetupFocusAction TargetObject="{Binding ElementName=TitleTextBox}" />
<core:ChangePropertyAction TargetObject="{Binding ElementName=TitleTextBox}" PropertyName="BorderThickness" Value="2" />
</core:DataTriggerBehavior>
<core:DataTriggerBehavior Binding="{Binding IsEditMode}" Value="False">
<core:ChangePropertyAction TargetObject="{Binding ElementName=TitleTextBox}" PropertyName="BorderThickness" Value="0" />
</core:DataTriggerBehavior>
</interactivity:Interaction.Behaviors>
</TextBox>
<controls:TopMenuUserControl <controls:TopMenuUserControl
x:Name="TopMenu" Grid.Column="4" x:Name="TopMenu" Grid.Column="1"
IsEditButtonChecked="{Binding IsEditMode, Mode=TwoWay}"
MoveButtonVisibility="{Binding IsCurrentEntry, Converter={StaticResource BooleanToVisibilityConverter}}" MoveButtonVisibility="{Binding IsCurrentEntry, Converter={StaticResource BooleanToVisibilityConverter}}"
RestoreButtonVisibility="{Binding IsCurrentEntry, Converter={StaticResource InverseBooleanToVisibilityConverter}}" RestoreButtonVisibility="{Binding IsCurrentEntry, Converter={StaticResource InverseBooleanToVisibilityConverter}}"
SaveCommand="{Binding SaveCommand}" SaveCommand="{Binding SaveCommand}"
@@ -278,5 +247,30 @@
</interactivity:Interaction.Behaviors> </interactivity:Interaction.Behaviors>
</controls:TopMenuUserControl> </controls:TopMenuUserControl>
</Grid> </Grid>
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="PageLayout">
<VisualState x:Name="Small">
<Storyboard>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="Hub" Storyboard.TargetProperty="Margin">
<DiscreteObjectKeyFrame KeyTime="0" Value="-30,0,0,0"/>
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
<VisualState x:Name="Medium">
<Storyboard>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="Hub" Storyboard.TargetProperty="Margin">
<DiscreteObjectKeyFrame KeyTime="0" Value="0,0,0,0"/>
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
<VisualState x:Name="Large">
<Storyboard>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="Hub" Storyboard.TargetProperty="Margin">
<DiscreteObjectKeyFrame KeyTime="0" Value="0,0,0,0"/>
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
</Grid> </Grid>
</Page> </Page>

View File

@@ -28,7 +28,6 @@ namespace ModernKeePass.Views
if (args != null) if (args != null)
{ {
await Model.Initialize(args.Id); await Model.Initialize(args.Id);
Model.IsEditMode = args.IsNew;
} }
} }
@@ -46,18 +45,21 @@ namespace ModernKeePass.Views
VisualStateManager.GoToState(this, "Small", true); VisualStateManager.GoToState(this, "Small", true);
VisualStateManager.GoToState(TopMenu, "Collapsed", true); VisualStateManager.GoToState(TopMenu, "Collapsed", true);
VisualStateManager.GoToState(HamburgerMenu, "Hidden", true); VisualStateManager.GoToState(HamburgerMenu, "Hidden", true);
VisualStateManager.GoToState(Breadcrumb, "Small", true);
} }
else if (e.NewSize.Width > 640 && e.NewSize.Width <= 1008) else if (e.NewSize.Width > 640 && e.NewSize.Width <= 1008)
{ {
VisualStateManager.GoToState(this, "Medium", true); VisualStateManager.GoToState(this, "Medium", true);
VisualStateManager.GoToState(TopMenu, "Overflowed", true); VisualStateManager.GoToState(TopMenu, "Overflowed", true);
VisualStateManager.GoToState(HamburgerMenu, "Collapsed", true); VisualStateManager.GoToState(HamburgerMenu, "Collapsed", true);
VisualStateManager.GoToState(Breadcrumb, "Medium", true);
} }
else else
{ {
VisualStateManager.GoToState(this, "Large", true); VisualStateManager.GoToState(this, "Large", true);
VisualStateManager.GoToState(TopMenu, "Overflowed", true); VisualStateManager.GoToState(TopMenu, "Overflowed", true);
VisualStateManager.GoToState(HamburgerMenu, "Collapsed", true); VisualStateManager.GoToState(HamburgerMenu, "Collapsed", true);
VisualStateManager.GoToState(Breadcrumb, "Large", true);
} }
} }
} }

View File

@@ -1,4 +1,4 @@
<Page <Page x:Name="Page"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
@@ -15,7 +15,6 @@
<Page.Resources> <Page.Resources>
<converters:ColorToBrushConverter x:Key="ColorToBrushConverter"/> <converters:ColorToBrushConverter x:Key="ColorToBrushConverter"/>
<converters:BooleanToVisibilityConverter x:Key="BooleanToVisibilityConverter"/> <converters:BooleanToVisibilityConverter x:Key="BooleanToVisibilityConverter"/>
<converters:InverseBooleanToVisibilityConverter x:Key="InverseBooleanToVisibilityConverter"/>
<converters:IconToSymbolConverter x:Key="IconToSymbolConverter"/> <converters:IconToSymbolConverter x:Key="IconToSymbolConverter"/>
</Page.Resources> </Page.Resources>
<Grid> <Grid>
@@ -41,14 +40,6 @@
<ColumnDefinition Width="Auto" /> <ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" /> <ColumnDefinition Width="*" />
</Grid.ColumnDefinitions> </Grid.ColumnDefinitions>
<userControls:HamburgerMenuUserControl
x:Name="HamburgerMenu"
x:Uid="GroupsLeftListView"
ItemsSource="{Binding Groups}"
CanDragItems="{Binding IsEditMode}"
SelectionChanged="groups_SelectionChanged"
ActionButtonCommand="{Binding CreateGroupCommand}"
IsButtonVisible="Visible" />
<Grid Grid.Column="1"> <Grid Grid.Column="1">
<Grid.ColumnDefinitions> <Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" /> <ColumnDefinition Width="Auto" />
@@ -71,7 +62,7 @@
</StackPanel> </StackPanel>
</HyperlinkButton> </HyperlinkButton>
<SemanticZoom Grid.Column="0" Grid.ColumnSpan="3" Grid.Row="1" ViewChangeStarted="SemanticZoom_ViewChangeStarted" ScrollViewer.HorizontalScrollBarVisibility="Visible"> <SemanticZoom x:Name="SemanticZoom" Grid.Column="0" Grid.ColumnSpan="3" Grid.Row="1" ViewChangeStarted="SemanticZoom_ViewChangeStarted" ScrollViewer.HorizontalScrollBarVisibility="Visible">
<SemanticZoom.ZoomedInView> <SemanticZoom.ZoomedInView>
<!-- Horizontal scrolling grid --> <!-- Horizontal scrolling grid -->
<GridView <GridView
@@ -80,7 +71,6 @@
AutomationProperties.AutomationId="ItemGridView" AutomationProperties.AutomationId="ItemGridView"
AutomationProperties.Name="Entries" AutomationProperties.Name="Entries"
TabIndex="1" TabIndex="1"
SelectionChanged="entries_SelectionChanged"
IsSynchronizedWithCurrentItem="False" IsSynchronizedWithCurrentItem="False"
BorderBrush="{StaticResource ListViewItemSelectedBackgroundThemeBrush}" BorderBrush="{StaticResource ListViewItemSelectedBackgroundThemeBrush}"
AllowDrop="True" AllowDrop="True"
@@ -88,8 +78,11 @@
CanDragItems="{Binding IsEditMode}"> CanDragItems="{Binding IsEditMode}">
<interactivity:Interaction.Behaviors> <interactivity:Interaction.Behaviors>
<core:DataTriggerBehavior Binding="{Binding IsEditMode}" Value="False"> <core:DataTriggerBehavior Binding="{Binding IsEditMode}" Value="False">
<actions:SetupFocusAction TargetObject="{Binding}" /> <actions:SetupFocusAction TargetObject="{Binding ElementName=GridView}" />
</core:DataTriggerBehavior> </core:DataTriggerBehavior>
<core:EventTriggerBehavior EventName="SelectionChanged">
<core:InvokeCommandAction Command="{Binding GoToEntryCommand}" CommandParameter="{Binding SelectedItem, ElementName=GridView}" />
</core:EventTriggerBehavior>
</interactivity:Interaction.Behaviors> </interactivity:Interaction.Behaviors>
<GridView.ItemTemplate> <GridView.ItemTemplate>
<DataTemplate> <DataTemplate>
@@ -137,6 +130,7 @@
</core:EventTriggerBehavior> </core:EventTriggerBehavior>
</interactivity:Interaction.Behaviors> </interactivity:Interaction.Behaviors>
</MenuFlyoutItem> </MenuFlyoutItem>
<MenuFlyoutItem x:Uid="EntryItemDelete" Command="{Binding DataContext.DeleteEntryCommand, ElementName=Page}" CommandParameter="{Binding}" />
</MenuFlyout> </MenuFlyout>
</Button.Flyout> </Button.Flyout>
</Button> </Button>
@@ -176,64 +170,32 @@
</SemanticZoom.ZoomedOutView> </SemanticZoom.ZoomedOutView>
</SemanticZoom> </SemanticZoom>
</Grid> </Grid>
<userControls:HamburgerMenuUserControl
Grid.Column="0"
x:Name="HamburgerMenu"
x:Uid="GroupsLeftListView"
HeaderLabel="{Binding Title}"
ItemsSource="{Binding Groups}"
CanDragItems="{Binding IsEditMode}"
ActionButtonCommand="{Binding CreateGroupCommand}"
IsButtonVisible="Visible">
<interactivity:Interaction.Behaviors>
<core:EventTriggerBehavior EventName="SelectionChanged">
<core:InvokeCommandAction Command="{Binding GoToGroupCommand}" CommandParameter="{Binding SelectedItem, ElementName=HamburgerMenu}" />
</core:EventTriggerBehavior>
</interactivity:Interaction.Behaviors>
</userControls:HamburgerMenuUserControl>
</Grid> </Grid>
<!-- Back button and page title --> <!-- Back button and page title -->
<Grid Grid.Row="0" Background="{ThemeResource AppBarBackgroundThemeBrush}"> <Grid Grid.Row="0" Background="{ThemeResource AppBarBackgroundThemeBrush}">
<Grid.ColumnDefinitions> <Grid.ColumnDefinitions>
<ColumnDefinition Width="{StaticResource MenuWidthGridLength}"/>
<ColumnDefinition Width="{StaticResource MenuWidthGridLength}"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/> <ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/> <ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions> </Grid.ColumnDefinitions>
<Button Grid.Column="0" <userControls:BreadcrumbUserControl x:Name="Breadcrumb" Group="{Binding Parent}" />
Command="{Binding GoBackCommand}" <userControls:TopMenuUserControl x:Name="TopMenu" Grid.Column="1"
Height="{StaticResource MenuHeight}"
Width="{StaticResource MenuWidth}"
AutomationProperties.Name="Back"
AutomationProperties.AutomationId="BackButton"
AutomationProperties.ItemType="Navigation Button"
Style="{StaticResource NoBorderButtonStyle}">
<SymbolIcon Symbol="Back" />
</Button>
<Button Grid.Column="1"
Height="{StaticResource MenuHeight}"
Width="{StaticResource MenuWidth}"
Command="{Binding GoToParentCommand}"
Style="{StaticResource NoBorderButtonStyle}">
<SymbolIcon Symbol="Up" />
<ToolTipService.ToolTip>
<ToolTip Content="{Binding ParentGroupName}" />
</ToolTipService.ToolTip>
</Button>
<Viewbox Grid.Column="2" MaxHeight="200" Visibility="{Binding IsEditMode, Converter={StaticResource BooleanToVisibilityConverter}}">
<userControls:SymbolPickerUserControl Width="100" Height="70" SelectedSymbol="{Binding Icon, Mode=TwoWay}" />
</Viewbox>
<Viewbox Grid.Column="2" MaxHeight="200" Visibility="{Binding IsEditMode, Converter={StaticResource InverseBooleanToVisibilityConverter}}">
<SymbolIcon Symbol="{Binding Icon}" Width="100" Height="70" />
</Viewbox>
<TextBox Grid.Column="3"
x:Uid="GroupTitle"
x:Name="TitleTextBox"
Text="{Binding Title, Mode=TwoWay}"
Background="Transparent"
IsHitTestVisible="{Binding IsEditMode}"
FontSize="20"
FontWeight="Light"
TextWrapping="NoWrap"
VerticalAlignment="Center">
<interactivity:Interaction.Behaviors>
<core:DataTriggerBehavior Binding="{Binding IsEditMode}" Value="True">
<actions:SetupFocusAction TargetObject="{Binding ElementName=TitleTextBox}" />
<core:ChangePropertyAction TargetObject="{Binding ElementName=TitleTextBox}" PropertyName="BorderThickness" Value="2" />
</core:DataTriggerBehavior>
<core:DataTriggerBehavior Binding="{Binding IsEditMode}" Value="False">
<core:ChangePropertyAction TargetObject="{Binding ElementName=TitleTextBox}" PropertyName="BorderThickness" Value="0" />
</core:DataTriggerBehavior>
</interactivity:Interaction.Behaviors>
</TextBox>
<userControls:TopMenuUserControl x:Name="TopMenu" Grid.Column="4"
SortButtonVisibility="{Binding IsEditMode, Converter={StaticResource BooleanToVisibilityConverter}}" SortButtonVisibility="{Binding IsEditMode, Converter={StaticResource BooleanToVisibilityConverter}}"
EditButtonVisibility="Visible"
IsEditButtonChecked="{Binding IsEditMode, Mode=TwoWay}" IsEditButtonChecked="{Binding IsEditMode, Mode=TwoWay}"
SaveCommand="{Binding SaveCommand}" SaveCommand="{Binding SaveCommand}"
MoveCommand="{Binding MoveCommand}" MoveCommand="{Binding MoveCommand}"
@@ -246,7 +208,6 @@
</core:EventTriggerBehavior> </core:EventTriggerBehavior>
</interactivity:Interaction.Behaviors> </interactivity:Interaction.Behaviors>
</userControls:TopMenuUserControl> </userControls:TopMenuUserControl>
</Grid> </Grid>
<VisualStateManager.VisualStateGroups> <VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="PageLayout"> <VisualStateGroup x:Name="PageLayout">
@@ -258,6 +219,9 @@
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="HamburgerMenu" Storyboard.TargetProperty="IsOpen"> <ObjectAnimationUsingKeyFrames Storyboard.TargetName="HamburgerMenu" Storyboard.TargetProperty="IsOpen">
<DiscreteObjectKeyFrame KeyTime="0" Value="False"/> <DiscreteObjectKeyFrame KeyTime="0" Value="False"/>
</ObjectAnimationUsingKeyFrames> </ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="SemanticZoom" Storyboard.TargetProperty="Margin">
<DiscreteObjectKeyFrame KeyTime="0" Value="-60,0,0,0"/>
</ObjectAnimationUsingKeyFrames>
</Storyboard> </Storyboard>
</VisualState> </VisualState>
<VisualState x:Name="Medium"> <VisualState x:Name="Medium">
@@ -265,6 +229,9 @@
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="HamburgerMenu" Storyboard.TargetProperty="IsOpen"> <ObjectAnimationUsingKeyFrames Storyboard.TargetName="HamburgerMenu" Storyboard.TargetProperty="IsOpen">
<DiscreteObjectKeyFrame KeyTime="0" Value="False"/> <DiscreteObjectKeyFrame KeyTime="0" Value="False"/>
</ObjectAnimationUsingKeyFrames> </ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="SemanticZoom" Storyboard.TargetProperty="Margin">
<DiscreteObjectKeyFrame KeyTime="0" Value="0,0,0,0"/>
</ObjectAnimationUsingKeyFrames>
</Storyboard> </Storyboard>
</VisualState> </VisualState>
<VisualState x:Name="Large"> <VisualState x:Name="Large">
@@ -275,6 +242,9 @@
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="HamburgerMenu" Storyboard.TargetProperty="IsOpen"> <ObjectAnimationUsingKeyFrames Storyboard.TargetName="HamburgerMenu" Storyboard.TargetProperty="IsOpen">
<DiscreteObjectKeyFrame KeyTime="0" Value="True"/> <DiscreteObjectKeyFrame KeyTime="0" Value="True"/>
</ObjectAnimationUsingKeyFrames> </ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="SemanticZoom" Storyboard.TargetProperty="Margin">
<DiscreteObjectKeyFrame KeyTime="0" Value="0,0,0,0"/>
</ObjectAnimationUsingKeyFrames>
</Storyboard> </Storyboard>
</VisualState> </VisualState>
</VisualStateGroup> </VisualStateGroup>

View File

@@ -1,7 +1,6 @@
using Windows.UI.Xaml; using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls; using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Navigation; using Windows.UI.Xaml.Navigation;
using ModernKeePass.Application.Entry.Models;
using ModernKeePass.Models; using ModernKeePass.Models;
using ModernKeePass.ViewModels; using ModernKeePass.ViewModels;
@@ -38,33 +37,6 @@ namespace ModernKeePass.Views
#region Event Handlers #region Event Handlers
private void groups_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
var listView = sender as ListView;
switch (listView?.SelectedIndex)
{
case -1:
return;
default:
var group = listView?.SelectedItem as Application.Group.Models.GroupVm;
Model.GoToGroup(group?.Id);
break;
}
}
private void entries_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
switch (GridView.SelectedIndex)
{
case -1:
return;
default:
var entry = GridView.SelectedItem as EntryVm;
Model.GoToEntry(entry?.Id);
break;
}
}
private void SemanticZoom_ViewChangeStarted(object sender, SemanticZoomViewChangedEventArgs e) private void SemanticZoom_ViewChangeStarted(object sender, SemanticZoomViewChangedEventArgs e)
{ {
// We need to synchronize the two lists (zoomed-in and zoomed-out) because the source is different // We need to synchronize the two lists (zoomed-in and zoomed-out) because the source is different
@@ -81,18 +53,21 @@ namespace ModernKeePass.Views
VisualStateManager.GoToState(this, "Small", true); VisualStateManager.GoToState(this, "Small", true);
VisualStateManager.GoToState(TopMenu, "Collapsed", true); VisualStateManager.GoToState(TopMenu, "Collapsed", true);
VisualStateManager.GoToState(HamburgerMenu, "Hidden", true); VisualStateManager.GoToState(HamburgerMenu, "Hidden", true);
VisualStateManager.GoToState(Breadcrumb, "Small", true);
} }
else if (e.NewSize.Width > 640 && e.NewSize.Width <= 1008) else if (e.NewSize.Width > 640 && e.NewSize.Width <= 1008)
{ {
VisualStateManager.GoToState(this, "Medium", true); VisualStateManager.GoToState(this, "Medium", true);
VisualStateManager.GoToState(TopMenu, "Overflowed", true); VisualStateManager.GoToState(TopMenu, "Overflowed", true);
VisualStateManager.GoToState(HamburgerMenu, "Collapsed", true); VisualStateManager.GoToState(HamburgerMenu, "Collapsed", true);
VisualStateManager.GoToState(Breadcrumb, "Medium", true);
} }
else else
{ {
VisualStateManager.GoToState(this, "Large", true); VisualStateManager.GoToState(this, "Large", true);
VisualStateManager.GoToState(TopMenu, "Overflowed", true); VisualStateManager.GoToState(TopMenu, "Overflowed", true);
VisualStateManager.GoToState(HamburgerMenu, "Expanded", true); VisualStateManager.GoToState(HamburgerMenu, "Expanded", true);
VisualStateManager.GoToState(Breadcrumb, "Large", true);
} }
} }

View File

@@ -57,6 +57,7 @@
Grid.Column="0" Grid.Column="0"
Grid.Row="1" Grid.Row="1"
x:Name="MenuListView" x:Name="MenuListView"
ScrollViewer.VerticalScrollMode="Disabled"
SelectionChanged="ListView_SelectionChanged" SelectionChanged="ListView_SelectionChanged"
Background="{ThemeResource AppBarBackgroundThemeBrush}" Background="{ThemeResource AppBarBackgroundThemeBrush}"
ItemsSource="{Binding Source={StaticResource MenuItemsSource}}" ItemsSource="{Binding Source={StaticResource MenuItemsSource}}"

View File

@@ -40,7 +40,11 @@ namespace ModernKeePass.Views
protected override async void OnNavigatedTo(NavigationEventArgs e) protected override async void OnNavigatedTo(NavigationEventArgs e)
{ {
base.OnNavigatedTo(e); base.OnNavigatedTo(e);
var file = e.Parameter as FileInfo; FileInfo file;
if (e.NavigationMode == NavigationMode.Back)
file = null;
else
file = e.Parameter as FileInfo;
await Model.Initialize(Frame, MenuFrame, file); await Model.Initialize(Frame, MenuFrame, file);
} }
} }

View File

@@ -1,5 +1,5 @@
<Page <Page
x:Class="ModernKeePass.Views.ImportExportPage" x:Class="ModernKeePass.Views.ImportPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:d="http://schemas.microsoft.com/expression/blend/2008"

View File

@@ -5,9 +5,9 @@ namespace ModernKeePass.Views
/// <summary> /// <summary>
/// The import/export page. /// The import/export page.
/// </summary> /// </summary>
public sealed partial class ImportExportPage public sealed partial class ImportPage
{ {
public ImportExportPage() public ImportPage()
{ {
InitializeComponent(); InitializeComponent();
} }

View File

@@ -9,6 +9,11 @@
mc:Ignorable="d" mc:Ignorable="d"
DataContext="{Binding Source={StaticResource Locator}, Path=Recent}"> DataContext="{Binding Source={StaticResource Locator}, Path=Recent}">
<Grid Background="{StaticResource ApplicationPageBackgroundThemeBrush}"> <Grid Background="{StaticResource ApplicationPageBackgroundThemeBrush}">
<Grid.Resources>
<CollectionViewSource
x:Name="RecentItemsSource"
Source="{Binding RecentItems}" />
</Grid.Resources>
<Grid.RowDefinitions> <Grid.RowDefinitions>
<RowDefinition Height="40" /> <RowDefinition Height="40" />
<RowDefinition Height="*" /> <RowDefinition Height="*" />
@@ -21,7 +26,7 @@
</HyperlinkButton> </HyperlinkButton>
<controls:SelectableTemplateListView Grid.Row="1" <controls:SelectableTemplateListView Grid.Row="1"
SelectedIndex="{Binding SelectedIndex}" SelectedIndex="{Binding SelectedIndex}"
ItemsSource="{Binding RecentItems}" ItemsSource="{Binding Source={StaticResource RecentItemsSource}}"
ItemContainerStyle="{StaticResource ListViewLeftIndicatorItemExpanded}"> ItemContainerStyle="{StaticResource ListViewLeftIndicatorItemExpanded}">
<controls:SelectableTemplateListView.SelectedItemTemplate> <controls:SelectableTemplateListView.SelectedItemTemplate>
<DataTemplate> <DataTemplate>

View File

@@ -0,0 +1,117 @@
<UserControl
x:Class="ModernKeePass.Views.UserControls.BreadcrumbUserControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
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"
xmlns:converters="using:ModernKeePass.Converters"
xmlns:templateSelectors="using:ModernKeePass.TemplateSelectors"
xmlns:interactivity="using:Microsoft.Xaml.Interactivity"
xmlns:core="using:Microsoft.Xaml.Interactions.Core"
mc:Ignorable="d">
<StackPanel x:Name="StackPanel" DataContext="{Binding Source={StaticResource Locator}, Path=Breadcrumb}" Orientation="Horizontal">
<StackPanel.Resources>
<converters:IconToSymbolConverter x:Key="IconToSymbolConverter"/>
<DataTemplate x:Key="FirstItemTemplate">
<StackPanel Orientation="Horizontal">
<TextBlock Margin="10,5,10,0" Text="{Binding Name}" FontStyle="Italic" HorizontalAlignment="Center" />
<SymbolIcon Symbol="{Binding Icon, Converter={StaticResource IconToSymbolConverter}, ConverterParameter=33}" Margin="0,3,0,0" />
</StackPanel>
</DataTemplate>
<DataTemplate x:Key="OtherItemTemplate">
<StackPanel Orientation="Horizontal">
<TextBlock Margin="10,5,10,0" Text="&lt;" HorizontalAlignment="Center" />
<TextBlock Margin="10,5,10,0" Text="{Binding Name}" FontStyle="Italic" HorizontalAlignment="Center" />
<SymbolIcon Symbol="{Binding Icon, Converter={StaticResource IconToSymbolConverter}, ConverterParameter=33}" Margin="0,3,0,0" />
</StackPanel>
</DataTemplate>
</StackPanel.Resources>
<Button
Command="{Binding GoBackCommand}"
Height="{StaticResource MenuHeight}"
Width="{StaticResource MenuWidth}"
AutomationProperties.Name="Back"
AutomationProperties.AutomationId="BackButton"
AutomationProperties.ItemType="Navigation Button"
Style="{StaticResource NoBorderButtonStyle}">
<SymbolIcon Symbol="Back" />
</Button>
<Button
Height="{StaticResource MenuHeight}"
Command="{Binding GoUpCommand}"
Style="{StaticResource NoBorderButtonStyle}">
<StackPanel Orientation="Horizontal">
<SymbolIcon Symbol="Up" />
<SymbolIcon x:Name="UpButtonIcon" Symbol="{Binding ParentGroupIcon, Converter={StaticResource IconToSymbolConverter}, ConverterParameter=33}" Margin="16,0,0,0" />
<TextBlock x:Name="UpButtonText" Margin="10,2,0,0" Text="{Binding ParentGroupName}" FontStyle="Italic" FontWeight="Normal" HorizontalAlignment="Center" />
</StackPanel>
<ToolTipService.ToolTip>
<ToolTip Content="{Binding ParentGroupName}" />
</ToolTipService.ToolTip>
</Button>
<ListView x:Name="ListView" ItemsSource="{Binding BreadcrumbItems}" ScrollViewer.HorizontalScrollMode="Auto">
<ListView.ItemsPanel>
<ItemsPanelTemplate>
<ItemsStackPanel FlowDirection="RightToLeft" Orientation="Horizontal" />
</ItemsPanelTemplate>
</ListView.ItemsPanel>
<ListView.ItemTemplateSelector>
<templateSelectors:FirstItemDataTemplateSelector FirstItem="{StaticResource FirstItemTemplate}" OtherItem="{StaticResource OtherItemTemplate}" />
</ListView.ItemTemplateSelector>
<ListView.ItemContainerStyle>
<Style TargetType="ListViewItem">
<Setter Property="Margin" Value="0"/>
<Setter Property="Height" Value="{StaticResource MenuHeight}" />
</Style>
</ListView.ItemContainerStyle>
<interactivity:Interaction.Behaviors>
<core:EventTriggerBehavior EventName="SelectionChanged">
<core:InvokeCommandAction Command="{Binding GoToCommand}" CommandParameter="{Binding SelectedIndex, ElementName=ListView}" />
</core:EventTriggerBehavior>
</interactivity:Interaction.Behaviors>
</ListView>
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="PageLayout">
<VisualState x:Name="Small">
<Storyboard>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="UpButtonIcon" Storyboard.TargetProperty="Visibility">
<DiscreteObjectKeyFrame KeyTime="0" Value="Visible"/>
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="UpButtonText" Storyboard.TargetProperty="Visibility">
<DiscreteObjectKeyFrame KeyTime="0" Value="Collapsed"/>
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="ListView" Storyboard.TargetProperty="Visibility">
<DiscreteObjectKeyFrame KeyTime="0" Value="Collapsed"/>
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
<VisualState x:Name="Medium">
<Storyboard>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="UpButtonIcon" Storyboard.TargetProperty="Visibility">
<DiscreteObjectKeyFrame KeyTime="0" Value="Visible"/>
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="UpButtonText" Storyboard.TargetProperty="Visibility">
<DiscreteObjectKeyFrame KeyTime="0" Value="Visible"/>
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="ListView" Storyboard.TargetProperty="Visibility">
<DiscreteObjectKeyFrame KeyTime="0" Value="Collapsed"/>
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
<VisualState x:Name="Large">
<Storyboard>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="UpButtonIcon" Storyboard.TargetProperty="Visibility">
<DiscreteObjectKeyFrame KeyTime="0" Value="Collapsed"/>
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="UpButtonText" Storyboard.TargetProperty="Visibility">
<DiscreteObjectKeyFrame KeyTime="0" Value="Collapsed"/>
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="ListView" Storyboard.TargetProperty="Visibility">
<DiscreteObjectKeyFrame KeyTime="0" Value="Visible"/>
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
</StackPanel>
</UserControl>

View File

@@ -0,0 +1,36 @@
using System.Linq;
using Windows.UI.Xaml;
using ModernKeePass.Application.Group.Models;
using ModernKeePass.ViewModels;
// The User Control item template is documented at http://go.microsoft.com/fwlink/?LinkId=234236
namespace ModernKeePass.Views.UserControls
{
public sealed partial class BreadcrumbUserControl
{
public GroupVm Group
{
get { return (GroupVm)GetValue(GroupProperty); }
set { SetValue(GroupProperty, value); }
}
public static readonly DependencyProperty GroupProperty =
DependencyProperty.Register(
nameof(Group),
typeof(GroupVm),
typeof(BreadcrumbUserControl),
new PropertyMetadata(null, async (o, args) =>
{
var userControl = o as BreadcrumbUserControl;
var vm = userControl?.StackPanel.DataContext as BreadcrumbControlVm;
if (vm == null) return;
await vm.Initialize(args.NewValue as GroupVm).ConfigureAwait(false);
userControl?.ListView.ScrollIntoView(vm.BreadcrumbItems.Last());
}));
public BreadcrumbUserControl()
{
InitializeComponent();
}
}
}

View File

@@ -1,4 +1,5 @@
using Windows.UI.Xaml; using System.Linq;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls; using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Media; using Windows.UI.Xaml.Media;
using ModernKeePass.ViewModels; using ModernKeePass.ViewModels;
@@ -33,7 +34,8 @@ namespace ModernKeePass.Views.UserControls
private void ComboBox_OnSelectionChanged(object sender, SelectionChangedEventArgs e) private void ComboBox_OnSelectionChanged(object sender, SelectionChangedEventArgs e)
{ {
SelectedColor = (e.AddedItems[0] as ColorPickerControlVm.Color)?.ColorBrush; if (e.AddedItems.Any())
SelectedColor = (e.AddedItems[0] as ColorPickerControlVm.Color)?.ColorBrush;
} }
} }
} }

View File

@@ -60,7 +60,10 @@
<RowDefinition Height="*" /> <RowDefinition Height="*" />
</Grid.RowDefinitions> </Grid.RowDefinitions>
<StackPanel Orientation="Horizontal"> <StackPanel Orientation="Horizontal">
<ToggleButton Style="{StaticResource HamburgerToggleButton}" IsChecked="{Binding IsOpen, ElementName=UserControl}" Unchecked="ToggleButton_OnUnchecked"> <ToggleButton Style="{StaticResource HamburgerToggleButton}" IsChecked="{Binding IsOpen, ElementName=UserControl, Mode=TwoWay}" Unchecked="ToggleButton_OnUnchecked">
<ToolTipService.ToolTip>
<ToolTip Content="{Binding HeaderLabel, ElementName=UserControl}" />
</ToolTipService.ToolTip>
<interactivity:Interaction.Behaviors> <interactivity:Interaction.Behaviors>
<core:EventTriggerBehavior EventName="Checked"> <core:EventTriggerBehavior EventName="Checked">
<core:GoToStateAction StateName="Expanded" /> <core:GoToStateAction StateName="Expanded" />
@@ -94,7 +97,7 @@
<ListView.ItemTemplate> <ListView.ItemTemplate>
<DataTemplate x:Name="IsNormal"> <DataTemplate x:Name="IsNormal">
<StackPanel Orientation="Horizontal"> <StackPanel Orientation="Horizontal">
<SymbolIcon Symbol="{Binding Icon, Converter={StaticResource IconToSymbolConverter}, ConverterParameter=48}" Margin="7,10,0,15"> <SymbolIcon Symbol="{Binding Icon, Converter={StaticResource IconToSymbolConverter}, ConverterParameter=33}" Margin="7,10,0,15">
<ToolTipService.ToolTip> <ToolTipService.ToolTip>
<ToolTip Content="{Binding Path={Binding DisplayMemberPath, ElementName=UserControl}}" /> <ToolTip Content="{Binding Path={Binding DisplayMemberPath, ElementName=UserControl}}" />
</ToolTipService.ToolTip> </ToolTipService.ToolTip>
@@ -125,6 +128,7 @@
</StackPanel> </StackPanel>
<interactivity:Interaction.Behaviors> <interactivity:Interaction.Behaviors>
<core:EventTriggerBehavior EventName="Click"> <core:EventTriggerBehavior EventName="Click">
<core:ChangePropertyAction TargetObject="{Binding ElementName=UserControl}" PropertyName="IsOpen" Value="True" />
<core:ChangePropertyAction TargetObject="{Binding ElementName=NewGroupTextBox}" PropertyName="Visibility" Value="Visible" /> <core:ChangePropertyAction TargetObject="{Binding ElementName=NewGroupTextBox}" PropertyName="Visibility" Value="Visible" />
<core:ChangePropertyAction TargetObject="{Binding ElementName=NewGroupButton}" PropertyName="Visibility" Value="Collapsed" /> <core:ChangePropertyAction TargetObject="{Binding ElementName=NewGroupButton}" PropertyName="Visibility" Value="Collapsed" />
<actions:SetupFocusAction TargetObject="{Binding ElementName=NewGroupTextBox}" /> <actions:SetupFocusAction TargetObject="{Binding ElementName=NewGroupTextBox}" />

View File

@@ -1,5 +1,4 @@
using System; using System;
using System.Collections.Generic;
using System.Windows.Input; using System.Windows.Input;
using Windows.System; using Windows.System;
using Windows.UI.Xaml; using Windows.UI.Xaml;
@@ -7,6 +6,7 @@ using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Input; using Windows.UI.Xaml.Input;
using ModernKeePass.Application.Common.Interfaces; using ModernKeePass.Application.Common.Interfaces;
using ModernKeePass.Controls; using ModernKeePass.Controls;
using System.Collections.ObjectModel;
// The User Control item template is documented at http://go.microsoft.com/fwlink/?LinkId=234236 // The User Control item template is documented at http://go.microsoft.com/fwlink/?LinkId=234236
@@ -62,18 +62,18 @@ namespace ModernKeePass.Views.UserControls
typeof(HamburgerMenuUserControl), typeof(HamburgerMenuUserControl),
new PropertyMetadata(Visibility.Collapsed, (o, args) => { })); new PropertyMetadata(Visibility.Collapsed, (o, args) => { }));
public IEnumerable<IEntityVm> ItemsSource public ObservableCollection<IEntityVm> ItemsSource
{ {
get { return (IEnumerable<IEntityVm>)GetValue(ItemsSourceProperty); } get { return (ObservableCollection<IEntityVm>)GetValue(ItemsSourceProperty); }
set { SetValue(ItemsSourceProperty, value); } set { SetValue(ItemsSourceProperty, value); }
} }
public static readonly DependencyProperty ItemsSourceProperty = public static readonly DependencyProperty ItemsSourceProperty =
DependencyProperty.Register( DependencyProperty.Register(
nameof(ItemsSource), nameof(ItemsSource),
typeof(IEnumerable<IEntityVm>), typeof(ObservableCollection<IEntityVm>),
typeof(HamburgerMenuUserControl), typeof(HamburgerMenuUserControl),
new PropertyMetadata(new List<IEntityVm>(), (o, args) => { })); new PropertyMetadata(new ObservableCollection<IEntityVm>(), (o, args) => { }));
public object SelectedItem public object SelectedItem
{ {

View File

@@ -98,7 +98,7 @@
</MenuFlyout> </MenuFlyout>
</Button.Flyout> </Button.Flyout>
</Button> </Button>
<ToggleButton Command="{Binding EditCommand, ElementName=UserControl}" IsChecked="{Binding IsEditButtonChecked, ElementName=UserControl, Mode=TwoWay}" Checked="EditButton_Click" Style="{StaticResource MenuToggleButtonStyle}"> <ToggleButton Visibility="{Binding EditButtonVisibility, ElementName=UserControl}" Command="{Binding EditCommand, ElementName=UserControl}" IsChecked="{Binding IsEditButtonChecked, ElementName=UserControl, Mode=TwoWay}" Checked="EditButton_Click" Style="{StaticResource MenuToggleButtonStyle}">
<SymbolIcon Symbol="Edit"> <SymbolIcon Symbol="Edit">
<ToolTipService.ToolTip> <ToolTipService.ToolTip>
<ToolTip x:Uid="TopMenuEditButton" /> <ToolTip x:Uid="TopMenuEditButton" />
@@ -151,7 +151,7 @@
<MenuFlyout Opening="OverflowFlyout_OnOpening"> <MenuFlyout Opening="OverflowFlyout_OnOpening">
<MenuFlyoutItem x:Uid="TopMenuSaveFlyout" x:Name="SaveFlyout" /> <MenuFlyoutItem x:Uid="TopMenuSaveFlyout" x:Name="SaveFlyout" />
<MenuFlyoutItem x:Uid="TopMenuRestoreFlyout" x:Name="RestoreFlyout" Visibility="{Binding RestoreButtonVisibility, ElementName=UserControl}" /> <MenuFlyoutItem x:Uid="TopMenuRestoreFlyout" x:Name="RestoreFlyout" Visibility="{Binding RestoreButtonVisibility, ElementName=UserControl}" />
<ToggleMenuFlyoutItem x:Uid="TopMenuEditFlyout" x:Name="EditFlyout" IsChecked="{Binding IsEditButtonChecked, ElementName=UserControl, Mode=TwoWay}" Click="EditButton_Click" /> <ToggleMenuFlyoutItem x:Uid="TopMenuEditFlyout" x:Name="EditFlyout" Visibility="{Binding EditButtonVisibility, ElementName=UserControl}" IsChecked="{Binding IsEditButtonChecked, ElementName=UserControl, Mode=TwoWay}" Click="EditButton_Click" />
<MenuFlyoutItem x:Uid="TopMenuDeleteFlyout" x:Name="DeleteFlyout" /> <MenuFlyoutItem x:Uid="TopMenuDeleteFlyout" x:Name="DeleteFlyout" />
<MenuFlyoutItem x:Uid="TopMenuSortEntriesFlyout" x:Name="SortEntriesFlyout" Visibility="{Binding SortButtonVisibility, ElementName=UserControl}" /> <MenuFlyoutItem x:Uid="TopMenuSortEntriesFlyout" x:Name="SortEntriesFlyout" Visibility="{Binding SortButtonVisibility, ElementName=UserControl}" />
<MenuFlyoutItem x:Uid="TopMenuSortGroupsFlyout" x:Name="SortGroupsFlyout" Visibility="{Binding SortButtonVisibility, ElementName=UserControl}" /> <MenuFlyoutItem x:Uid="TopMenuSortGroupsFlyout" x:Name="SortGroupsFlyout" Visibility="{Binding SortButtonVisibility, ElementName=UserControl}" />

View File

@@ -39,6 +39,18 @@ namespace ModernKeePass.Views.UserControls
typeof(TopMenuUserControl), typeof(TopMenuUserControl),
new PropertyMetadata(null, (o, args) => { })); new PropertyMetadata(null, (o, args) => { }));
public Visibility EditButtonVisibility
{
get { return (Visibility)GetValue(EditButtonVisibilityProperty); }
set { SetValue(EditButtonVisibilityProperty, value); }
}
public static readonly DependencyProperty EditButtonVisibilityProperty =
DependencyProperty.Register(
nameof(EditButtonVisibility),
typeof(Visibility),
typeof(TopMenuUserControl),
new PropertyMetadata(Visibility.Collapsed, (o, args) => { }));
public ICommand DeleteCommand public ICommand DeleteCommand
{ {
get { return (ICommand)GetValue(DeleteCommandProperty); } get { return (ICommand)GetValue(DeleteCommandProperty); }
@@ -214,7 +226,6 @@ namespace ModernKeePass.Views.UserControls
MoveCommand.RaiseCanExecuteChanged(); MoveCommand.RaiseCanExecuteChanged();
} }
private async void EntrySearchBox_OnSuggestionsRequested(SearchBox sender, SearchBoxSuggestionsRequestedEventArgs args) private async void EntrySearchBox_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"));

View File

@@ -100,8 +100,8 @@
<DependentUpon>DonatePage.xaml</DependentUpon> <DependentUpon>DonatePage.xaml</DependentUpon>
</Compile> </Compile>
<Compile Include="Views\BasePages\LayoutAwarePageBase.cs" /> <Compile Include="Views\BasePages\LayoutAwarePageBase.cs" />
<Compile Include="Views\MainPageFrames\ImportExportPage.xaml.cs"> <Compile Include="Views\MainPageFrames\ImportPage.xaml.cs">
<DependentUpon>ImportExportPage.xaml</DependentUpon> <DependentUpon>ImportPage.xaml</DependentUpon>
</Compile> </Compile>
<Compile Include="Views\SettingsPageFrames\SettingsHistoryPage.xaml.cs"> <Compile Include="Views\SettingsPageFrames\SettingsHistoryPage.xaml.cs">
<DependentUpon>SettingsHistoryPage.xaml</DependentUpon> <DependentUpon>SettingsHistoryPage.xaml</DependentUpon>
@@ -124,6 +124,9 @@
<Compile Include="Views\SettingsPageFrames\SettingsWelcomePage.xaml.cs"> <Compile Include="Views\SettingsPageFrames\SettingsWelcomePage.xaml.cs">
<DependentUpon>SettingsWelcomePage.xaml</DependentUpon> <DependentUpon>SettingsWelcomePage.xaml</DependentUpon>
</Compile> </Compile>
<Compile Include="Views\UserControls\BreadcrumbUserControl.xaml.cs">
<DependentUpon>BreadcrumbUserControl.xaml</DependentUpon>
</Compile>
<Compile Include="Views\UserControls\ColorPickerUserControl.xaml.cs"> <Compile Include="Views\UserControls\ColorPickerUserControl.xaml.cs">
<DependentUpon>ColorPickerUserControl.xaml</DependentUpon> <DependentUpon>ColorPickerUserControl.xaml</DependentUpon>
</Compile> </Compile>
@@ -189,10 +192,6 @@
<SubType>Designer</SubType> <SubType>Designer</SubType>
</None> </None>
<None Include="Win81App_StoreKey.pfx" /> <None Include="Win81App_StoreKey.pfx" />
<PRIResource Include="Strings\fr-FR\Resources.resw" />
<PRIResource Include="Strings\fr-FR\CodeBehind.resw" />
<PRIResource Include="Strings\en-US\CodeBehind.resw" />
<PRIResource Include="Strings\en-US\Resources.resw" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<None Include="Package.StoreAssociation.xml" /> <None Include="Package.StoreAssociation.xml" />
@@ -207,7 +206,7 @@
<Generator>MSBuild:Compile</Generator> <Generator>MSBuild:Compile</Generator>
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Page> </Page>
<Page Include="Views\MainPageFrames\ImportExportPage.xaml"> <Page Include="Views\MainPageFrames\ImportPage.xaml">
<SubType>Designer</SubType> <SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator> <Generator>MSBuild:Compile</Generator>
</Page> </Page>
@@ -223,6 +222,10 @@
<SubType>Designer</SubType> <SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator> <Generator>MSBuild:Compile</Generator>
</Page> </Page>
<Page Include="Views\UserControls\BreadcrumbUserControl.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>
<Page Include="Views\UserControls\ColorPickerUserControl.xaml"> <Page Include="Views\UserControls\ColorPickerUserControl.xaml">
<SubType>Designer</SubType> <SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator> <Generator>MSBuild:Compile</Generator>

View File

@@ -1,5 +1,2 @@
Data is now protected in memory as well as at rest (Re-)Added a breadcrumb to help with navigation
Improved zoomed-out Semantic view Design improvements
Having a custom entry color doesn't automatically trigger a change
Fix clipboard copy expiration issue
Fix a resume bug

View File

@@ -1,5 +1,2 @@
Protection des donnees en memoire en plus du chiffrement de la base de donnees (R)Ajout du fil d'Ariane pour aider a la navigation
Amelioration du rendu de la vue Semantique dezoomee Ameliorations de design
Avoir une couleur personnalisee ne declenche plus automatiquement un changement
Correction d'un bug dans l'expiration des donnees copiees
Correction d'un bug de resume de l'app

View File

@@ -372,9 +372,6 @@
<data name="GroupsLeftListView.ButtonLabel" xml:space="preserve"> <data name="GroupsLeftListView.ButtonLabel" xml:space="preserve">
<value>New group</value> <value>New group</value>
</data> </data>
<data name="GroupsLeftListView.HeaderLabel" xml:space="preserve">
<value>Groups</value>
</data>
<data name="HistoryLeftListView.HeaderLabel" xml:space="preserve"> <data name="HistoryLeftListView.HeaderLabel" xml:space="preserve">
<value>History</value> <value>History</value>
</data> </data>
@@ -453,9 +450,6 @@
<data name="ToastUpdateDatabase.Title" xml:space="preserve"> <data name="ToastUpdateDatabase.Title" xml:space="preserve">
<value>Composite Key</value> <value>Composite Key</value>
</data> </data>
<data name="EntryTitle.PlaceholderText" xml:space="preserve">
<value>New entry name...</value>
</data>
<data name="SearchButtonLabel.Text" xml:space="preserve"> <data name="SearchButtonLabel.Text" xml:space="preserve">
<value>Search</value> <value>Search</value>
</data> </data>
@@ -570,4 +564,16 @@
<data name="PasswordGenerationButton.ButtonContent" xml:space="preserve"> <data name="PasswordGenerationButton.ButtonContent" xml:space="preserve">
<value>Random</value> <value>Random</value>
</data> </data>
<data name="EntryTitle.Text" xml:space="preserve">
<value>Title</value>
</data>
<data name="TitleTextBox.ButtonTooltip" xml:space="preserve">
<value>Copy title</value>
</data>
<data name="ToastCopyTitle.Message" xml:space="preserve">
<value>Title copied to clipboard</value>
</data>
<data name="EntryItemDelete.Text" xml:space="preserve">
<value>Delete</value>
</data>
</root> </root>

View File

@@ -372,9 +372,6 @@
<data name="GroupsLeftListView.ButtonLabel" xml:space="preserve"> <data name="GroupsLeftListView.ButtonLabel" xml:space="preserve">
<value>Nouveau groupe</value> <value>Nouveau groupe</value>
</data> </data>
<data name="GroupsLeftListView.HeaderLabel" xml:space="preserve">
<value>Groupes</value>
</data>
<data name="HistoryLeftListView.HeaderLabel" xml:space="preserve"> <data name="HistoryLeftListView.HeaderLabel" xml:space="preserve">
<value>Historique</value> <value>Historique</value>
</data> </data>
@@ -453,9 +450,6 @@
<data name="ToastUpdateDatabase.Title" xml:space="preserve"> <data name="ToastUpdateDatabase.Title" xml:space="preserve">
<value>Clé maître</value> <value>Clé maître</value>
</data> </data>
<data name="EntryTitle.PlaceholderText" xml:space="preserve">
<value>Nom de la nouvelle entrée...</value>
</data>
<data name="SearchButtonLabel.Text" xml:space="preserve"> <data name="SearchButtonLabel.Text" xml:space="preserve">
<value>Recherche</value> <value>Recherche</value>
</data> </data>
@@ -570,4 +564,16 @@
<data name="PasswordGenerationButton.ButtonContent" xml:space="preserve"> <data name="PasswordGenerationButton.ButtonContent" xml:space="preserve">
<value>Aléatoire</value> <value>Aléatoire</value>
</data> </data>
<data name="EntryTitle.Text" xml:space="preserve">
<value>Titre</value>
</data>
<data name="TitleTextBox.ButtonTooltip" xml:space="preserve">
<value>Copier le titre</value>
</data>
<data name="ToastCopyTitle.Message" xml:space="preserve">
<value>Titre copié dans le presse-papiers</value>
</data>
<data name="EntryItemDelete.Text" xml:space="preserve">
<value>Supprimer</value>
</data>
</root> </root>

View File

@@ -0,0 +1,18 @@
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
namespace ModernKeePass.TemplateSelectors
{
public class FirstItemDataTemplateSelector : DataTemplateSelector
{
public DataTemplate FirstItem { get; set; }
public DataTemplate OtherItem { get; set; }
protected override DataTemplate SelectTemplateCore(object item, DependencyObject container)
{
var itemsControl = ItemsControl.ItemsControlFromItemContainer(container);
var returnTemplate = itemsControl?.IndexFromContainer(container) == 0 ? FirstItem : OtherItem;
return returnTemplate;
}
}
}

View File

@@ -0,0 +1,62 @@
using System.Collections.ObjectModel;
using System.Threading.Tasks;
using GalaSoft.MvvmLight.Command;
using GalaSoft.MvvmLight.Views;
using MediatR;
using ModernKeePass.Application.Common.Models;
using ModernKeePass.Application.Group.Models;
using ModernKeePass.Application.Group.Queries.GetGroup;
using ModernKeePass.Common;
using ModernKeePass.Domain.Enums;
using ModernKeePass.Models;
namespace ModernKeePass.ViewModels
{
public class BreadcrumbControlVm
{
public ObservableCollection<BreadcrumbItem> BreadcrumbItems { get; }
public string ParentGroupName { get; private set; }
public Icon ParentGroupIcon { get; private set; } = Icon.Folder;
public RelayCommand GoBackCommand { get; }
public RelayCommand GoUpCommand { get; private set; }
public RelayCommand<int> GoToCommand { get; }
private readonly IMediator _mediator;
private readonly INavigationService _navigation;
public BreadcrumbControlVm(IMediator mediator, INavigationService navigation)
{
_mediator = mediator;
_navigation = navigation;
BreadcrumbItems = new ObservableCollection<BreadcrumbItem>();
GoBackCommand = new RelayCommand(() => _navigation.GoBack());
GoToCommand = new RelayCommand<int>(GoTo);
GoUpCommand = new RelayCommand(() => GoTo(BreadcrumbItems.Count - 1), () => !string.IsNullOrEmpty(ParentGroupName));
}
public async Task Initialize(GroupVm group)
{
if (group == null) return;
GoBackCommand.RaiseCanExecuteChanged();
ParentGroupName = group.Title;
ParentGroupIcon = group.Icon;
BreadcrumbItems.Add(new BreadcrumbItem { Path = group.Id, Name = group.Title, Icon = group.Icon });
var parentGroup = group;
while (!string.IsNullOrEmpty(parentGroup.ParentGroupId))
{
parentGroup = await _mediator.Send(new GetGroupQuery {Id = parentGroup.ParentGroupId});
BreadcrumbItems.Add(new BreadcrumbItem {Path = parentGroup.Id, Name = parentGroup.Title, Icon = parentGroup.Icon});
}
}
private void GoTo(int index)
{
if (BreadcrumbItems.Count == 0) return;
var breadcrumb = BreadcrumbItems[index];
_navigation.NavigateTo(Constants.Navigation.GroupPage, new NavigationItem { Id = breadcrumb.Path });
}
}
}

View File

@@ -21,6 +21,7 @@ using MediatR;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using ModernKeePass.Application.Common.Interfaces; using ModernKeePass.Application.Common.Interfaces;
using ModernKeePass.ViewModels.Settings; using ModernKeePass.ViewModels.Settings;
using ModernKeePass.Domain.Interfaces;
namespace ModernKeePass.ViewModels namespace ModernKeePass.ViewModels
{ {
@@ -45,7 +46,7 @@ namespace ModernKeePass.ViewModels
else else
{ {
// Create run time view services and models // Create run time view services and models
//SimpleIoc.Default.Register<IDataService, DataService>();IDataService SimpleIoc.Default.Register(() => App.Services.GetRequiredService<IDateTime>());
SimpleIoc.Default.Register(() => App.Services.GetRequiredService<IMediator>()); SimpleIoc.Default.Register(() => App.Services.GetRequiredService<IMediator>());
SimpleIoc.Default.Register(() => App.Services.GetRequiredService<IRecentProxy>()); SimpleIoc.Default.Register(() => App.Services.GetRequiredService<IRecentProxy>());
SimpleIoc.Default.Register(() => App.Services.GetRequiredService<IResourceProxy>()); SimpleIoc.Default.Register(() => App.Services.GetRequiredService<IResourceProxy>());
@@ -69,6 +70,7 @@ namespace ModernKeePass.ViewModels
SimpleIoc.Default.Register<TopMenuVm>(); SimpleIoc.Default.Register<TopMenuVm>();
SimpleIoc.Default.Register<ColorPickerControlVm>(); SimpleIoc.Default.Register<ColorPickerControlVm>();
SimpleIoc.Default.Register<PasswordGenerationBoxControlVm>(); SimpleIoc.Default.Register<PasswordGenerationBoxControlVm>();
SimpleIoc.Default.Register<BreadcrumbControlVm>();
SimpleIoc.Default.Register<NewVm>(); SimpleIoc.Default.Register<NewVm>();
SimpleIoc.Default.Register<OpenVm>(); SimpleIoc.Default.Register<OpenVm>();
SimpleIoc.Default.Register<RecentVm>(); SimpleIoc.Default.Register<RecentVm>();
@@ -86,6 +88,7 @@ namespace ModernKeePass.ViewModels
public TopMenuVm TopMenu => ServiceLocator.Current.GetInstance<TopMenuVm>(Guid.NewGuid().ToString()); public TopMenuVm TopMenu => ServiceLocator.Current.GetInstance<TopMenuVm>(Guid.NewGuid().ToString());
public ColorPickerControlVm ColorPicker => ServiceLocator.Current.GetInstance<ColorPickerControlVm>(Guid.NewGuid().ToString()); public ColorPickerControlVm ColorPicker => ServiceLocator.Current.GetInstance<ColorPickerControlVm>(Guid.NewGuid().ToString());
public PasswordGenerationBoxControlVm PasswordGenerationBox => ServiceLocator.Current.GetInstance<PasswordGenerationBoxControlVm>(Guid.NewGuid().ToString()); public PasswordGenerationBoxControlVm PasswordGenerationBox => ServiceLocator.Current.GetInstance<PasswordGenerationBoxControlVm>(Guid.NewGuid().ToString());
public BreadcrumbControlVm Breadcrumb => ServiceLocator.Current.GetInstance<BreadcrumbControlVm>(Guid.NewGuid().ToString());
public NewVm New => ServiceLocator.Current.GetInstance<NewVm>(Guid.NewGuid().ToString()); public NewVm New => ServiceLocator.Current.GetInstance<NewVm>(Guid.NewGuid().ToString());
public OpenVm Open => ServiceLocator.Current.GetInstance<OpenVm>(Guid.NewGuid().ToString()); public OpenVm Open => ServiceLocator.Current.GetInstance<OpenVm>(Guid.NewGuid().ToString());
public RecentVm Recent => ServiceLocator.Current.GetInstance<RecentVm>(Guid.NewGuid().ToString()); public RecentVm Recent => ServiceLocator.Current.GetInstance<RecentVm>(Guid.NewGuid().ToString());

View File

@@ -42,6 +42,7 @@
<Compile Include="$(MSBuildThisFileDirectory)Messages\NavigateToPageMessage.cs" /> <Compile Include="$(MSBuildThisFileDirectory)Messages\NavigateToPageMessage.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Messages\PasswordGeneratedMessage.cs" /> <Compile Include="$(MSBuildThisFileDirectory)Messages\PasswordGeneratedMessage.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Messages\SaveErrorMessage.cs" /> <Compile Include="$(MSBuildThisFileDirectory)Messages\SaveErrorMessage.cs" />
<Compile Include="$(MSBuildThisFileDirectory)TemplateSelectors\FirstItemDataTemplateSelector.cs" />
<Compile Include="$(MSBuildThisFileDirectory)ViewModels\AboutVm.cs" /> <Compile Include="$(MSBuildThisFileDirectory)ViewModels\AboutVm.cs" />
<Compile Include="$(MSBuildThisFileDirectory)ViewModels\Items\EntryFieldVm.cs" /> <Compile Include="$(MSBuildThisFileDirectory)ViewModels\Items\EntryFieldVm.cs" />
<Compile Include="$(MSBuildThisFileDirectory)ViewModels\Settings\CredentialsVm.cs" /> <Compile Include="$(MSBuildThisFileDirectory)ViewModels\Settings\CredentialsVm.cs" />
@@ -50,6 +51,7 @@
<Compile Include="$(MSBuildThisFileDirectory)ViewModels\Settings\RecycleBinVm.cs" /> <Compile Include="$(MSBuildThisFileDirectory)ViewModels\Settings\RecycleBinVm.cs" />
<Compile Include="$(MSBuildThisFileDirectory)ViewModels\Settings\GeneralVm.cs" /> <Compile Include="$(MSBuildThisFileDirectory)ViewModels\Settings\GeneralVm.cs" />
<Compile Include="$(MSBuildThisFileDirectory)ViewModels\Settings\SecurityVm.cs" /> <Compile Include="$(MSBuildThisFileDirectory)ViewModels\Settings\SecurityVm.cs" />
<Compile Include="$(MSBuildThisFileDirectory)ViewModels\UserControls\BreadcrumbControlVm.cs" />
<Compile Include="$(MSBuildThisFileDirectory)ViewModels\UserControls\ColorPickerControlVm.cs" /> <Compile Include="$(MSBuildThisFileDirectory)ViewModels\UserControls\ColorPickerControlVm.cs" />
<Compile Include="$(MSBuildThisFileDirectory)ViewModels\UserControls\PasswordGenerationBoxControlVm.cs" /> <Compile Include="$(MSBuildThisFileDirectory)ViewModels\UserControls\PasswordGenerationBoxControlVm.cs" />
<Compile Include="$(MSBuildThisFileDirectory)ViewModels\ViewModelLocatorCommon.cs" /> <Compile Include="$(MSBuildThisFileDirectory)ViewModels\ViewModelLocatorCommon.cs" />
@@ -64,4 +66,10 @@
<Compile Include="$(MSBuildThisFileDirectory)ViewModels\UserControls\SetCredentialsVm.cs" /> <Compile Include="$(MSBuildThisFileDirectory)ViewModels\UserControls\SetCredentialsVm.cs" />
<Compile Include="$(MSBuildThisFileDirectory)ViewModels\UserControls\TopMenuVm.cs" /> <Compile Include="$(MSBuildThisFileDirectory)ViewModels\UserControls\TopMenuVm.cs" />
</ItemGroup> </ItemGroup>
<ItemGroup>
<PRIResource Include="$(MSBuildThisFileDirectory)Strings\en-US\CodeBehind.resw" />
<PRIResource Include="$(MSBuildThisFileDirectory)Strings\en-US\Resources.resw" />
<PRIResource Include="$(MSBuildThisFileDirectory)Strings\fr-FR\CodeBehind.resw" />
<PRIResource Include="$(MSBuildThisFileDirectory)Strings\fr-FR\Resources.resw" />
</ItemGroup>
</Project> </Project>