New Breadcrumb user control

New Breadcrumb service
WIP icons and back button behavior
This commit is contained in:
Geoffroy BONNEVILLE
2020-06-09 20:18:17 +02:00
parent d6b17fe696
commit c62ed584dc
17 changed files with 308 additions and 45 deletions

View File

@@ -86,6 +86,7 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Compile Include="Common\Behaviors\DirtyStatusBehavior.cs" /> <Compile Include="Common\Behaviors\DirtyStatusBehavior.cs" />
<Compile Include="Common\Interfaces\IBreadcrumbService.cs" />
<Compile Include="Common\Interfaces\ICryptographyClient.cs" /> <Compile Include="Common\Interfaces\ICryptographyClient.cs" />
<Compile Include="Common\Interfaces\IDatabaseSettingsProxy.cs" /> <Compile Include="Common\Interfaces\IDatabaseSettingsProxy.cs" />
<Compile Include="Common\Interfaces\IDatabaseProxy.cs" /> <Compile Include="Common\Interfaces\IDatabaseProxy.cs" />
@@ -99,6 +100,8 @@
<Compile Include="Common\Interfaces\ISettingsProxy.cs" /> <Compile Include="Common\Interfaces\ISettingsProxy.cs" />
<Compile Include="Common\Mappings\IMapFrom.cs" /> <Compile Include="Common\Mappings\IMapFrom.cs" />
<Compile Include="Common\Mappings\MappingProfile.cs" /> <Compile Include="Common\Mappings\MappingProfile.cs" />
<Compile Include="Common\Models\BreadcrumbItem.cs" />
<Compile Include="Common\Services\BreadcrumbService.cs" />
<Compile Include="Entry\Commands\AddAttachment\AddAttachmentCommand.cs" /> <Compile Include="Entry\Commands\AddAttachment\AddAttachmentCommand.cs" />
<Compile Include="Entry\Commands\AddHistory\AddHistoryCommand.cs" /> <Compile Include="Entry\Commands\AddHistory\AddHistoryCommand.cs" />
<Compile Include="Entry\Commands\DeleteAttachment\DeleteAttachmentCommand.cs" /> <Compile Include="Entry\Commands\DeleteAttachment\DeleteAttachmentCommand.cs" />

View File

@@ -0,0 +1,12 @@
using System.Collections.Generic;
using ModernKeePass.Application.Common.Models;
namespace ModernKeePass.Application.Common.Interfaces
{
public interface IBreadcrumbService
{
void Push(BreadcrumbItem item);
BreadcrumbItem Pop(int count = 1);
IEnumerable<BreadcrumbItem> GetItems();
}
}

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

@@ -0,0 +1,37 @@
using System.Collections.Generic;
using ModernKeePass.Application.Common.Interfaces;
using ModernKeePass.Application.Common.Models;
namespace ModernKeePass.Application.Common.Services
{
public class BreadcrumbService: IBreadcrumbService
{
private readonly Stack<BreadcrumbItem> _breadcrumb;
public BreadcrumbService()
{
_breadcrumb = new Stack<BreadcrumbItem>();
}
public void Push(BreadcrumbItem item)
{
_breadcrumb.Push(item);
}
public BreadcrumbItem Pop(int count = 1)
{
if (_breadcrumb.Count == 0) return null;
for (var i = 1; i < count; i++)
{
_breadcrumb.Pop();
}
return _breadcrumb.Pop();
}
public IEnumerable<BreadcrumbItem> GetItems()
{
return _breadcrumb;
}
}
}

View File

@@ -2,6 +2,8 @@
using MediatR; using MediatR;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using ModernKeePass.Application.Common.Behaviors; using ModernKeePass.Application.Common.Behaviors;
using ModernKeePass.Application.Common.Interfaces;
using ModernKeePass.Application.Common.Services;
namespace ModernKeePass.Application namespace ModernKeePass.Application
{ {
@@ -13,6 +15,7 @@ namespace ModernKeePass.Application
services.AddMediatR(assembly); services.AddMediatR(assembly);
services.AddTransient(typeof(IPipelineBehavior<,>), typeof(DirtyStatusBehavior<,>)); services.AddTransient(typeof(IPipelineBehavior<,>), typeof(DirtyStatusBehavior<,>));
services.AddSingleton(typeof(IBreadcrumbService), typeof(BreadcrumbService));
return services; return services;
} }
} }

View File

@@ -43,9 +43,7 @@ namespace ModernKeePass.ViewModels
public bool HasExpired => HasExpirationDate && ExpiryDate < _dateTime.Now; public bool HasExpired => HasExpirationDate && ExpiryDate < _dateTime.Now;
public string Id => _current.Id; public string Id => _current.Id;
public string ParentGroupName => _parent.Title;
public bool IsRecycleOnDelete public bool IsRecycleOnDelete
{ {
get get
@@ -209,8 +207,6 @@ namespace ModernKeePass.ViewModels
public RelayCommand<string> MoveCommand { get; } public RelayCommand<string> MoveCommand { get; }
public RelayCommand RestoreCommand { get; } public RelayCommand RestoreCommand { get; }
public RelayCommand DeleteCommand { get; } public RelayCommand DeleteCommand { get; }
public RelayCommand GoBackCommand { get; }
public RelayCommand GoToParentCommand { get; }
public RelayCommand AddAdditionalField { get; } public RelayCommand AddAdditionalField { get; }
public RelayCommand<EntryFieldVm> DeleteAdditionalField { get; } public RelayCommand<EntryFieldVm> DeleteAdditionalField { get; }
public RelayCommand<Attachment> OpenAttachmentCommand { get; } public RelayCommand<Attachment> OpenAttachmentCommand { get; }
@@ -249,8 +245,6 @@ namespace ModernKeePass.ViewModels
MoveCommand = new RelayCommand<string>(async destination => await Move(destination), destination => _parent != null && !string.IsNullOrEmpty(destination) && destination != _parent.Id); MoveCommand = new RelayCommand<string>(async destination => await Move(destination), destination => _parent != null && !string.IsNullOrEmpty(destination) && destination != _parent.Id);
RestoreCommand = new RelayCommand(async () => await RestoreHistory()); RestoreCommand = new RelayCommand(async () => await RestoreHistory());
DeleteCommand = new RelayCommand(async () => await AskForDelete()); DeleteCommand = new RelayCommand(async () => await AskForDelete());
GoBackCommand = new RelayCommand(() => _navigation.GoBack());
GoToParentCommand = new RelayCommand(() => GoToGroup(_parent.Id));
AddAdditionalField = new RelayCommand(AddField, () => IsCurrentEntry); AddAdditionalField = new RelayCommand(AddField, () => IsCurrentEntry);
DeleteAdditionalField = new RelayCommand<EntryFieldVm>(async field => await DeleteField(field), field => field != null && IsCurrentEntry); DeleteAdditionalField = new RelayCommand<EntryFieldVm>(async field => await DeleteField(field), field => field != null && IsCurrentEntry);
OpenAttachmentCommand = new RelayCommand<Attachment>(async attachment => await OpenAttachment(attachment)); OpenAttachmentCommand = new RelayCommand<Attachment>(async attachment => await OpenAttachment(attachment));

View File

@@ -10,6 +10,7 @@ using GalaSoft.MvvmLight.Views;
using MediatR; using MediatR;
using Messages; using Messages;
using ModernKeePass.Application.Common.Interfaces; using ModernKeePass.Application.Common.Interfaces;
using ModernKeePass.Application.Common.Models;
using ModernKeePass.Application.Database.Commands.SaveDatabase; using ModernKeePass.Application.Database.Commands.SaveDatabase;
using ModernKeePass.Application.Database.Models; using ModernKeePass.Application.Database.Models;
using ModernKeePass.Application.Database.Queries.GetDatabase; using ModernKeePass.Application.Database.Queries.GetDatabase;
@@ -18,6 +19,7 @@ using ModernKeePass.Application.Group.Commands.AddEntry;
using ModernKeePass.Application.Group.Commands.AddGroup; using ModernKeePass.Application.Group.Commands.AddGroup;
using ModernKeePass.Application.Group.Commands.CreateEntry; using ModernKeePass.Application.Group.Commands.CreateEntry;
using ModernKeePass.Application.Group.Commands.CreateGroup; using ModernKeePass.Application.Group.Commands.CreateGroup;
using ModernKeePass.Application.Group.Commands.DeleteEntry;
using ModernKeePass.Application.Group.Commands.DeleteGroup; using ModernKeePass.Application.Group.Commands.DeleteGroup;
using ModernKeePass.Application.Group.Commands.MoveEntry; using ModernKeePass.Application.Group.Commands.MoveEntry;
using ModernKeePass.Application.Group.Commands.MoveGroup; using ModernKeePass.Application.Group.Commands.MoveGroup;
@@ -97,6 +99,7 @@ namespace ModernKeePass.ViewModels
public RelayCommand GoToParentCommand { get; } public RelayCommand GoToParentCommand { get; }
public RelayCommand<GroupVm> GoToGroupCommand { get; } public RelayCommand<GroupVm> GoToGroupCommand { get; }
public RelayCommand<EntryVm> GoToEntryCommand { get; } public RelayCommand<EntryVm> GoToEntryCommand { get; }
public RelayCommand<EntryVm> DeleteEntryCommand { get; }
private DatabaseVm Database => _mediator.Send(new GetDatabaseQuery()).GetAwaiter().GetResult(); private DatabaseVm Database => _mediator.Send(new GetDatabaseQuery()).GetAwaiter().GetResult();
@@ -105,19 +108,21 @@ namespace ModernKeePass.ViewModels
private readonly INavigationService _navigation; private readonly INavigationService _navigation;
private readonly IDialogService _dialog; private readonly IDialogService _dialog;
private readonly INotificationService _notification; private readonly INotificationService _notification;
private readonly IBreadcrumbService _breadcrumb;
private GroupVm _group; private GroupVm _group;
private GroupVm _parent; private GroupVm _parent;
private bool _isEditMode; private bool _isEditMode;
private EntryVm _reorderedEntry; private EntryVm _reorderedEntry;
private GroupVm _reorderedGroup; private GroupVm _reorderedGroup;
public GroupDetailVm(IMediator mediator, IResourceProxy resource, INavigationService navigation, IDialogService dialog, INotificationService notification) public GroupDetailVm(IMediator mediator, IResourceProxy resource, INavigationService navigation, IDialogService dialog, INotificationService notification, IBreadcrumbService breadcrumb)
{ {
_mediator = mediator; _mediator = mediator;
_resource = resource; _resource = resource;
_navigation = navigation; _navigation = navigation;
_dialog = dialog; _dialog = dialog;
_notification = notification; _notification = notification;
_breadcrumb = breadcrumb;
SaveCommand = new RelayCommand(async () => await SaveChanges(), () => Database.IsDirty); SaveCommand = new RelayCommand(async () => await SaveChanges(), () => Database.IsDirty);
SortEntriesCommand = new RelayCommand(async () => await SortEntriesAsync(), () => IsEditMode); SortEntriesCommand = new RelayCommand(async () => await SortEntriesAsync(), () => IsEditMode);
@@ -130,10 +135,41 @@ namespace ModernKeePass.ViewModels
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); GoToGroupCommand = new RelayCommand<GroupVm>(group => GoToGroup(group.Id), group => group != null);
GoToEntryCommand = new RelayCommand<EntryVm>(entry => GoToEntry(entry.Id), entry => entry != null); GoToEntryCommand = new RelayCommand<EntryVm>(entry => GoToEntry(entry.Id), entry => entry != null);
DeleteEntryCommand = new RelayCommand<EntryVm>(async entry => await AskForDeleteEntry(entry), entry => entry != null);
MessengerInstance.Register<DatabaseSavedMessage>(this, _ => SaveCommand.RaiseCanExecuteChanged()); MessengerInstance.Register<DatabaseSavedMessage>(this, _ => SaveCommand.RaiseCanExecuteChanged());
} }
private async Task AskForDeleteEntry(EntryVm entry)
{
if (IsRecycleOnDelete)
{
await DeleteEntry(entry.Id);
_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.Id);
});
}
}
private async Task DeleteEntry(string entryId)
{
await _mediator.Send(new DeleteEntryCommand
{
EntryId = entryId,
ParentGroupId = Id,
RecycleBinName = _resource.GetResourceValue("RecycleBinTitle")
});
}
public async Task Initialize(string groupId) public async Task Initialize(string groupId)
{ {
_group = await _mediator.Send(new GetGroupQuery { Id = groupId }); _group = await _mediator.Send(new GetGroupQuery { Id = groupId });
@@ -150,6 +186,7 @@ namespace ModernKeePass.ViewModels
public void GoToEntry(string entryId, bool isNew = false) public void GoToEntry(string entryId, bool isNew = false)
{ {
_breadcrumb.Push(new BreadcrumbItem { Name = Title, Path = Id, Icon = _group.Icon });
_navigation.NavigateTo(Constants.Navigation.EntryPage, new NavigationItem _navigation.NavigateTo(Constants.Navigation.EntryPage, new NavigationItem
{ {
Id = entryId, Id = entryId,
@@ -158,6 +195,7 @@ namespace ModernKeePass.ViewModels
} }
public void GoToGroup(string groupId, bool isNew = false) public void GoToGroup(string groupId, bool isNew = false)
{ {
_breadcrumb.Push(new BreadcrumbItem { Name = Title, Path = Id, Icon = _group.Icon });
_navigation.NavigateTo(Constants.Navigation.GroupPage, new NavigationItem _navigation.NavigateTo(Constants.Navigation.GroupPage, new NavigationItem
{ {
Id = groupId, Id = groupId,
@@ -289,6 +327,8 @@ namespace ModernKeePass.ViewModels
ParentGroupId = _group.ParentGroupId, ParentGroupId = _group.ParentGroupId,
RecycleBinName = _resource.GetResourceValue("RecycleBinTitle") RecycleBinName = _resource.GetResourceValue("RecycleBinTitle")
}); });
_breadcrumb.Pop();
_navigation.GoBack(); _navigation.GoBack();
} }
} }

View File

@@ -32,7 +32,6 @@
<ColumnDefinition Width="Auto" /> <ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" /> <ColumnDefinition Width="*" />
</Grid.ColumnDefinitions> </Grid.ColumnDefinitions>
<Grid Grid.Column="1"> <Grid Grid.Column="1">
<Hub x:Name="Hub" Padding="0"> <Hub x:Name="Hub" Padding="0">
<Hub.Resources> <Hub.Resources>
@@ -229,36 +228,13 @@
<!-- Bouton Précédent et titre de la page --> <!-- Bouton Précédent et titre de la page -->
<Grid Grid.Row="0" Background="{ThemeResource AppBarBackgroundThemeBrush}"> <Grid Grid.Row="0" Background="{ThemeResource AppBarBackgroundThemeBrush}">
<Grid.ColumnDefinitions> <Grid.ColumnDefinitions>
<ColumnDefinition Width="{StaticResource MenuWidthGridLength}"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="Auto"/> <ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/> <ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/> <ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions> </Grid.ColumnDefinitions>
<Button Grid.Column="0" <controls:BreadcrumbUserControl x:Name="Breadcrumb" />
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}"
Command="{Binding GoToParentCommand}"
Style="{StaticResource NoBorderButtonStyle}">
<StackPanel Orientation="Horizontal">
<SymbolIcon Symbol="Up" />
<TextBlock x:Name="UpButtonText" Margin="10,2,0,0" Text="{Binding ParentGroupName}" FontStyle="Italic" HorizontalAlignment="Center" />
</StackPanel>
<ToolTipService.ToolTip>
<ToolTip Content="{Binding ParentGroupName}" />
</ToolTipService.ToolTip>
</Button>
<controls:TopMenuUserControl <controls:TopMenuUserControl
x:Name="TopMenu" Grid.Column="4" x:Name="TopMenu" Grid.Column="2"
MoveButtonVisibility="{Binding IsCurrentEntry, Converter={StaticResource BooleanToVisibilityConverter}}" MoveButtonVisibility="{Binding IsCurrentEntry, Converter={StaticResource BooleanToVisibilityConverter}}"
RestoreButtonVisibility="{Binding IsCurrentEntry, Converter={StaticResource InverseBooleanToVisibilityConverter}}" RestoreButtonVisibility="{Binding IsCurrentEntry, Converter={StaticResource InverseBooleanToVisibilityConverter}}"
SaveCommand="{Binding SaveCommand}" SaveCommand="{Binding SaveCommand}"
@@ -279,9 +255,6 @@
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="Hub" Storyboard.TargetProperty="Margin"> <ObjectAnimationUsingKeyFrames Storyboard.TargetName="Hub" Storyboard.TargetProperty="Margin">
<DiscreteObjectKeyFrame KeyTime="0" Value="-30,0,0,0"/> <DiscreteObjectKeyFrame KeyTime="0" Value="-30,0,0,0"/>
</ObjectAnimationUsingKeyFrames> </ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="UpButtonText" Storyboard.TargetProperty="Visibility">
<DiscreteObjectKeyFrame KeyTime="0" Value="Collapsed"/>
</ObjectAnimationUsingKeyFrames>
</Storyboard> </Storyboard>
</VisualState> </VisualState>
<VisualState x:Name="Medium"> <VisualState x:Name="Medium">
@@ -289,9 +262,6 @@
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="Hub" Storyboard.TargetProperty="Margin"> <ObjectAnimationUsingKeyFrames Storyboard.TargetName="Hub" Storyboard.TargetProperty="Margin">
<DiscreteObjectKeyFrame KeyTime="0" Value="0,0,0,0"/> <DiscreteObjectKeyFrame KeyTime="0" Value="0,0,0,0"/>
</ObjectAnimationUsingKeyFrames> </ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="UpButtonText" Storyboard.TargetProperty="Visibility">
<DiscreteObjectKeyFrame KeyTime="0" Value="Visible"/>
</ObjectAnimationUsingKeyFrames>
</Storyboard> </Storyboard>
</VisualState> </VisualState>
<VisualState x:Name="Large"> <VisualState x:Name="Large">
@@ -299,9 +269,6 @@
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="Hub" Storyboard.TargetProperty="Margin"> <ObjectAnimationUsingKeyFrames Storyboard.TargetName="Hub" Storyboard.TargetProperty="Margin">
<DiscreteObjectKeyFrame KeyTime="0" Value="0,0,0,0"/> <DiscreteObjectKeyFrame KeyTime="0" Value="0,0,0,0"/>
</ObjectAnimationUsingKeyFrames> </ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="UpButtonText" Storyboard.TargetProperty="Visibility">
<DiscreteObjectKeyFrame KeyTime="0" Value="Visible"/>
</ObjectAnimationUsingKeyFrames>
</Storyboard> </Storyboard>
</VisualState> </VisualState>
</VisualStateGroup> </VisualStateGroup>

View File

@@ -45,18 +45,21 @@ namespace ModernKeePass.Views
VisualStateManager.GoToState(this, "Small", true); VisualStateManager.GoToState(this, "Small", true);
VisualStateManager.GoToState(TopMenu, "Collapsed", true); VisualStateManager.GoToState(TopMenu, "Collapsed", true);
VisualStateManager.GoToState(HamburgerMenu, "Hidden", true); VisualStateManager.GoToState(HamburgerMenu, "Hidden", true);
VisualStateManager.GoToState(Breadcrumb, "Small", true);
} }
else if (e.NewSize.Width > 640 && e.NewSize.Width <= 1008) else if (e.NewSize.Width > 640 && e.NewSize.Width <= 1008)
{ {
VisualStateManager.GoToState(this, "Medium", true); VisualStateManager.GoToState(this, "Medium", true);
VisualStateManager.GoToState(TopMenu, "Overflowed", true); VisualStateManager.GoToState(TopMenu, "Overflowed", true);
VisualStateManager.GoToState(HamburgerMenu, "Collapsed", true); VisualStateManager.GoToState(HamburgerMenu, "Collapsed", true);
VisualStateManager.GoToState(Breadcrumb, "Medium", true);
} }
else else
{ {
VisualStateManager.GoToState(this, "Large", true); VisualStateManager.GoToState(this, "Large", true);
VisualStateManager.GoToState(TopMenu, "Overflowed", true); VisualStateManager.GoToState(TopMenu, "Overflowed", true);
VisualStateManager.GoToState(HamburgerMenu, "Collapsed", true); VisualStateManager.GoToState(HamburgerMenu, "Collapsed", true);
VisualStateManager.GoToState(Breadcrumb, "Large", true);
} }
} }
} }

View File

@@ -1,4 +1,4 @@
<Page <Page x:Name="Page"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
@@ -15,7 +15,6 @@
<Page.Resources> <Page.Resources>
<converters:ColorToBrushConverter x:Key="ColorToBrushConverter"/> <converters:ColorToBrushConverter x:Key="ColorToBrushConverter"/>
<converters:BooleanToVisibilityConverter x:Key="BooleanToVisibilityConverter"/> <converters:BooleanToVisibilityConverter x:Key="BooleanToVisibilityConverter"/>
<converters:InverseBooleanToVisibilityConverter x:Key="InverseBooleanToVisibilityConverter"/>
<converters:IconToSymbolConverter x:Key="IconToSymbolConverter"/> <converters:IconToSymbolConverter x:Key="IconToSymbolConverter"/>
</Page.Resources> </Page.Resources>
<Grid> <Grid>
@@ -131,6 +130,7 @@
</core:EventTriggerBehavior> </core:EventTriggerBehavior>
</interactivity:Interaction.Behaviors> </interactivity:Interaction.Behaviors>
</MenuFlyoutItem> </MenuFlyoutItem>
<MenuFlyoutItem x:Uid="EntryItemDelete" Command="{Binding DeleteEntryCommand}" CommandParameter="{Binding}" />
</MenuFlyout> </MenuFlyout>
</Button.Flyout> </Button.Flyout>
</Button> </Button>

View File

@@ -0,0 +1,105 @@
<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 Orientation="Horizontal" DataContext="{Binding Source={StaticResource Locator}, Path=Breadcrumb}">
<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" />
</StackPanel>
</DataTemplate>
<DataTemplate x:Key="OtherItemTemplate">
<StackPanel Orientation="Horizontal">
<SymbolIcon Symbol="Play" Margin="0" />
<SymbolIcon Symbol="{Binding Icon, Converter={StaticResource IconToSymbolConverter}, ConverterParameter=48}" Margin="0" />
<TextBlock Margin="10,5,10,0" Text="{Binding Name}" FontStyle="Italic" HorizontalAlignment="Center" />
</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 GoToCommand}"
Style="{StaticResource NoBorderButtonStyle}">
<StackPanel Orientation="Horizontal">
<SymbolIcon Symbol="Up" />
<TextBlock x:Name="UpButtonText" Margin="10,2,0,0" Text="{Binding ParentGroupName}" FontStyle="Italic" HorizontalAlignment="Center" />
</StackPanel>
<ToolTipService.ToolTip>
<ToolTip Content="{Binding ParentGroupName}" />
</ToolTipService.ToolTip>
</Button>
<ListView x:Name="ListView" ItemsSource="{Binding BreadcrumbItems}" Height="{StaticResource MenuHeight}">
<ListView.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel 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"/>
</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="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="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="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,12 @@
// 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 BreadcrumbUserControl()
{
InitializeComponent();
}
}
}

View File

@@ -124,6 +124,9 @@
<Compile Include="Views\SettingsPageFrames\SettingsWelcomePage.xaml.cs"> <Compile Include="Views\SettingsPageFrames\SettingsWelcomePage.xaml.cs">
<DependentUpon>SettingsWelcomePage.xaml</DependentUpon> <DependentUpon>SettingsWelcomePage.xaml</DependentUpon>
</Compile> </Compile>
<Compile Include="Views\UserControls\BreadcrumbUserControl.xaml.cs">
<DependentUpon>BreadcrumbUserControl.xaml</DependentUpon>
</Compile>
<Compile Include="Views\UserControls\ColorPickerUserControl.xaml.cs"> <Compile Include="Views\UserControls\ColorPickerUserControl.xaml.cs">
<DependentUpon>ColorPickerUserControl.xaml</DependentUpon> <DependentUpon>ColorPickerUserControl.xaml</DependentUpon>
</Compile> </Compile>
@@ -219,6 +222,10 @@
<SubType>Designer</SubType> <SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator> <Generator>MSBuild:Compile</Generator>
</Page> </Page>
<Page Include="Views\UserControls\BreadcrumbUserControl.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>
<Page Include="Views\UserControls\ColorPickerUserControl.xaml"> <Page Include="Views\UserControls\ColorPickerUserControl.xaml">
<SubType>Designer</SubType> <SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator> <Generator>MSBuild:Compile</Generator>

View File

@@ -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,45 @@
using System.Collections.Generic;
using System.Linq;
using GalaSoft.MvvmLight.Command;
using GalaSoft.MvvmLight.Views;
using ModernKeePass.Application.Common.Interfaces;
using ModernKeePass.Application.Common.Models;
using ModernKeePass.Common;
using ModernKeePass.Models;
namespace ModernKeePass.ViewModels
{
public class BreadcrumbControlVm
{
public IEnumerable<BreadcrumbItem> BreadcrumbItems { get; }
public string ParentGroupName => BreadcrumbItems.Last()?.Name;
public RelayCommand GoBackCommand { get; }
public RelayCommand<int> GoToCommand { get; }
private readonly INavigationService _navigation;
private readonly IBreadcrumbService _breadcrumb;
public BreadcrumbControlVm(INavigationService navigation, IBreadcrumbService breadcrumb)
{
_navigation = navigation;
_breadcrumb = breadcrumb;
BreadcrumbItems = _breadcrumb.GetItems().Reverse();
GoBackCommand = new RelayCommand(GoBack);
GoToCommand = new RelayCommand<int>(GoTo);
}
private void GoTo(int index)
{
var breadcrumb = _breadcrumb.Pop(BreadcrumbItems.Count() - index);
_navigation.NavigateTo(Constants.Navigation.GroupPage, new NavigationItem { Id = breadcrumb.Path });
}
private void GoBack()
{
_breadcrumb.Pop();
_navigation.GoBack();
}
}
}

View File

@@ -57,6 +57,7 @@ namespace ModernKeePass.ViewModels
SimpleIoc.Default.Register(() => App.Services.GetRequiredService<INavigationService>()); SimpleIoc.Default.Register(() => App.Services.GetRequiredService<INavigationService>());
SimpleIoc.Default.Register(() => App.Services.GetRequiredService<INotificationService>()); SimpleIoc.Default.Register(() => App.Services.GetRequiredService<INotificationService>());
SimpleIoc.Default.Register(() => App.Services.GetRequiredService<ICryptographyClient>()); SimpleIoc.Default.Register(() => App.Services.GetRequiredService<ICryptographyClient>());
SimpleIoc.Default.Register(() => App.Services.GetRequiredService<IBreadcrumbService>());
} }
SimpleIoc.Default.Register<SecurityVm>(); SimpleIoc.Default.Register<SecurityVm>();
@@ -70,6 +71,7 @@ namespace ModernKeePass.ViewModels
SimpleIoc.Default.Register<TopMenuVm>(); SimpleIoc.Default.Register<TopMenuVm>();
SimpleIoc.Default.Register<ColorPickerControlVm>(); SimpleIoc.Default.Register<ColorPickerControlVm>();
SimpleIoc.Default.Register<PasswordGenerationBoxControlVm>(); SimpleIoc.Default.Register<PasswordGenerationBoxControlVm>();
SimpleIoc.Default.Register<BreadcrumbControlVm>();
SimpleIoc.Default.Register<NewVm>(); SimpleIoc.Default.Register<NewVm>();
SimpleIoc.Default.Register<OpenVm>(); SimpleIoc.Default.Register<OpenVm>();
SimpleIoc.Default.Register<RecentVm>(); SimpleIoc.Default.Register<RecentVm>();
@@ -87,6 +89,7 @@ namespace ModernKeePass.ViewModels
public TopMenuVm TopMenu => ServiceLocator.Current.GetInstance<TopMenuVm>(Guid.NewGuid().ToString()); public TopMenuVm TopMenu => ServiceLocator.Current.GetInstance<TopMenuVm>(Guid.NewGuid().ToString());
public ColorPickerControlVm ColorPicker => ServiceLocator.Current.GetInstance<ColorPickerControlVm>(Guid.NewGuid().ToString()); public ColorPickerControlVm ColorPicker => ServiceLocator.Current.GetInstance<ColorPickerControlVm>(Guid.NewGuid().ToString());
public PasswordGenerationBoxControlVm PasswordGenerationBox => ServiceLocator.Current.GetInstance<PasswordGenerationBoxControlVm>(Guid.NewGuid().ToString()); public PasswordGenerationBoxControlVm PasswordGenerationBox => ServiceLocator.Current.GetInstance<PasswordGenerationBoxControlVm>(Guid.NewGuid().ToString());
public BreadcrumbControlVm Breadcrumb => ServiceLocator.Current.GetInstance<BreadcrumbControlVm>(Guid.NewGuid().ToString());
public NewVm New => ServiceLocator.Current.GetInstance<NewVm>(Guid.NewGuid().ToString()); public NewVm New => ServiceLocator.Current.GetInstance<NewVm>(Guid.NewGuid().ToString());
public OpenVm Open => ServiceLocator.Current.GetInstance<OpenVm>(Guid.NewGuid().ToString()); public OpenVm Open => ServiceLocator.Current.GetInstance<OpenVm>(Guid.NewGuid().ToString());
public RecentVm Recent => ServiceLocator.Current.GetInstance<RecentVm>(Guid.NewGuid().ToString()); public RecentVm Recent => ServiceLocator.Current.GetInstance<RecentVm>(Guid.NewGuid().ToString());

View File

@@ -42,6 +42,7 @@
<Compile Include="$(MSBuildThisFileDirectory)Messages\NavigateToPageMessage.cs" /> <Compile Include="$(MSBuildThisFileDirectory)Messages\NavigateToPageMessage.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Messages\PasswordGeneratedMessage.cs" /> <Compile Include="$(MSBuildThisFileDirectory)Messages\PasswordGeneratedMessage.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Messages\SaveErrorMessage.cs" /> <Compile Include="$(MSBuildThisFileDirectory)Messages\SaveErrorMessage.cs" />
<Compile Include="$(MSBuildThisFileDirectory)TemplateSelectors\FirstItemDataTemplateSelector.cs" />
<Compile Include="$(MSBuildThisFileDirectory)ViewModels\AboutVm.cs" /> <Compile Include="$(MSBuildThisFileDirectory)ViewModels\AboutVm.cs" />
<Compile Include="$(MSBuildThisFileDirectory)ViewModels\Items\EntryFieldVm.cs" /> <Compile Include="$(MSBuildThisFileDirectory)ViewModels\Items\EntryFieldVm.cs" />
<Compile Include="$(MSBuildThisFileDirectory)ViewModels\Settings\CredentialsVm.cs" /> <Compile Include="$(MSBuildThisFileDirectory)ViewModels\Settings\CredentialsVm.cs" />
@@ -50,6 +51,7 @@
<Compile Include="$(MSBuildThisFileDirectory)ViewModels\Settings\RecycleBinVm.cs" /> <Compile Include="$(MSBuildThisFileDirectory)ViewModels\Settings\RecycleBinVm.cs" />
<Compile Include="$(MSBuildThisFileDirectory)ViewModels\Settings\GeneralVm.cs" /> <Compile Include="$(MSBuildThisFileDirectory)ViewModels\Settings\GeneralVm.cs" />
<Compile Include="$(MSBuildThisFileDirectory)ViewModels\Settings\SecurityVm.cs" /> <Compile Include="$(MSBuildThisFileDirectory)ViewModels\Settings\SecurityVm.cs" />
<Compile Include="$(MSBuildThisFileDirectory)ViewModels\UserControls\BreadcrumbControlVm.cs" />
<Compile Include="$(MSBuildThisFileDirectory)ViewModels\UserControls\ColorPickerControlVm.cs" /> <Compile Include="$(MSBuildThisFileDirectory)ViewModels\UserControls\ColorPickerControlVm.cs" />
<Compile Include="$(MSBuildThisFileDirectory)ViewModels\UserControls\PasswordGenerationBoxControlVm.cs" /> <Compile Include="$(MSBuildThisFileDirectory)ViewModels\UserControls\PasswordGenerationBoxControlVm.cs" />
<Compile Include="$(MSBuildThisFileDirectory)ViewModels\ViewModelLocatorCommon.cs" /> <Compile Include="$(MSBuildThisFileDirectory)ViewModels\ViewModelLocatorCommon.cs" />