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>
<PlatformTarget>x64</PlatformTarget>
</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' ">
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
@@ -81,7 +91,6 @@
<Compile Include="Common\Interfaces\IDatabaseProxy.cs" />
<Compile Include="Common\Interfaces\IEntityVm.cs" />
<Compile Include="Common\Interfaces\IFileProxy.cs" />
<Compile Include="Common\Interfaces\IImportFormat.cs" />
<Compile Include="Common\Interfaces\ICredentialsProxy.cs" />
<Compile Include="Common\Interfaces\ILogger.cs" />
<Compile Include="Common\Interfaces\INotificationService.cs" />
@@ -90,6 +99,7 @@
<Compile Include="Common\Interfaces\ISettingsProxy.cs" />
<Compile Include="Common\Mappings\IMapFrom.cs" />
<Compile Include="Common\Mappings\MappingProfile.cs" />
<Compile Include="Common\Models\BreadcrumbItem.cs" />
<Compile Include="Entry\Commands\AddAttachment\AddAttachmentCommand.cs" />
<Compile Include="Entry\Commands\AddHistory\AddHistoryCommand.cs" />
<Compile Include="Entry\Commands\DeleteAttachment\DeleteAttachmentCommand.cs" />
@@ -105,6 +115,8 @@
<Compile Include="Group\Queries\GetAllGroups\GetAllGroupsQuery.cs" />
<Compile Include="Group\Queries\GetGroup\GetGroupQuery.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\SetCompression\SetCompressionCommand.cs" />
<Compile Include="Parameters\Commands\SetHasRecycleBin\SetHasRecycleBinCommand.cs" />
@@ -119,7 +131,7 @@
<Compile Include="Parameters\Queries\GetKeyDerivations\GetKeyDerivationsQuery.cs" />
<Compile Include="Database\Commands\CloseDatabase\CloseDatabaseCommand.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\UpdateCredentials\UpdateCredentialsCommand.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
{
public class CreateDatabaseQueryValidator : AbstractValidator<CreateDatabaseCommand>
public class CreateDatabaseCommandValidator : AbstractValidator<CreateDatabaseCommand>
{
public CreateDatabaseQueryValidator()
public CreateDatabaseCommandValidator()
{
RuleFor(v => v.FilePath)
.NotNull()

View File

@@ -12,7 +12,7 @@ namespace ModernKeePass.Application
var assembly = typeof(DependencyInjection).GetTypeInfo().Assembly;
services.AddMediatR(assembly);
services.AddTransient(typeof(IPipelineBehavior<,>), typeof(DirtyStatusBehavior<,>));
return services;
}
}

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>
<PlatformTarget>x64</PlatformTarget>
</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' ">
<DebugType>pdbonly</DebugType>
<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>
<PlatformTarget>x64</PlatformTarget>
</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' ">
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
@@ -78,7 +88,6 @@
<ItemGroup>
<Compile Include="Common\MachineDateTime.cs" />
<Compile Include="DependencyInjection.cs" />
<Compile Include="File\CsvImportFormat.cs" />
<Compile Include="KeePass\MappingProfiles.cs" />
<Compile Include="KeePass\IconMapper.cs" />
<Compile Include="KeePass\KeePassDatabaseSettingsProxy.cs" />

View File

@@ -1,6 +1,6 @@
<?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">
<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>
<DisplayName>ModernKeePass</DisplayName>
<PublisherDisplayName>wismna</PublisherDisplayName>

View File

@@ -31,6 +31,7 @@ using ModernKeePass.Application.Group.Models;
using ModernKeePass.Common;
using ModernKeePass.Domain.Dtos;
using ModernKeePass.Domain.Exceptions;
using ModernKeePass.Domain.Interfaces;
using ModernKeePass.Extensions;
using ModernKeePass.Models;
using ModernKeePass.ViewModels.ListItems;
@@ -39,22 +40,22 @@ namespace ModernKeePass.ViewModels
{
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
{
get
{
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<Attachment> Attachments { get; private set; }
@@ -62,37 +63,6 @@ namespace ModernKeePass.ViewModels
/// Determines if the Entry is current or from history
/// </summary>
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
{
@@ -117,33 +87,33 @@ namespace ModernKeePass.ViewModels
public string Title
{
get { return SelectedItem.Title.Value; }
get { return _current.Title.Value; }
set
{
SelectedItem.Title.Value = value;
SetFieldValue(SelectedItem.Title.Name, value, false).ConfigureAwait(false).GetAwaiter();
_current.Title.Value = value;
SetFieldValue(_current.Title.Name, value, false).ConfigureAwait(false).GetAwaiter();
}
}
public string UserName
{
get { return SelectedItem.Username.Value; }
get { return _current.Username.Value; }
set
{
SelectedItem.Username.Value = value;
SetFieldValue(SelectedItem.Username.Name, value, false).ConfigureAwait(false).GetAwaiter();
_current.Username.Value = value;
SetFieldValue(_current.Username.Name, value, false).ConfigureAwait(false).GetAwaiter();
RaisePropertyChanged(nameof(UserName));
}
}
public string Password
{
get { return _cryptography.UnProtect(SelectedItem.Password.Value).GetAwaiter().GetResult(); }
get { return _cryptography.UnProtect(_current.Password.Value).GetAwaiter().GetResult(); }
set
{
var protectedPassword = _cryptography.Protect(value).ConfigureAwait(false).GetAwaiter().GetResult();
SelectedItem.Password.Value = protectedPassword;
SetFieldValue(SelectedItem.Password.Name, protectedPassword, true).ConfigureAwait(false).GetAwaiter();
_current.Password.Value = protectedPassword;
SetFieldValue(_current.Password.Name, protectedPassword, true).ConfigureAwait(false).GetAwaiter();
RaisePropertyChanged(nameof(Password));
}
@@ -151,65 +121,65 @@ namespace ModernKeePass.ViewModels
public string Url
{
get { return SelectedItem.Url.Value; }
get { return _current.Url.Value; }
set
{
SelectedItem.Url.Value = value;
SetFieldValue(SelectedItem.Url.Name, value, false).ConfigureAwait(false).GetAwaiter();
_current.Url.Value = value;
SetFieldValue(_current.Url.Name, value, false).ConfigureAwait(false).GetAwaiter();
RaisePropertyChanged(nameof(Url));
}
}
public string Notes
{
get { return SelectedItem.Notes.Value; }
get { return _current.Notes.Value; }
set
{
SelectedItem.Notes.Value = value;
SetFieldValue(SelectedItem.Notes.Name, value, false).ConfigureAwait(false).GetAwaiter();
_current.Notes.Value = value;
SetFieldValue(_current.Notes.Name, value, false).ConfigureAwait(false).GetAwaiter();
}
}
public Symbol Icon
{
get { return (Symbol)Enum.Parse(typeof(Symbol), SelectedItem.Icon.ToString()); }
get { return (Symbol)Enum.Parse(typeof(Symbol), _current.Icon.ToString()); }
set
{
SelectedItem.Icon = (Icon)Enum.Parse(typeof(Icon), value.ToString());
SetFieldValue(EntryFieldName.Icon, SelectedItem.Icon, false).ConfigureAwait(false).GetAwaiter();
_current.Icon = (Icon)Enum.Parse(typeof(Icon), value.ToString());
SetFieldValue(EntryFieldName.Icon, _current.Icon, false).ConfigureAwait(false).GetAwaiter();
}
}
public DateTimeOffset ExpiryDate
{
get { return SelectedItem.ExpirationDate; }
get { return _current.ExpirationDate; }
set
{
if (!HasExpirationDate) return;
SelectedItem.ExpirationDate = value.Date;
SetFieldValue(EntryFieldName.ExpirationDate, SelectedItem.ExpirationDate, false).ConfigureAwait(false).GetAwaiter();
_current.ExpirationDate = value.Date;
SetFieldValue(EntryFieldName.ExpirationDate, _current.ExpirationDate, false).ConfigureAwait(false).GetAwaiter();
}
}
public TimeSpan ExpiryTime
{
get { return SelectedItem.ExpirationDate.TimeOfDay; }
get { return _current.ExpirationDate.TimeOfDay; }
set
{
if (!HasExpirationDate) return;
SelectedItem.ExpirationDate = SelectedItem.ExpirationDate.Date.Add(value);
SetFieldValue(EntryFieldName.ExpirationDate, SelectedItem.ExpirationDate, false).ConfigureAwait(false).GetAwaiter();
_current.ExpirationDate = _current.ExpirationDate.Date.Add(value);
SetFieldValue(EntryFieldName.ExpirationDate, _current.ExpirationDate, false).ConfigureAwait(false).GetAwaiter();
}
}
public bool HasExpirationDate
{
get { return SelectedItem.HasExpirationDate; }
get { return _current.HasExpirationDate; }
set
{
SelectedItem.HasExpirationDate = value;
_current.HasExpirationDate = value;
SetFieldValue(EntryFieldName.HasExpirationDate, value, false).ConfigureAwait(false).GetAwaiter();
RaisePropertyChanged(nameof(HasExpirationDate));
}
@@ -217,41 +187,34 @@ namespace ModernKeePass.ViewModels
public SolidColorBrush BackgroundColor
{
get { return SelectedItem?.BackgroundColor.ToSolidColorBrush(); }
get { return _current?.BackgroundColor.ToSolidColorBrush(); }
set
{
SelectedItem.BackgroundColor = value.ToColor();
SetFieldValue(EntryFieldName.BackgroundColor, SelectedItem.BackgroundColor, false).ConfigureAwait(false).GetAwaiter();
_current.BackgroundColor = value.ToColor();
SetFieldValue(EntryFieldName.BackgroundColor, _current.BackgroundColor, false).ConfigureAwait(false).GetAwaiter();
}
}
public SolidColorBrush ForegroundColor
{
get { return SelectedItem?.ForegroundColor.ToSolidColorBrush(); }
get { return _current?.ForegroundColor.ToSolidColorBrush(); }
set
{
SelectedItem.ForegroundColor = value.ToColor();
SetFieldValue(EntryFieldName.ForegroundColor, SelectedItem.ForegroundColor, false).ConfigureAwait(false).GetAwaiter();
_current.ForegroundColor = value.ToColor();
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<string> MoveCommand { get; }
public RelayCommand RestoreCommand { get; }
public RelayCommand DeleteCommand { get; }
public RelayCommand GoBackCommand { get; }
public RelayCommand GoToParentCommand { get; set; }
public RelayCommand AddAdditionalField { get; set; }
public RelayCommand<EntryFieldVm> DeleteAdditionalField { get; set; }
public RelayCommand<Attachment> OpenAttachmentCommand { get; set; }
public RelayCommand AddAttachmentCommand { get; set; }
public RelayCommand<Attachment> DeleteAttachmentCommand { get; set; }
public RelayCommand AddAdditionalField { get; }
public RelayCommand<EntryFieldVm> DeleteAdditionalField { get; }
public RelayCommand<Attachment> OpenAttachmentCommand { get; }
public RelayCommand AddAttachmentCommand { get; }
public RelayCommand<Attachment> DeleteAttachmentCommand { get; }
public RelayCommand<EntryVm> SetCurrentEntryCommand { get; }
private DatabaseVm Database => _mediator.Send(new GetDatabaseQuery()).GetAwaiter().GetResult();
@@ -262,14 +225,13 @@ namespace ModernKeePass.ViewModels
private readonly INotificationService _notification;
private readonly IFileProxy _file;
private readonly ICryptographyClient _cryptography;
private GroupVm _parent;
private EntryVm _selectedItem;
private readonly IDateTime _dateTime;
private EntryVm _current;
private int _selectedIndex;
private int _additionalFieldSelectedIndex = -1;
private bool _isEditMode;
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;
_navigation = navigation;
@@ -278,18 +240,18 @@ namespace ModernKeePass.ViewModels
_notification = notification;
_file = file;
_cryptography = cryptography;
_dateTime = dateTime;
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());
DeleteCommand = new RelayCommand(async () => await AskForDelete());
GoBackCommand = new RelayCommand(() => _navigation.GoBack());
GoToParentCommand = new RelayCommand(() => GoToGroup(_parent.Id));
AddAdditionalField = new RelayCommand(AddField, () => IsCurrentEntry);
DeleteAdditionalField = new RelayCommand<EntryFieldVm>(async field => await DeleteField(field), field => field != null && IsCurrentEntry);
OpenAttachmentCommand = new RelayCommand<Attachment>(async attachment => await OpenAttachment(attachment));
AddAttachmentCommand = new RelayCommand(async () => await AddAttachment(), () => 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<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)
{
SelectedIndex = 0;
SelectedItem = await _mediator.Send(new GetEntryQuery { Id = entryId });
_parent = await _mediator.Send(new GetGroupQuery { Id = SelectedItem.ParentGroupId });
History = new ObservableCollection<EntryVm> { SelectedItem };
foreach (var entry in SelectedItem.History.Skip(1))
_current = await _mediator.Send(new GetEntryQuery { Id = entryId });
SetCurrentEntry(_current);
Parent = await _mediator.Send(new GetGroupQuery { Id = _current.ParentGroupId });
History = new ObservableCollection<IEntityVm> { _current };
foreach (var entry in _current.History.Skip(1))
{
History.Add(entry);
}
@@ -316,7 +279,7 @@ namespace ModernKeePass.ViewModels
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)
@@ -327,7 +290,7 @@ namespace ModernKeePass.ViewModels
private async Task Move(string destination)
{
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);
}
@@ -389,7 +352,7 @@ namespace ModernKeePass.ViewModels
_resource.GetResourceValue("EntityDeleteCancelButton"), async isOk =>
{
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);
});
}
@@ -397,8 +360,8 @@ namespace ModernKeePass.ViewModels
private async Task RestoreHistory()
{
await _mediator.Send(new RestoreHistoryCommand { Entry = History[0], HistoryIndex = History.Count - SelectedIndex - 1 });
History.Insert(0, SelectedItem);
await _mediator.Send(new RestoreHistoryCommand { Entry = History[0] as EntryVm, HistoryIndex = History.Count - SelectedIndex - 1 });
History.Insert(0, _current);
}
private async Task SaveChanges()
@@ -420,7 +383,7 @@ namespace ModernKeePass.ViewModels
await _mediator.Send(new DeleteEntryCommand
{
EntryId = Id,
ParentGroupId = SelectedItem.ParentGroupId,
ParentGroupId = _current.ParentGroupId,
RecycleBinName = _resource.GetResourceValue("RecycleBinTitle")
});
_isDirty = false;
@@ -443,13 +406,13 @@ namespace ModernKeePass.ViewModels
var fileInfo = await _file.OpenFile(string.Empty, Domain.Common.Constants.Extensions.Any, false);
if (fileInfo == null) return;
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 });
}
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);
}
@@ -458,5 +421,29 @@ namespace ModernKeePass.ViewModels
SaveCommand.RaiseCanExecuteChanged();
_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.CreateEntry;
using ModernKeePass.Application.Group.Commands.CreateGroup;
using ModernKeePass.Application.Group.Commands.DeleteEntry;
using ModernKeePass.Application.Group.Commands.DeleteGroup;
using ModernKeePass.Application.Group.Commands.MoveEntry;
using ModernKeePass.Application.Group.Commands.MoveGroup;
@@ -38,10 +39,12 @@ namespace ModernKeePass.ViewModels
{
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 GroupVm Parent { get; private set; }
public IOrderedEnumerable<IGrouping<char, EntryVm>> EntriesZoomedOut => from e in Entries
group e by (e.Title.Value ?? string.Empty).ToUpper().FirstOrDefault() into grp
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 IsInRecycleBin => _parent != null && _parent.Id == Database.RecycleBinId;
public bool IsInRecycleBin => Parent != null && Parent.Id == Database.RecycleBinId;
public RelayCommand SaveCommand { get; }
public RelayCommand SortEntriesCommand { get; }
@@ -92,9 +95,12 @@ namespace ModernKeePass.ViewModels
public RelayCommand<string> MoveCommand { get; }
public RelayCommand CreateEntryCommand { get; }
public RelayCommand<string> CreateGroupCommand { get; }
public RelayCommand DeleteCommand { get; set; }
public RelayCommand GoBackCommand { get; set; }
public RelayCommand GoToParentCommand { get; set; }
public RelayCommand DeleteCommand { get; }
public RelayCommand GoBackCommand { get; }
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();
@@ -104,7 +110,6 @@ namespace ModernKeePass.ViewModels
private readonly IDialogService _dialog;
private readonly INotificationService _notification;
private GroupVm _group;
private GroupVm _parent;
private bool _isEditMode;
private EntryVm _reorderedEntry;
private GroupVm _reorderedGroup;
@@ -125,7 +130,10 @@ namespace ModernKeePass.ViewModels
CreateGroupCommand = new RelayCommand<string>(async newGroupName => await AddNewGroup(newGroupName), _ => !IsInRecycleBin && Database.RecycleBinId != Id);
DeleteCommand = new RelayCommand(async () => await AskForDelete(),() => IsNotRoot);
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());
}
@@ -135,12 +143,13 @@ namespace ModernKeePass.ViewModels
_group = await _mediator.Send(new GetGroupQuery { Id = groupId });
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.CollectionChanged += Entries_CollectionChanged;
Groups = new ObservableCollection<GroupVm>(_group.Groups);
Groups = new ObservableCollection<IEntityVm>(_group.Groups);
Groups.CollectionChanged += Groups_CollectionChanged;
}
@@ -176,7 +185,7 @@ namespace ModernKeePass.ViewModels
public async Task Move(string destinationId)
{
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);
}
@@ -251,7 +260,7 @@ namespace ModernKeePass.ViewModels
private async Task SortGroupsAsync()
{
await _mediator.Send(new SortGroupsCommand {Group = _group});
Groups = new ObservableCollection<GroupVm>(_group.Groups);
Groups = new ObservableCollection<IEntityVm>(_group.Groups);
RaisePropertyChanged(nameof(Groups));
SaveCommand.RaiseCanExecuteChanged();
}
@@ -285,7 +294,39 @@ namespace ModernKeePass.ViewModels
ParentGroupId = _group.ParentGroupId,
RecycleBinName = _resource.GetResourceValue("RecycleBinTitle")
});
_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="*" />
</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">
<Hub Padding="0">
<Hub x:Name="Hub" Padding="0">
<Hub.Resources>
<Style TargetType="TextBlock" x:Key="EntryTextBlockStyle">
<Setter Property="Margin" Value="0,20,0,0"/>
@@ -57,6 +51,15 @@
<Setter Property="FontSize" Value="18"/>
</Style>
</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}" />
<local:TextBoxWithButton x:Uid="LoginTextBox" Text="{Binding UserName, Mode=TwoWay}" Style="{StaticResource TextBoxWithButtonStyle}" ButtonContent="&#xE16F;" IsEnabled="{Binding IsCurrentEntry}">
<interactivity:Interaction.Behaviors>
@@ -209,62 +212,28 @@
</HubSection>
</Hub>
</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>
<!-- Bouton Précédent et titre de la page -->
<Grid Grid.Row="0" Background="{ThemeResource AppBarBackgroundThemeBrush}">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="{StaticResource MenuWidthGridLength}"/>
<ColumnDefinition Width="{StaticResource MenuWidthGridLength}"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<Button Grid.Column="0"
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:BreadcrumbUserControl x:Name="Breadcrumb" Group="{Binding Parent}" />
<controls:TopMenuUserControl
x:Name="TopMenu" Grid.Column="4"
IsEditButtonChecked="{Binding IsEditMode, Mode=TwoWay}"
x:Name="TopMenu" Grid.Column="1"
MoveButtonVisibility="{Binding IsCurrentEntry, Converter={StaticResource BooleanToVisibilityConverter}}"
RestoreButtonVisibility="{Binding IsCurrentEntry, Converter={StaticResource InverseBooleanToVisibilityConverter}}"
SaveCommand="{Binding SaveCommand}"
@@ -278,5 +247,30 @@
</interactivity:Interaction.Behaviors>
</controls:TopMenuUserControl>
</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>
</Page>

View File

@@ -28,7 +28,6 @@ namespace ModernKeePass.Views
if (args != null)
{
await Model.Initialize(args.Id);
Model.IsEditMode = args.IsNew;
}
}
@@ -46,18 +45,21 @@ namespace ModernKeePass.Views
VisualStateManager.GoToState(this, "Small", true);
VisualStateManager.GoToState(TopMenu, "Collapsed", true);
VisualStateManager.GoToState(HamburgerMenu, "Hidden", true);
VisualStateManager.GoToState(Breadcrumb, "Small", true);
}
else if (e.NewSize.Width > 640 && e.NewSize.Width <= 1008)
{
VisualStateManager.GoToState(this, "Medium", true);
VisualStateManager.GoToState(TopMenu, "Overflowed", true);
VisualStateManager.GoToState(HamburgerMenu, "Collapsed", true);
VisualStateManager.GoToState(Breadcrumb, "Medium", true);
}
else
{
VisualStateManager.GoToState(this, "Large", true);
VisualStateManager.GoToState(TopMenu, "Overflowed", 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:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
@@ -15,7 +15,6 @@
<Page.Resources>
<converters:ColorToBrushConverter x:Key="ColorToBrushConverter"/>
<converters:BooleanToVisibilityConverter x:Key="BooleanToVisibilityConverter"/>
<converters:InverseBooleanToVisibilityConverter x:Key="InverseBooleanToVisibilityConverter"/>
<converters:IconToSymbolConverter x:Key="IconToSymbolConverter"/>
</Page.Resources>
<Grid>
@@ -41,14 +40,6 @@
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
</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.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
@@ -71,7 +62,7 @@
</StackPanel>
</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>
<!-- Horizontal scrolling grid -->
<GridView
@@ -80,7 +71,6 @@
AutomationProperties.AutomationId="ItemGridView"
AutomationProperties.Name="Entries"
TabIndex="1"
SelectionChanged="entries_SelectionChanged"
IsSynchronizedWithCurrentItem="False"
BorderBrush="{StaticResource ListViewItemSelectedBackgroundThemeBrush}"
AllowDrop="True"
@@ -88,8 +78,11 @@
CanDragItems="{Binding IsEditMode}">
<interactivity:Interaction.Behaviors>
<core:DataTriggerBehavior Binding="{Binding IsEditMode}" Value="False">
<actions:SetupFocusAction TargetObject="{Binding}" />
<actions:SetupFocusAction TargetObject="{Binding ElementName=GridView}" />
</core:DataTriggerBehavior>
<core:EventTriggerBehavior EventName="SelectionChanged">
<core:InvokeCommandAction Command="{Binding GoToEntryCommand}" CommandParameter="{Binding SelectedItem, ElementName=GridView}" />
</core:EventTriggerBehavior>
</interactivity:Interaction.Behaviors>
<GridView.ItemTemplate>
<DataTemplate>
@@ -137,6 +130,7 @@
</core:EventTriggerBehavior>
</interactivity:Interaction.Behaviors>
</MenuFlyoutItem>
<MenuFlyoutItem x:Uid="EntryItemDelete" Command="{Binding DataContext.DeleteEntryCommand, ElementName=Page}" CommandParameter="{Binding}" />
</MenuFlyout>
</Button.Flyout>
</Button>
@@ -176,64 +170,32 @@
</SemanticZoom.ZoomedOutView>
</SemanticZoom>
</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>
<!-- Back button and page title -->
<Grid Grid.Row="0" Background="{ThemeResource AppBarBackgroundThemeBrush}">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="{StaticResource MenuWidthGridLength}"/>
<ColumnDefinition Width="{StaticResource MenuWidthGridLength}"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<Button Grid.Column="0"
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" 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"
<userControls:BreadcrumbUserControl x:Name="Breadcrumb" Group="{Binding Parent}" />
<userControls:TopMenuUserControl x:Name="TopMenu" Grid.Column="1"
SortButtonVisibility="{Binding IsEditMode, Converter={StaticResource BooleanToVisibilityConverter}}"
EditButtonVisibility="Visible"
IsEditButtonChecked="{Binding IsEditMode, Mode=TwoWay}"
SaveCommand="{Binding SaveCommand}"
MoveCommand="{Binding MoveCommand}"
@@ -246,7 +208,6 @@
</core:EventTriggerBehavior>
</interactivity:Interaction.Behaviors>
</userControls:TopMenuUserControl>
</Grid>
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="PageLayout">
@@ -258,6 +219,9 @@
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="HamburgerMenu" Storyboard.TargetProperty="IsOpen">
<DiscreteObjectKeyFrame KeyTime="0" Value="False"/>
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="SemanticZoom" Storyboard.TargetProperty="Margin">
<DiscreteObjectKeyFrame KeyTime="0" Value="-60,0,0,0"/>
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
<VisualState x:Name="Medium">
@@ -265,6 +229,9 @@
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="HamburgerMenu" Storyboard.TargetProperty="IsOpen">
<DiscreteObjectKeyFrame KeyTime="0" Value="False"/>
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="SemanticZoom" Storyboard.TargetProperty="Margin">
<DiscreteObjectKeyFrame KeyTime="0" Value="0,0,0,0"/>
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
<VisualState x:Name="Large">
@@ -275,6 +242,9 @@
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="HamburgerMenu" Storyboard.TargetProperty="IsOpen">
<DiscreteObjectKeyFrame KeyTime="0" Value="True"/>
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="SemanticZoom" Storyboard.TargetProperty="Margin">
<DiscreteObjectKeyFrame KeyTime="0" Value="0,0,0,0"/>
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
</VisualStateGroup>

View File

@@ -1,7 +1,6 @@
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Navigation;
using ModernKeePass.Application.Entry.Models;
using ModernKeePass.Models;
using ModernKeePass.ViewModels;
@@ -37,33 +36,6 @@ namespace ModernKeePass.Views
#endregion
#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)
{
@@ -81,18 +53,21 @@ namespace ModernKeePass.Views
VisualStateManager.GoToState(this, "Small", true);
VisualStateManager.GoToState(TopMenu, "Collapsed", true);
VisualStateManager.GoToState(HamburgerMenu, "Hidden", true);
VisualStateManager.GoToState(Breadcrumb, "Small", true);
}
else if (e.NewSize.Width > 640 && e.NewSize.Width <= 1008)
{
VisualStateManager.GoToState(this, "Medium", true);
VisualStateManager.GoToState(TopMenu, "Overflowed", true);
VisualStateManager.GoToState(HamburgerMenu, "Collapsed", true);
VisualStateManager.GoToState(Breadcrumb, "Medium", true);
}
else
{
VisualStateManager.GoToState(this, "Large", true);
VisualStateManager.GoToState(TopMenu, "Overflowed", true);
VisualStateManager.GoToState(HamburgerMenu, "Expanded", true);
VisualStateManager.GoToState(Breadcrumb, "Large", true);
}
}

View File

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

View File

@@ -40,7 +40,11 @@ namespace ModernKeePass.Views
protected override async void OnNavigatedTo(NavigationEventArgs 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);
}
}

View File

@@ -1,5 +1,5 @@
<Page
x:Class="ModernKeePass.Views.ImportExportPage"
x:Class="ModernKeePass.Views.ImportPage"
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"

View File

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

View File

@@ -9,6 +9,11 @@
mc:Ignorable="d"
DataContext="{Binding Source={StaticResource Locator}, Path=Recent}">
<Grid Background="{StaticResource ApplicationPageBackgroundThemeBrush}">
<Grid.Resources>
<CollectionViewSource
x:Name="RecentItemsSource"
Source="{Binding RecentItems}" />
</Grid.Resources>
<Grid.RowDefinitions>
<RowDefinition Height="40" />
<RowDefinition Height="*" />
@@ -21,7 +26,7 @@
</HyperlinkButton>
<controls:SelectableTemplateListView Grid.Row="1"
SelectedIndex="{Binding SelectedIndex}"
ItemsSource="{Binding RecentItems}"
ItemsSource="{Binding Source={StaticResource RecentItemsSource}}"
ItemContainerStyle="{StaticResource ListViewLeftIndicatorItemExpanded}">
<controls:SelectableTemplateListView.SelectedItemTemplate>
<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.Media;
using ModernKeePass.ViewModels;
@@ -33,7 +34,8 @@ namespace ModernKeePass.Views.UserControls
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="*" />
</Grid.RowDefinitions>
<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>
<core:EventTriggerBehavior EventName="Checked">
<core:GoToStateAction StateName="Expanded" />
@@ -94,7 +97,7 @@
<ListView.ItemTemplate>
<DataTemplate x:Name="IsNormal">
<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>
<ToolTip Content="{Binding Path={Binding DisplayMemberPath, ElementName=UserControl}}" />
</ToolTipService.ToolTip>
@@ -125,6 +128,7 @@
</StackPanel>
<interactivity:Interaction.Behaviors>
<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=NewGroupButton}" PropertyName="Visibility" Value="Collapsed" />
<actions:SetupFocusAction TargetObject="{Binding ElementName=NewGroupTextBox}" />

View File

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

View File

@@ -98,7 +98,7 @@
</MenuFlyout>
</Button.Flyout>
</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">
<ToolTipService.ToolTip>
<ToolTip x:Uid="TopMenuEditButton" />
@@ -151,7 +151,7 @@
<MenuFlyout Opening="OverflowFlyout_OnOpening">
<MenuFlyoutItem x:Uid="TopMenuSaveFlyout" x:Name="SaveFlyout" />
<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="TopMenuSortEntriesFlyout" x:Name="SortEntriesFlyout" 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),
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
{
get { return (ICommand)GetValue(DeleteCommandProperty); }
@@ -213,8 +225,7 @@ namespace ModernKeePass.Views.UserControls
MoveButton.CommandParameter = args.Tag;
MoveCommand.RaiseCanExecuteChanged();
}
private async void EntrySearchBox_OnSuggestionsRequested(SearchBox sender, SearchBoxSuggestionsRequestedEventArgs args)
{
var imageUri = RandomAccessStreamReference.CreateFromUri(new Uri("ms-appdata:/Assets/ModernKeePass-SmallLogo.scale-80.png"));

View File

@@ -100,8 +100,8 @@
<DependentUpon>DonatePage.xaml</DependentUpon>
</Compile>
<Compile Include="Views\BasePages\LayoutAwarePageBase.cs" />
<Compile Include="Views\MainPageFrames\ImportExportPage.xaml.cs">
<DependentUpon>ImportExportPage.xaml</DependentUpon>
<Compile Include="Views\MainPageFrames\ImportPage.xaml.cs">
<DependentUpon>ImportPage.xaml</DependentUpon>
</Compile>
<Compile Include="Views\SettingsPageFrames\SettingsHistoryPage.xaml.cs">
<DependentUpon>SettingsHistoryPage.xaml</DependentUpon>
@@ -124,6 +124,9 @@
<Compile Include="Views\SettingsPageFrames\SettingsWelcomePage.xaml.cs">
<DependentUpon>SettingsWelcomePage.xaml</DependentUpon>
</Compile>
<Compile Include="Views\UserControls\BreadcrumbUserControl.xaml.cs">
<DependentUpon>BreadcrumbUserControl.xaml</DependentUpon>
</Compile>
<Compile Include="Views\UserControls\ColorPickerUserControl.xaml.cs">
<DependentUpon>ColorPickerUserControl.xaml</DependentUpon>
</Compile>
@@ -189,10 +192,6 @@
<SubType>Designer</SubType>
</None>
<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>
<None Include="Package.StoreAssociation.xml" />
@@ -207,7 +206,7 @@
<Generator>MSBuild:Compile</Generator>
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Page>
<Page Include="Views\MainPageFrames\ImportExportPage.xaml">
<Page Include="Views\MainPageFrames\ImportPage.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>
@@ -223,6 +222,10 @@
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>
<Page Include="Views\UserControls\BreadcrumbUserControl.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>
<Page Include="Views\UserControls\ColorPickerUserControl.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>

View File

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

View File

@@ -1,5 +1,2 @@
Protection des donnees en memoire en plus du chiffrement de la base de donnees
Amelioration du rendu de la vue Semantique dezoomee
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
(R)Ajout du fil d'Ariane pour aider a la navigation
Ameliorations de design

View File

@@ -372,9 +372,6 @@
<data name="GroupsLeftListView.ButtonLabel" xml:space="preserve">
<value>New group</value>
</data>
<data name="GroupsLeftListView.HeaderLabel" xml:space="preserve">
<value>Groups</value>
</data>
<data name="HistoryLeftListView.HeaderLabel" xml:space="preserve">
<value>History</value>
</data>
@@ -453,9 +450,6 @@
<data name="ToastUpdateDatabase.Title" xml:space="preserve">
<value>Composite Key</value>
</data>
<data name="EntryTitle.PlaceholderText" xml:space="preserve">
<value>New entry name...</value>
</data>
<data name="SearchButtonLabel.Text" xml:space="preserve">
<value>Search</value>
</data>
@@ -570,4 +564,16 @@
<data name="PasswordGenerationButton.ButtonContent" xml:space="preserve">
<value>Random</value>
</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>

View File

@@ -372,9 +372,6 @@
<data name="GroupsLeftListView.ButtonLabel" xml:space="preserve">
<value>Nouveau groupe</value>
</data>
<data name="GroupsLeftListView.HeaderLabel" xml:space="preserve">
<value>Groupes</value>
</data>
<data name="HistoryLeftListView.HeaderLabel" xml:space="preserve">
<value>Historique</value>
</data>
@@ -453,9 +450,6 @@
<data name="ToastUpdateDatabase.Title" xml:space="preserve">
<value>Clé maître</value>
</data>
<data name="EntryTitle.PlaceholderText" xml:space="preserve">
<value>Nom de la nouvelle entrée...</value>
</data>
<data name="SearchButtonLabel.Text" xml:space="preserve">
<value>Recherche</value>
</data>
@@ -570,4 +564,16 @@
<data name="PasswordGenerationButton.ButtonContent" xml:space="preserve">
<value>Aléatoire</value>
</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>

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 ModernKeePass.Application.Common.Interfaces;
using ModernKeePass.ViewModels.Settings;
using ModernKeePass.Domain.Interfaces;
namespace ModernKeePass.ViewModels
{
@@ -45,7 +46,7 @@ namespace ModernKeePass.ViewModels
else
{
// 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<IRecentProxy>());
SimpleIoc.Default.Register(() => App.Services.GetRequiredService<IResourceProxy>());
@@ -69,6 +70,7 @@ namespace ModernKeePass.ViewModels
SimpleIoc.Default.Register<TopMenuVm>();
SimpleIoc.Default.Register<ColorPickerControlVm>();
SimpleIoc.Default.Register<PasswordGenerationBoxControlVm>();
SimpleIoc.Default.Register<BreadcrumbControlVm>();
SimpleIoc.Default.Register<NewVm>();
SimpleIoc.Default.Register<OpenVm>();
SimpleIoc.Default.Register<RecentVm>();
@@ -86,6 +88,7 @@ namespace ModernKeePass.ViewModels
public TopMenuVm TopMenu => ServiceLocator.Current.GetInstance<TopMenuVm>(Guid.NewGuid().ToString());
public ColorPickerControlVm ColorPicker => ServiceLocator.Current.GetInstance<ColorPickerControlVm>(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 OpenVm Open => ServiceLocator.Current.GetInstance<OpenVm>(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\PasswordGeneratedMessage.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Messages\SaveErrorMessage.cs" />
<Compile Include="$(MSBuildThisFileDirectory)TemplateSelectors\FirstItemDataTemplateSelector.cs" />
<Compile Include="$(MSBuildThisFileDirectory)ViewModels\AboutVm.cs" />
<Compile Include="$(MSBuildThisFileDirectory)ViewModels\Items\EntryFieldVm.cs" />
<Compile Include="$(MSBuildThisFileDirectory)ViewModels\Settings\CredentialsVm.cs" />
@@ -50,6 +51,7 @@
<Compile Include="$(MSBuildThisFileDirectory)ViewModels\Settings\RecycleBinVm.cs" />
<Compile Include="$(MSBuildThisFileDirectory)ViewModels\Settings\GeneralVm.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\PasswordGenerationBoxControlVm.cs" />
<Compile Include="$(MSBuildThisFileDirectory)ViewModels\ViewModelLocatorCommon.cs" />
@@ -64,4 +66,10 @@
<Compile Include="$(MSBuildThisFileDirectory)ViewModels\UserControls\SetCredentialsVm.cs" />
<Compile Include="$(MSBuildThisFileDirectory)ViewModels\UserControls\TopMenuVm.cs" />
</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>