Additional fields Add, Update and Delete work (too well)

SelectableListView works when programmatically setting selection
This commit is contained in:
Geoffroy BONNEVILLE
2020-05-11 10:53:14 +02:00
parent 71b6009a29
commit bb2b99ed66
20 changed files with 274 additions and 101 deletions

View File

@@ -91,6 +91,7 @@
<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" />
<Compile Include="Entry\Commands\DeleteField\DeleteFieldCommand.cs" />
<Compile Include="Entry\Commands\DeleteHistory\DeleteHistoryCommand.cs" /> <Compile Include="Entry\Commands\DeleteHistory\DeleteHistoryCommand.cs" />
<Compile Include="Entry\Commands\RestoreHistory\RestoreHistoryCommand.cs" /> <Compile Include="Entry\Commands\RestoreHistory\RestoreHistoryCommand.cs" />
<Compile Include="Entry\Queries\GetEntry\GetEntryQuery.cs" /> <Compile Include="Entry\Queries\GetEntry\GetEntryQuery.cs" />
@@ -121,8 +122,8 @@
<Compile Include="Database\Queries\OpenDatabase\OpenDatabaseQueryValidator.cs" /> <Compile Include="Database\Queries\OpenDatabase\OpenDatabaseQueryValidator.cs" />
<Compile Include="Database\Queries\ReOpenDatabase\ReOpenDatabaseQuery.cs" /> <Compile Include="Database\Queries\ReOpenDatabase\ReOpenDatabaseQuery.cs" />
<Compile Include="DependencyInjection.cs" /> <Compile Include="DependencyInjection.cs" />
<Compile Include="Entry\Commands\SetFieldValue\SetFieldValueCommand.cs" /> <Compile Include="Entry\Commands\UpsertField\UpsertFieldCommand.cs" />
<Compile Include="Entry\Commands\SetFieldValue\SetFieldValueCommandValidator.cs" /> <Compile Include="Entry\Commands\UpsertField\UpsertFieldCommandValidator.cs" />
<Compile Include="Entry\Models\EntryVm.cs" /> <Compile Include="Entry\Models\EntryVm.cs" />
<Compile Include="Group\Commands\AddEntry\AddEntryCommand.cs" /> <Compile Include="Group\Commands\AddEntry\AddEntryCommand.cs" />
<Compile Include="Group\Commands\AddGroup\AddGroupCommand.cs" /> <Compile Include="Group\Commands\AddGroup\AddGroupCommand.cs" />

View File

@@ -31,19 +31,22 @@ namespace ModernKeePass.Application.Common.Interfaces
void CloseDatabase(); void CloseDatabase();
EntryEntity GetEntry(string id); EntryEntity GetEntry(string id);
GroupEntity GetGroup(string id);
Task AddEntry(string parentGroupId, string entryId); Task AddEntry(string parentGroupId, string entryId);
Task MoveEntry(string parentGroupId, string entryId, int index); Task MoveEntry(string parentGroupId, string entryId, int index);
Task AddGroup(string parentGroupId, string groupId);
void UpdateEntry(string entryId, string fieldName, object fieldValue); void UpdateEntry(string entryId, string fieldName, object fieldValue);
void UpdateGroup(GroupEntity group); void DeleteField(string entryId, string fieldName);
Task RemoveEntry(string parentGroupId, string entryId); Task RemoveEntry(string parentGroupId, string entryId);
EntryEntity CreateEntry(string parentGroupId);
void SortEntries(string groupId);
GroupEntity GetGroup(string id);
Task AddGroup(string parentGroupId, string groupId);
void UpdateGroup(GroupEntity group);
Task RemoveGroup(string parentGroupId, string groupId); Task RemoveGroup(string parentGroupId, string groupId);
void DeleteEntity(string entityId); void DeleteEntity(string entityId);
EntryEntity CreateEntry(string parentGroupId);
GroupEntity CreateGroup(string parentGroupId, string name, bool isRecycleBin = false); GroupEntity CreateGroup(string parentGroupId, string name, bool isRecycleBin = false);
void SortEntries(string groupId);
void SortSubGroups(string groupId); void SortSubGroups(string groupId);
void AddAttachment(string entryId, string attachmentName, byte[] attachmentContent); void AddAttachment(string entryId, string attachmentName, byte[] attachmentContent);
void DeleteAttachment(string entryId, string attachmentName); void DeleteAttachment(string entryId, string attachmentName);

View File

@@ -0,0 +1,29 @@
using MediatR;
using ModernKeePass.Application.Common.Interfaces;
using ModernKeePass.Domain.Exceptions;
namespace ModernKeePass.Application.Entry.Commands.DeleteField
{
public class DeleteFieldCommand: IRequest
{
public string EntryId { get; set; }
public string FieldName { get; set; }
public class DeleteFieldCommandHandler : IRequestHandler<DeleteFieldCommand>
{
private readonly IDatabaseProxy _database;
public DeleteFieldCommandHandler(IDatabaseProxy database)
{
_database = database;
}
public void Handle(DeleteFieldCommand message)
{
if (!_database.IsOpen) throw new DatabaseClosedException();
_database.DeleteField(message.EntryId, message.FieldName);
}
}
}
}

View File

@@ -2,24 +2,24 @@
using ModernKeePass.Application.Common.Interfaces; using ModernKeePass.Application.Common.Interfaces;
using ModernKeePass.Domain.Exceptions; using ModernKeePass.Domain.Exceptions;
namespace ModernKeePass.Application.Entry.Commands.SetFieldValue namespace ModernKeePass.Application.Entry.Commands.UpsertField
{ {
public class SetFieldValueCommand : IRequest public class UpsertFieldCommand : IRequest
{ {
public string EntryId { get; set; } public string EntryId { get; set; }
public string FieldName { get; set; } public string FieldName { get; set; }
public object FieldValue { get; set; } public object FieldValue { get; set; }
public class SetFieldValueCommandHandler : IRequestHandler<SetFieldValueCommand> public class UpsertFieldCommandHandler : IRequestHandler<UpsertFieldCommand>
{ {
private readonly IDatabaseProxy _database; private readonly IDatabaseProxy _database;
public SetFieldValueCommandHandler(IDatabaseProxy database) public UpsertFieldCommandHandler(IDatabaseProxy database)
{ {
_database = database; _database = database;
} }
public void Handle(SetFieldValueCommand message) public void Handle(UpsertFieldCommand message)
{ {
if (!_database.IsOpen) throw new DatabaseClosedException(); if (!_database.IsOpen) throw new DatabaseClosedException();

View File

@@ -1,10 +1,10 @@
using FluentValidation; using FluentValidation;
namespace ModernKeePass.Application.Entry.Commands.SetFieldValue namespace ModernKeePass.Application.Entry.Commands.UpsertField
{ {
public class SetFieldValueCommandValidator: AbstractValidator<SetFieldValueCommand> public class UpsertFieldCommandValidator: AbstractValidator<UpsertFieldCommand>
{ {
public SetFieldValueCommandValidator() public UpsertFieldCommandValidator()
{ {
RuleFor(v => v.EntryId) RuleFor(v => v.EntryId)
.NotNull() .NotNull()

View File

@@ -78,7 +78,6 @@
<Compile Include="Common\Constants.cs" /> <Compile Include="Common\Constants.cs" />
<Compile Include="Dtos\Attachment.cs" /> <Compile Include="Dtos\Attachment.cs" />
<Compile Include="Dtos\Credentials.cs" /> <Compile Include="Dtos\Credentials.cs" />
<Compile Include="Dtos\Field.cs" />
<Compile Include="Dtos\FileInfo.cs" /> <Compile Include="Dtos\FileInfo.cs" />
<Compile Include="Dtos\PasswordGenerationOptions.cs" /> <Compile Include="Dtos\PasswordGenerationOptions.cs" />
<Compile Include="Entities\BaseEntity.cs" /> <Compile Include="Entities\BaseEntity.cs" />

View File

@@ -1,8 +0,0 @@
namespace ModernKeePass.Domain.Dtos
{
public class Field
{
public string Name { get; set; }
public string Value { get; set; }
}
}

View File

@@ -242,9 +242,18 @@ namespace ModernKeePass.Infrastructure.KeePass
case EntryFieldName.ForegroundColor: case EntryFieldName.ForegroundColor:
pwEntry.ForegroundColor = (Color)fieldValue; pwEntry.ForegroundColor = (Color)fieldValue;
break; break;
default:
pwEntry.Strings.Set(fieldName, new ProtectedString(true, fieldValue.ToString()));
break;
} }
} }
public void DeleteField(string entryId, string fieldName)
{
var pwEntry = _pwDatabase.RootGroup.FindEntry(BuildIdFromString(entryId), true);
pwEntry.Strings.Remove(fieldName);
}
public EntryEntity AddHistory(string entryId) public EntryEntity AddHistory(string entryId)
{ {
var pwEntry = _pwDatabase.RootGroup.FindEntry(BuildIdFromString(entryId), true); var pwEntry = _pwDatabase.RootGroup.FindEntry(BuildIdFromString(entryId), true);
@@ -306,7 +315,7 @@ namespace ModernKeePass.Infrastructure.KeePass
var pwGroup = _pwDatabase.RootGroup.FindGroup(BuildIdFromString(groupId), true); var pwGroup = _pwDatabase.RootGroup.FindGroup(BuildIdFromString(groupId), true);
pwGroup.SortSubGroups(false); pwGroup.SortSubGroups(false);
} }
public void AddAttachment(string entryId, string attachmentName, byte[] attachmentContent) public void AddAttachment(string entryId, string attachmentName, byte[] attachmentContent)
{ {
var pwEntry = _pwDatabase.RootGroup.FindEntry(BuildIdFromString(entryId), true); var pwEntry = _pwDatabase.RootGroup.FindEntry(BuildIdFromString(entryId), true);

View File

@@ -534,4 +534,10 @@
<data name="EntryAddAttachment.Text" xml:space="preserve"> <data name="EntryAddAttachment.Text" xml:space="preserve">
<value>Add attachment</value> <value>Add attachment</value>
</data> </data>
<data name="EntryAddAdditionalField.Text" xml:space="preserve">
<value>Add field</value>
</data>
<data name="EntryDeleteAdditionalField.Content" xml:space="preserve">
<value>Delete</value>
</data>
</root> </root>

View File

@@ -276,4 +276,7 @@
<data name="CompositeKeyFileNameSuggestion" xml:space="preserve"> <data name="CompositeKeyFileNameSuggestion" xml:space="preserve">
<value>Clé</value> <value>Clé</value>
</data> </data>
<data name="EntryAddAdditionalField.Text" xml:space="preserve">
<value>Ajouter un champ</value>
</data>
</root> </root>

View File

@@ -534,4 +534,7 @@
<data name="EntryAddAttachment.Text" xml:space="preserve"> <data name="EntryAddAttachment.Text" xml:space="preserve">
<value>Ajouter une pièce jointe</value> <value>Ajouter une pièce jointe</value>
</data> </data>
<data name="EntryDeleteAdditionalField.Content" xml:space="preserve">
<value>Supprimer</value>
</data>
</root> </root>

View File

@@ -16,9 +16,10 @@ using ModernKeePass.Application.Database.Queries.GetDatabase;
using ModernKeePass.Application.Entry.Commands.AddAttachment; using ModernKeePass.Application.Entry.Commands.AddAttachment;
using ModernKeePass.Application.Entry.Commands.AddHistory; using ModernKeePass.Application.Entry.Commands.AddHistory;
using ModernKeePass.Application.Entry.Commands.DeleteAttachment; using ModernKeePass.Application.Entry.Commands.DeleteAttachment;
using ModernKeePass.Application.Entry.Commands.DeleteField;
using ModernKeePass.Application.Entry.Commands.DeleteHistory; using ModernKeePass.Application.Entry.Commands.DeleteHistory;
using ModernKeePass.Application.Entry.Commands.RestoreHistory; using ModernKeePass.Application.Entry.Commands.RestoreHistory;
using ModernKeePass.Application.Entry.Commands.SetFieldValue; using ModernKeePass.Application.Entry.Commands.UpsertField;
using ModernKeePass.Application.Entry.Models; using ModernKeePass.Application.Entry.Models;
using ModernKeePass.Application.Entry.Queries.GetEntry; using ModernKeePass.Application.Entry.Queries.GetEntry;
using ModernKeePass.Application.Group.Commands.AddEntry; using ModernKeePass.Application.Group.Commands.AddEntry;
@@ -34,6 +35,7 @@ using ModernKeePass.Domain.Dtos;
using ModernKeePass.Domain.Exceptions; using ModernKeePass.Domain.Exceptions;
using ModernKeePass.Extensions; using ModernKeePass.Extensions;
using ModernKeePass.Models; using ModernKeePass.Models;
using ModernKeePass.ViewModels.ListItems;
namespace ModernKeePass.ViewModels namespace ModernKeePass.ViewModels
{ {
@@ -66,7 +68,7 @@ namespace ModernKeePass.ViewModels
} }
public ObservableCollection<EntryVm> History { get; private set; } public ObservableCollection<EntryVm> History { get; private set; }
public ObservableCollection<Field> AdditionalFields { get; private set; } public ObservableCollection<FieldVm> AdditionalFields { get; private set; }
public ObservableCollection<Attachment> Attachments { get; private set; } public ObservableCollection<Attachment> Attachments { get; private set; }
/// <summary> /// <summary>
@@ -82,11 +84,9 @@ namespace ModernKeePass.ViewModels
Set(() => SelectedItem, ref _selectedItem, value, true); Set(() => SelectedItem, ref _selectedItem, value, true);
if (value != null) if (value != null)
{ {
AdditionalFields = new ObservableCollection<Field>(SelectedItem.AdditionalFields.Select(f => new Field AdditionalFields =
{ new ObservableCollection<FieldVm>(
Name = f.Key, SelectedItem.AdditionalFields.Select(f => new FieldVm(f.Key, f.Value)));
Value = f.Value
}));
Attachments = new ObservableCollection<Attachment>(SelectedItem.Attachments.Select(f => new Attachment Attachments = new ObservableCollection<Attachment>(SelectedItem.Attachments.Select(f => new Attachment
{ {
Name = f.Key, Name = f.Key,
@@ -113,6 +113,16 @@ namespace ModernKeePass.ViewModels
} }
} }
public int AdditionalFieldSelectedIndex
{
get { return _additionalFieldSelectedIndex; }
set
{
Set(() => AdditionalFieldSelectedIndex, ref _additionalFieldSelectedIndex, value);
DeleteAdditionalField.RaiseCanExecuteChanged();
}
}
public double PasswordLength public double PasswordLength
{ {
get { return _passwordLength; } get { return _passwordLength; }
@@ -258,6 +268,8 @@ namespace ModernKeePass.ViewModels
public RelayCommand DeleteCommand { get; } public RelayCommand DeleteCommand { get; }
public RelayCommand GoBackCommand { get; } public RelayCommand GoBackCommand { get; }
public RelayCommand GoToParentCommand { get; set; } public RelayCommand GoToParentCommand { get; set; }
public RelayCommand AddAdditionalField { get; set; }
public RelayCommand<FieldVm> DeleteAdditionalField { get; set; }
public RelayCommand<Attachment> OpenAttachmentCommand { get; set; } public RelayCommand<Attachment> OpenAttachmentCommand { get; set; }
public RelayCommand AddAttachmentCommand { get; set; } public RelayCommand AddAttachmentCommand { get; set; }
public RelayCommand<Attachment> DeleteAttachmentCommand { get; set; } public RelayCommand<Attachment> DeleteAttachmentCommand { get; set; }
@@ -273,11 +285,12 @@ namespace ModernKeePass.ViewModels
private GroupVm _parent; private GroupVm _parent;
private EntryVm _selectedItem; private EntryVm _selectedItem;
private int _selectedIndex; private int _selectedIndex;
private int _additionalFieldSelectedIndex;
private bool _isEditMode; private bool _isEditMode;
private bool _isRevealPassword; private bool _isRevealPassword;
private double _passwordLength = 25; private double _passwordLength = 25;
private bool _isDirty; private bool _isDirty;
public EntryDetailVm(IMediator mediator, INavigationService navigation, IResourceProxy resource, IDialogService dialog, INotificationService notification, IFileProxy file) public EntryDetailVm(IMediator mediator, INavigationService navigation, IResourceProxy resource, IDialogService dialog, INotificationService notification, IFileProxy file)
{ {
_mediator = mediator; _mediator = mediator;
@@ -294,13 +307,17 @@ namespace ModernKeePass.ViewModels
DeleteCommand = new RelayCommand(async () => await AskForDelete()); DeleteCommand = new RelayCommand(async () => await AskForDelete());
GoBackCommand = new RelayCommand(() => _navigation.GoBack()); GoBackCommand = new RelayCommand(() => _navigation.GoBack());
GoToParentCommand = new RelayCommand(() => GoToGroup(_parent.Id)); GoToParentCommand = new RelayCommand(() => GoToGroup(_parent.Id));
AddAdditionalField = new RelayCommand(AddField);
DeleteAdditionalField = new RelayCommand<FieldVm>(async field => await DeleteField(field), field => field != null);
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)); DeleteAttachmentCommand = new RelayCommand<Attachment>(async attachment => await DeleteAttachment(attachment));
MessengerInstance.Register<DatabaseSavedMessage>(this, _ => SaveCommand.RaiseCanExecuteChanged()); MessengerInstance.Register<DatabaseSavedMessage>(this, _ => SaveCommand.RaiseCanExecuteChanged());
MessengerInstance.Register<EntryFieldValueChangedMessage>(this, async message => await SetFieldValue(message.FieldName, message.FieldValue));
MessengerInstance.Register<EntryFieldNameChangedMessage>(this, async message => await UpdateFieldName(message.OldName, message.NewName, message.Value));
} }
public async Task Initialize(string entryId) public async Task Initialize(string entryId)
{ {
SelectedIndex = 0; SelectedIndex = 0;
@@ -314,6 +331,70 @@ namespace ModernKeePass.ViewModels
History.CollectionChanged += (sender, args) => SaveCommand.RaiseCanExecuteChanged(); History.CollectionChanged += (sender, args) => SaveCommand.RaiseCanExecuteChanged();
} }
public async Task GeneratePassword()
{
Password = await _mediator.Send(new GeneratePasswordCommand
{
BracketsPatternSelected = BracketsPatternSelected,
CustomChars = CustomChars,
DigitsPatternSelected = DigitsPatternSelected,
LowerCasePatternSelected = LowerCasePatternSelected,
MinusPatternSelected = MinusPatternSelected,
PasswordLength = (int)PasswordLength,
SpacePatternSelected = SpacePatternSelected,
SpecialPatternSelected = SpecialPatternSelected,
UnderscorePatternSelected = UnderscorePatternSelected,
UpperCasePatternSelected = UpperCasePatternSelected
});
RaisePropertyChanged(nameof(IsRevealPasswordEnabled));
}
public async Task AddHistory()
{
if (_isDirty) await _mediator.Send(new AddHistoryCommand { Entry = History[0] });
}
public void GoToGroup(string groupId)
{
_navigation.NavigateTo(Constants.Navigation.GroupPage, new NavigationItem { Id = groupId });
}
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 });
GoToGroup(destination);
}
private async Task SetFieldValue(string fieldName, object value)
{
await _mediator.Send(new UpsertFieldCommand { EntryId = Id, FieldName = fieldName, FieldValue = value });
SaveCommand.RaiseCanExecuteChanged();
_isDirty = true;
}
private async Task UpdateFieldName(string oldName, string newName, string value)
{
if (!string.IsNullOrEmpty(oldName)) await _mediator.Send(new DeleteFieldCommand { EntryId = Id, FieldName = oldName });
await SetFieldValue(newName, value);
}
private void AddField()
{
AdditionalFields.Add(new FieldVm(string.Empty, string.Empty));
AdditionalFieldSelectedIndex = AdditionalFields.Count - 1;
}
private async Task DeleteField(FieldVm field)
{
AdditionalFields.Remove(field);
if (!string.IsNullOrEmpty(field.Name))
{
await _mediator.Send(new DeleteFieldCommand {EntryId = Id, FieldName = field.Name});
SaveCommand.RaiseCanExecuteChanged();
_isDirty = true;
}
}
private async Task AskForDelete() private async Task AskForDelete()
{ {
if (IsCurrentEntry) if (IsCurrentEntry)
@@ -349,48 +430,6 @@ namespace ModernKeePass.ViewModels
} }
} }
public async Task GeneratePassword()
{
Password = await _mediator.Send(new GeneratePasswordCommand
{
BracketsPatternSelected = BracketsPatternSelected,
CustomChars = CustomChars,
DigitsPatternSelected = DigitsPatternSelected,
LowerCasePatternSelected = LowerCasePatternSelected,
MinusPatternSelected = MinusPatternSelected,
PasswordLength = (int)PasswordLength,
SpacePatternSelected = SpacePatternSelected,
SpecialPatternSelected = SpecialPatternSelected,
UnderscorePatternSelected = UnderscorePatternSelected,
UpperCasePatternSelected = UpperCasePatternSelected
});
RaisePropertyChanged(nameof(IsRevealPasswordEnabled));
}
public async Task Move(string destination)
{
await _mediator.Send(new AddEntryCommand { ParentGroupId = destination, EntryId = Id });
await _mediator.Send(new RemoveEntryCommand { ParentGroupId = _parent.Id, EntryId = Id });
GoToGroup(destination);
}
public async Task SetFieldValue(string fieldName, object value)
{
await _mediator.Send(new SetFieldValueCommand { EntryId = Id, FieldName = fieldName, FieldValue = value });
SaveCommand.RaiseCanExecuteChanged();
_isDirty = true;
}
public async Task AddHistory()
{
if (_isDirty) await _mediator.Send(new AddHistoryCommand { Entry = History[0] });
}
public void GoToGroup(string groupId)
{
_navigation.NavigateTo(Constants.Navigation.GroupPage, new NavigationItem { Id = groupId });
}
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], HistoryIndex = History.Count - SelectedIndex - 1 });
@@ -442,12 +481,16 @@ namespace ModernKeePass.ViewModels
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 = SelectedItem, AttachmentName = fileInfo.Name, AttachmentContent = contents });
Attachments.Add(new Attachment { Name = fileInfo.Name, Content = contents }); Attachments.Add(new Attachment { Name = fileInfo.Name, Content = contents });
SaveCommand.RaiseCanExecuteChanged();
_isDirty = true;
} }
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 = SelectedItem, AttachmentName = attachment.Name });
Attachments.Remove(attachment); Attachments.Remove(attachment);
SaveCommand.RaiseCanExecuteChanged();
_isDirty = true;
} }
} }

View File

@@ -482,29 +482,40 @@
</HubSection> </HubSection>
<HubSection x:Uid="EntryHubAdditional"> <HubSection x:Uid="EntryHubAdditional">
<DataTemplate> <DataTemplate>
<local:SelectableTemplateListView <StackPanel Orientation="Vertical">
ItemsSource="{Binding AdditionalFields}" <HyperlinkButton Command="{Binding Path=DataContext.AddAdditionalField, ElementName=Page}">
ItemContainerStyle="{StaticResource ListViewLeftIndicatorItemExpanded}"> <StackPanel Orientation="Horizontal">
<local:SelectableTemplateListView.SelectedItemTemplate> <SymbolIcon Symbol="Add" />
<DataTemplate> <TextBlock x:Uid="EntryAddAdditionalField" VerticalAlignment="Center" Margin="10,0,0,0" />
<StackPanel Orientation="Vertical"> </StackPanel>
<TextBox Text="{Binding Name, Mode=TwoWay}" Width="350" </HyperlinkButton>
IsEnabled="{Binding Path=DataContext.IsCurrentEntry, ElementName=Page}" /> <Border BorderBrush="DarkGray" BorderThickness="0,0,0,1" />
<TextBox HorizontalAlignment="Left" AcceptsReturn="True" Height="100" TextWrapping="Wrap" Width="350" <local:SelectableTemplateListView
Text="{Binding Value, Mode=TwoWay}" ItemsSource="{Binding AdditionalFields}"
IsEnabled="{Binding Path=DataContext.IsCurrentEntry, ElementName=Page}" /> SelectedIndex="{Binding AdditionalFieldSelectedIndex, Mode=TwoWay}"
</StackPanel> ItemContainerStyle="{StaticResource ListViewLeftIndicatorItemExpanded}">
</DataTemplate> <local:SelectableTemplateListView.SelectedItemTemplate>
</local:SelectableTemplateListView.SelectedItemTemplate> <DataTemplate>
<local:SelectableTemplateListView.ItemTemplate> <StackPanel Orientation="Vertical">
<DataTemplate> <TextBox Text="{Binding Name, Mode=TwoWay}" Width="350"
<StackPanel Orientation="Vertical"> IsEnabled="{Binding Path=DataContext.IsCurrentEntry, ElementName=Page}" />
<TextBlock Text="{Binding Name}" Style="{StaticResource EntryTextBlockStyle}" FontWeight="SemiBold" /> <TextBox HorizontalAlignment="Left" AcceptsReturn="True" Height="100" TextWrapping="Wrap" Width="350"
<TextBlock HorizontalAlignment="Left" MaxLines="3" FontSize="12" Margin="2,0,2,5" Text="{Binding Value}" Style="{StaticResource EntryTextBlockStyle}"/> Text="{Binding Value, Mode=TwoWay}"
</StackPanel> IsEnabled="{Binding Path=DataContext.IsCurrentEntry, ElementName=Page}" />
</DataTemplate> <Button x:Uid="EntryDeleteAdditionalField" HorizontalAlignment="Right" Command="{Binding Path=DataContext.DeleteAdditionalField, ElementName=Page}" CommandParameter="{Binding}" />
</local:SelectableTemplateListView.ItemTemplate> </StackPanel>
</local:SelectableTemplateListView> </DataTemplate>
</local:SelectableTemplateListView.SelectedItemTemplate>
<local:SelectableTemplateListView.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Vertical">
<TextBlock Text="{Binding Name}" Style="{StaticResource EntryTextBlockStyle}" FontWeight="SemiBold" />
<TextBlock HorizontalAlignment="Left" MaxLines="3" FontSize="12" Margin="2,0,2,5" Text="{Binding Value}" Style="{StaticResource EntryTextBlockStyle}"/>
</StackPanel>
</DataTemplate>
</local:SelectableTemplateListView.ItemTemplate>
</local:SelectableTemplateListView>
</StackPanel>
</DataTemplate> </DataTemplate>
</HubSection> </HubSection>
<HubSection x:Uid="EntryHubAttachments" Foreground="{StaticResource HubSectionBrush}"> <HubSection x:Uid="EntryHubAttachments" Foreground="{StaticResource HubSectionBrush}">

View File

@@ -20,6 +20,7 @@
</StackPanel> </StackPanel>
</HyperlinkButton> </HyperlinkButton>
<controls:SelectableTemplateListView Grid.Row="1" <controls:SelectableTemplateListView Grid.Row="1"
SelectedIndex="{Binding SelectedIndex}"
ItemsSource="{Binding RecentItems}" ItemsSource="{Binding RecentItems}"
ItemContainerStyle="{StaticResource ListViewLeftIndicatorItemExpanded}"> ItemContainerStyle="{StaticResource ListViewLeftIndicatorItemExpanded}">
<controls:SelectableTemplateListView.SelectedItemTemplate> <controls:SelectableTemplateListView.SelectedItemTemplate>

View File

@@ -19,9 +19,17 @@ namespace ModernKeePass.Controls
public SelectableTemplateListView() public SelectableTemplateListView()
{ {
ContainerContentChanging += SelectableTemplateListView_ContainerContentChanging;
SelectionChanged += SelectableTemplateListView_SelectionChanged; SelectionChanged += SelectableTemplateListView_SelectionChanged;
} }
private void SelectableTemplateListView_ContainerContentChanging(ListViewBase sender, ContainerContentChangingEventArgs args)
{
args.ItemContainer.ContentTemplate = args.ItemContainer.IsSelected
? SelectedItemTemplate
: ItemTemplate;
}
private void SelectableTemplateListView_SelectionChanged(object sender, SelectionChangedEventArgs e) private void SelectableTemplateListView_SelectionChanged(object sender, SelectionChangedEventArgs e)
{ {
var listView = sender as ListView; var listView = sender as ListView;

View File

@@ -0,0 +1,9 @@
namespace Messages
{
public class EntryFieldNameChangedMessage
{
public string OldName { get; set; }
public string NewName { get; set; }
public string Value { get; set; }
}
}

View File

@@ -0,0 +1,8 @@
namespace Messages
{
public class EntryFieldValueChangedMessage
{
public string FieldName { get; set; }
public string FieldValue { get; set; }
}
}

View File

@@ -0,0 +1,37 @@
using GalaSoft.MvvmLight;
using Messages;
namespace ModernKeePass.ViewModels.ListItems
{
public class FieldVm: ViewModelBase
{
private string _name;
private string _value;
public string Name
{
get { return _name; }
set
{
MessengerInstance.Send(new EntryFieldNameChangedMessage { OldName = Name, NewName = value, Value = Value });
Set(nameof(Name), ref _name, value);
}
}
public string Value
{
get { return _value; }
set
{
MessengerInstance.Send(new EntryFieldValueChangedMessage { FieldName = Name, FieldValue = value });
Set(nameof(Value), ref _value, value);
}
}
public FieldVm(string fieldName, string fieldValue)
{
_name = fieldName;
_value = fieldValue;
}
}
}

View File

@@ -13,6 +13,7 @@ namespace ModernKeePass.ViewModels
{ {
private readonly IRecentProxy _recent; private readonly IRecentProxy _recent;
private ObservableCollection<RecentItemVm> _recentItems; private ObservableCollection<RecentItemVm> _recentItems;
private int _selectedIndex;
public ObservableCollection<RecentItemVm> RecentItems public ObservableCollection<RecentItemVm> RecentItems
{ {
@@ -21,7 +22,13 @@ namespace ModernKeePass.ViewModels
} }
public ICommand ClearAllCommand { get; } public ICommand ClearAllCommand { get; }
public int SelectedIndex
{
get { return _selectedIndex; }
set { Set(() => SelectedIndex, ref _selectedIndex, value); }
}
public RecentVm(IRecentProxy recent) public RecentVm(IRecentProxy recent)
{ {
_recent = recent; _recent = recent;
@@ -34,6 +41,7 @@ namespace ModernKeePass.ViewModels
{ {
var recentItems = _recent.GetAll().Select(r => new RecentItemVm(r)); var recentItems = _recent.GetAll().Select(r => new RecentItemVm(r));
RecentItems = new ObservableCollection<RecentItemVm>(recentItems); RecentItems = new ObservableCollection<RecentItemVm>(recentItems);
if (RecentItems.Any()) SelectedIndex = 0;
} }
private void ClearAll() private void ClearAll()

View File

@@ -36,10 +36,13 @@
<Compile Include="$(MSBuildThisFileDirectory)Messages\DatabaseOpenedMessage.cs" /> <Compile Include="$(MSBuildThisFileDirectory)Messages\DatabaseOpenedMessage.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Messages\DatabaseOpeningMessage.cs" /> <Compile Include="$(MSBuildThisFileDirectory)Messages\DatabaseOpeningMessage.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Messages\DatabaseSavedMessage.cs" /> <Compile Include="$(MSBuildThisFileDirectory)Messages\DatabaseSavedMessage.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Messages\EntryFieldNameChangedMessage.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Messages\EntryFieldValueChangedMessage.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Messages\FileNotFoundMessage.cs" /> <Compile Include="$(MSBuildThisFileDirectory)Messages\FileNotFoundMessage.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Messages\NavigateToPageMessage.cs" /> <Compile Include="$(MSBuildThisFileDirectory)Messages\NavigateToPageMessage.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Messages\SaveErrorMessage.cs" /> <Compile Include="$(MSBuildThisFileDirectory)Messages\SaveErrorMessage.cs" />
<Compile Include="$(MSBuildThisFileDirectory)ViewModels\AboutVm.cs" /> <Compile Include="$(MSBuildThisFileDirectory)ViewModels\AboutVm.cs" />
<Compile Include="$(MSBuildThisFileDirectory)ViewModels\Items\FieldVm.cs" />
<Compile Include="$(MSBuildThisFileDirectory)ViewModels\ViewModelLocatorCommon.cs" /> <Compile Include="$(MSBuildThisFileDirectory)ViewModels\ViewModelLocatorCommon.cs" />
<Compile Include="$(MSBuildThisFileDirectory)ViewModels\Items\ListMenuItemVm.cs" /> <Compile Include="$(MSBuildThisFileDirectory)ViewModels\Items\ListMenuItemVm.cs" />
<Compile Include="$(MSBuildThisFileDirectory)ViewModels\Items\MainMenuItemVm.cs" /> <Compile Include="$(MSBuildThisFileDirectory)ViewModels\Items\MainMenuItemVm.cs" />